From da4e1feaa5c8818e43a6b1e25ab32eab92307c1f Mon Sep 17 00:00:00 2001 From: Genode Labs Date: Thu, 22 Dec 2011 16:19:25 +0100 Subject: [PATCH] Imported Genode release 11.11 --- LICENSE | 280 + README | 182 +- base-codezero/Makefile | 44 + base-codezero/README | 3 + base-codezero/config/vpb926.cml | 240 + base-codezero/doc/codezero.txt | 274 + base-codezero/etc/specs.conf | 1 + base-codezero/include/arm/cpu/atomic.h | 33 + base-codezero/include/base/ipc_msgbuf.h | 63 + base-codezero/include/base/ipc_pager.h | 169 + base-codezero/include/base/native_types.h | 143 + .../include/codezero/dummies/stdio.h | 0 .../include/codezero/dummies/string.h | 0 base-codezero/include/codezero/syscalls.h | 76 + base-codezero/lib/mk/arm/startup.mk | 9 + base-codezero/lib/mk/arm_v5/l4.mk | 3 + base-codezero/lib/mk/arm_v5/l4_arm_v5.mk | 9 + base-codezero/lib/mk/codezero_cml.inc | 3 + base-codezero/lib/mk/cxx.mk | 13 + base-codezero/lib/mk/ipc.mk | 4 + base-codezero/lib/mk/l4.inc | 43 + base-codezero/lib/mk/lock.mk | 7 + base-codezero/lib/mk/pager.mk | 4 + base-codezero/lib/mk/pl011/core_printf.mk | 6 + base-codezero/lib/mk/platform.mk | 33 + base-codezero/lib/mk/thread.mk | 5 + base-codezero/mk/spec-codezero.mk | 56 + base-codezero/mk/spec-codezero_arm.mk | 13 + base-codezero/mk/spec-codezero_arm_v5.mk | 9 + .../mk/spec-codezero_platform_vpb926.mk | 6 + base-codezero/patches/README | 67 + base-codezero/patches/binutils-2.21.patch | 33 + base-codezero/patches/gcc_4_6_1_fixes.patch | 182 + .../patches/gcc_shared_enabled.patch | 10 + base-codezero/patches/libc_search_dir.patch | 21 + base-codezero/patches/scons-2.0.1.patch | 97 + base-codezero/patches/set_fixed_pager.patch | 13 + base-codezero/run/env | 88 + .../src/base/console/pl011/core_console.h | 78 + base-codezero/src/base/cxx/exception.cc | 53 + base-codezero/src/base/cxx/memcmp.cc | 24 + base-codezero/src/base/ipc/ipc.cc | 175 + base-codezero/src/base/ipc/pager.cc | 175 + base-codezero/src/base/lock/cmpxchg.cc | 48 + base-codezero/src/base/lock/lock.cc | 63 + base-codezero/src/base/lock/lock_helper.h | 112 + base-codezero/src/base/pager/pager.cc | 101 + base-codezero/src/base/thread/thread_start.cc | 79 + base-codezero/src/core/core_rm_session.cc | 67 + .../src/core/include/core_rm_session.h | 52 + .../src/core/include/irq_session_component.h | 71 + base-codezero/src/core/include/map_local.h | 66 + base-codezero/src/core/include/platform.h | 72 + base-codezero/src/core/include/platform_pd.h | 74 + .../src/core/include/platform_thread.h | 135 + base-codezero/src/core/include/util.h | 46 + .../src/core/io_mem_session_support.cc | 27 + .../src/core/io_port_session_component.cc | 58 + .../src/core/irq_session_component.cc | 72 + base-codezero/src/core/platform.cc | 293 + base-codezero/src/core/platform_pd.cc | 124 + base-codezero/src/core/platform_thread.cc | 104 + base-codezero/src/core/ram_session_support.cc | 65 + base-codezero/src/core/rm_session_support.cc | 28 + base-codezero/src/core/target.inc | 55 + base-codezero/src/core/target.mk | 4 + base-codezero/src/core/thread_start.cc | 121 + base-codezero/src/kernel/target.mk | 74 + base-codezero/src/platform/_main_helper.h | 67 + base-codezero/src/platform/genode.ld | 131 + base-codezero/tool/gen_romfs | 202 + base-fiasco/Makefile | 44 + base-fiasco/README | 4 + base-fiasco/config/kernel-config.x86 | 95 + base-fiasco/config/l4env-config.x86 | 83 + base-fiasco/doc/fiasco.txt | 130 + base-fiasco/etc/fiasco.conf | 11 + base-fiasco/etc/specs.conf | 15 + base-fiasco/etc/tools.conf | 8 + base-fiasco/include/arm/cpu/atomic.h | 39 + base-fiasco/include/base/cancelable_lock.h | 57 + base-fiasco/include/base/ipc_msgbuf.h | 65 + base-fiasco/include/base/ipc_pager.h | 173 + base-fiasco/include/base/native_types.h | 115 + base-fiasco/include/fiasco/thread_helper.h | 40 + base-fiasco/lib/mk/arm/startup.mk | 8 + base-fiasco/lib/mk/core_printf.mk | 5 + base-fiasco/lib/mk/ipc.mk | 3 + base-fiasco/lib/mk/l4v2_support.mk | 19 + base-fiasco/lib/mk/lock.mk | 3 + base-fiasco/lib/mk/pager.mk | 3 + base-fiasco/lib/mk/platform.inc | 56 + base-fiasco/lib/mk/x86/platform.mk | 6 + base-fiasco/lib/mk/x86/startup.mk | 8 + base-fiasco/mk/l4_pkg.mk | 69 + base-fiasco/mk/spec-fiasco.mk | 32 + base-fiasco/mk/spec-fiasco_arm.mk | 50 + base-fiasco/mk/spec-fiasco_x86.mk | 25 + base-fiasco/mk/spec-platform_imx.mk | 16 + base-fiasco/mk/spec-platform_integrator.mk | 14 + base-fiasco/mk/spec-platform_mmsp2.mk | 14 + base-fiasco/run/env | 127 + base-fiasco/src/base/console/core_console.h | 30 + base-fiasco/src/base/ipc/ipc.cc | 259 + base-fiasco/src/base/ipc/pager.cc | 69 + base-fiasco/src/base/lock/lock.cc | 50 + base-fiasco/src/base/pager/pager.cc | 117 + base-fiasco/src/bootstrap/target.mk | 5 + base-fiasco/src/core/arm/platform_arm.cc | 24 + base-fiasco/src/core/arm/target.mk | 7 + base-fiasco/src/core/include/map_local.h | 76 + base-fiasco/src/core/include/platform.h | 157 + base-fiasco/src/core/include/platform_pd.h | 182 + .../src/core/include/platform_thread.h | 142 + base-fiasco/src/core/include/util.h | 121 + .../src/core/io_mem_session_support.cc | 95 + base-fiasco/src/core/irq_session_component.cc | 122 + base-fiasco/src/core/platform.cc | 509 + base-fiasco/src/core/platform_pd.cc | 290 + base-fiasco/src/core/platform_thread.cc | 153 + base-fiasco/src/core/ram_session_support.cc | 27 + base-fiasco/src/core/rm_session_support.cc | 48 + base-fiasco/src/core/target.inc | 51 + base-fiasco/src/core/thread_start.cc | 62 + base-fiasco/src/core/x86/platform_x86.cc | 52 + base-fiasco/src/core/x86/target.mk | 7 + base-fiasco/src/kernel/target.inc | 23 + base-fiasco/src/kernel/x86/target.mk | 4 + base-fiasco/src/platform/_main_helper.h | 19 + base-fiasco/src/platform/arm/Makefile | 25 + base-fiasco/src/platform/arm/_main.cc | 124 + base-fiasco/src/platform/arm/crt0.s | 37 + base-fiasco/src/sigma0/target.mk | 5 + base-foc/Makefile | 98 + base-foc/README | 9 + base-foc/config/pbxa9.kernel | 83 + base-foc/config/rva9.user | 64 + base-foc/config/vea9x4.kernel | 85 + base-foc/config/x86_32.kernel | 87 + base-foc/config/x86_64.kernel | 73 + base-foc/doc/foc.txt | 142 + base-foc/etc/foc.conf | 20 + base-foc/etc/specs.conf | 8 + base-foc/include/arm/cpu/atomic.h | 54 + base-foc/include/base/cap_sel_alloc.h | 68 + base-foc/include/base/ipc.h | 56 + base-foc/include/base/ipc_msgbuf.h | 152 + base-foc/include/base/ipc_pager.h | 200 + base-foc/include/base/native_types.h | 87 + base-foc/include/base/thread_state.h | 40 + base-foc/include/foc_cpu_session/client.h | 79 + base-foc/include/foc_cpu_session/connection.h | 41 + .../include/foc_cpu_session/foc_cpu_session.h | 46 + base-foc/include/foc_pd_session/client.h | 38 + base-foc/include/foc_pd_session/connection.h | 40 + .../include/foc_pd_session/foc_pd_session.h | 40 + base-foc/include/signal_session/foc_source.h | 35 + .../include/signal_session/source_client.h | 91 + .../signal_session/source_rpc_object.h | 39 + base-foc/lib/mk/arm/ipc.mk | 4 + base-foc/lib/mk/arm/platform.inc | 8 + base-foc/lib/mk/arm/startup.mk | 8 + base-foc/lib/mk/arm/syscalls.mk | 5 + base-foc/lib/mk/cap_alloc.mk | 3 + base-foc/lib/mk/core_printf.mk | 5 + base-foc/lib/mk/env.mk | 6 + base-foc/lib/mk/ipc.inc | 5 + base-foc/lib/mk/l4re_support.mk | 14 + base-foc/lib/mk/lock.mk | 5 + base-foc/lib/mk/pager.mk | 3 + base-foc/lib/mk/platform.inc | 69 + base-foc/lib/mk/platform_pbxa9/platform.mk | 6 + base-foc/lib/mk/platform_vea9x4/platform.mk | 6 + base-foc/lib/mk/raw_server.mk | 4 + base-foc/lib/mk/server.mk | 3 + base-foc/lib/mk/thread.mk | 3 + base-foc/lib/mk/x86/syscalls.mk | 5 + base-foc/lib/mk/x86_32/ipc.mk | 3 + base-foc/lib/mk/x86_32/platform.mk | 13 + base-foc/lib/mk/x86_32/startup.mk | 8 + base-foc/lib/mk/x86_64/ipc.mk | 3 + base-foc/lib/mk/x86_64/platform.mk | 13 + base-foc/lib/mk/x86_64/startup.mk | 8 + base-foc/mk/l4_pkg.mk | 60 + base-foc/mk/spec-foc.mk | 52 + base-foc/mk/spec-foc_arm.mk | 37 + base-foc/mk/spec-foc_pbxa9.mk | 4 + base-foc/mk/spec-foc_vea9x4.mk | 4 + base-foc/mk/spec-foc_x86_32.mk | 25 + base-foc/mk/spec-foc_x86_64.mk | 30 + base-foc/patches/README | 14 + .../patches/crtn_arm_binutils_2.21.1.patch | 21 + base-foc/patches/fix_exception_ip.patch | 15 + base-foc/patches/foc_single_step_x86.patch | 241 + base-foc/patches/timer_arm.patch | 20 + base-foc/patches/vexpress_detection.patch | 13 + base-foc/run/env | 203 + base-foc/src/base/console/core_console.h | 30 + base-foc/src/base/env/cap_sel_alloc.cc | 107 + base-foc/src/base/ipc/arm/pager.cc | 27 + base-foc/src/base/ipc/arm/pager_exception.cc | 29 + base-foc/src/base/ipc/ipc.cc | 317 + base-foc/src/base/ipc/pager.cc | 110 + base-foc/src/base/ipc/x86/pager_exception.cc | 29 + base-foc/src/base/ipc/x86_32/pager.cc | 35 + base-foc/src/base/ipc/x86_64/pager.cc | 42 + base-foc/src/base/lock/lock_helper.h | 108 + base-foc/src/base/pager/pager.cc | 183 + base-foc/src/base/server/server.cc | 39 + base-foc/src/base/thread/thread.cc | 200 + base-foc/src/base/thread/thread_bootstrap.cc | 51 + base-foc/src/base/thread/thread_start.cc | 67 + base-foc/src/bootstrap/target.mk | 5 + base-foc/src/core/arm/platform_arm.cc | 16 + base-foc/src/core/arm/platform_thread.cc | 25 + base-foc/src/core/arm/target.mk | 7 + base-foc/src/core/cap_session_component.cc | 230 + base-foc/src/core/cpu_session_extension.cc | 87 + .../src/core/include/cap_session_component.h | 118 + .../src/core/include/cpu_session_component.h | 155 + .../src/core/include/irq_session_component.h | 113 + base-foc/src/core/include/map_local.h | 66 + .../src/core/include/pd_session_component.h | 57 + base-foc/src/core/include/platform.h | 157 + base-foc/src/core/include/platform_pd.h | 116 + base-foc/src/core/include/platform_thread.h | 164 + base-foc/src/core/include/util.h | 117 + base-foc/src/core/io_mem_session_support.cc | 92 + base-foc/src/core/irq_session_component.cc | 150 + base-foc/src/core/pd_session_extension.cc | 23 + base-foc/src/core/platform.cc | 500 + base-foc/src/core/platform_pd.cc | 174 + base-foc/src/core/platform_thread.cc | 307 + base-foc/src/core/ram_session_support.cc | 27 + base-foc/src/core/rm_session_support.cc | 35 + base-foc/src/core/signal_source_component.cc | 77 + base-foc/src/core/target.inc | 57 + base-foc/src/core/thread_start.cc | 68 + base-foc/src/core/x86/platform_thread.cc | 27 + base-foc/src/core/x86/platform_x86.cc | 44 + base-foc/src/core/x86/target.mk | 8 + base-foc/src/kernel/pbxa9/target.mk | 4 + base-foc/src/kernel/target.inc | 21 + base-foc/src/kernel/vea9x4/target.mk | 4 + base-foc/src/kernel/x86_32/target.mk | 4 + base-foc/src/kernel/x86_64/target.mk | 4 + base-foc/src/platform/_main_helper.h | 29 + base-foc/src/platform/_main_parent_cap.h | 36 + base-foc/src/sigma0/target.mk | 5 + base-host/README | 7 + base-host/etc/specs.conf | 13 + base-host/etc/tools.conf | 4 + base-host/include/base/ipc_msgbuf.h | 39 + base-host/include/base/ipc_pager.h | 139 + base-host/include/base/native_types.h | 45 + base-host/lib/mk/core_printf.mk | 1 + base-host/lib/mk/env.mk | 6 + base-host/lib/mk/ipc.mk | 3 + base-host/lib/mk/lock.mk | 4 + base-host/lib/mk/pager.mk | 3 + base-host/lib/mk/printf_stdio.mk | 3 + base-host/src/base/env/parent.cc | 24 + base-host/src/base/ipc/ipc.cc | 77 + base-host/src/base/lock/lock_helper.h | 52 + base-host/src/base/pager/pager.cc | 57 + base-host/src/core/context_area.cc | 90 + base-host/src/core/core_rm_session.cc | 30 + base-host/src/core/include/core_rm_session.h | 48 + base-host/src/core/include/platform.h | 50 + base-host/src/core/include/platform_pd.h | 59 + base-host/src/core/include/platform_thread.h | 106 + base-host/src/core/include/util.h | 60 + base-host/src/core/io_mem_session_support.cc | 27 + .../src/core/io_port_session_component.cc | 58 + base-host/src/core/irq_session_component.cc | 54 + base-host/src/core/platform.cc | 41 + base-host/src/core/platform_pd.cc | 55 + base-host/src/core/platform_thread.cc | 77 + base-host/src/core/ram_session_support.cc | 29 + base-host/src/core/rm_session_support.cc | 26 + base-host/src/core/target.inc | 49 + base-host/src/core/target.mk | 1 + base-host/src/core/thread_host.cc | 23 + .../src/lib/printf_stdio/printf_stdio.cc | 35 + base-linux/README | 1 + base-linux/etc/specs.conf | 22 + base-linux/include/base/ipc_msgbuf.h | 64 + base-linux/include/base/local_interface.h | 89 + base-linux/include/base/native_types.h | 135 + base-linux/include/base/pager.h | 43 + base-linux/include/base/platform_env.h | 379 + base-linux/include/linux_dataspace/client.h | 47 + .../include/linux_dataspace/linux_dataspace.h | 47 + base-linux/include/rm_session/client.h | 59 + base-linux/lib/import/import-lx_hybrid.mk | 92 + base-linux/lib/import/import-syscall.mk | 6 + base-linux/lib/mk/core_printf.mk | 5 + base-linux/lib/mk/env.mk | 6 + base-linux/lib/mk/ipc.mk | 5 + base-linux/lib/mk/lock.mk | 5 + base-linux/lib/mk/lx_hybrid.mk | 7 + base-linux/lib/mk/process.mk | 13 + base-linux/lib/mk/rpath.mk | 6 + base-linux/lib/mk/thread.mk | 6 + base-linux/lib/mk/x86_32/startup.mk | 8 + base-linux/lib/mk/x86_32/syscall.mk | 5 + base-linux/lib/mk/x86_64/startup.mk | 8 + base-linux/lib/mk/x86_64/syscall.mk | 7 + base-linux/mk/spec-linux.mk | 18 + base-linux/mk/spec-linux_x86_32.mk | 21 + base-linux/mk/spec-linux_x86_64.mk | 22 + base-linux/run/env | 43 + base-linux/run/lx_hybrid_ctors.run | 77 + base-linux/run/lx_hybrid_exception.run | 60 + base-linux/src/base/console/core_console.h | 31 + base-linux/src/base/env/debug.cc | 57 + base-linux/src/base/env/platform_env.cc | 97 + base-linux/src/base/env/rm_session_mmap.cc | 283 + base-linux/src/base/ipc/ipc.cc | 358 + base-linux/src/base/lock/lock_helper.h | 80 + base-linux/src/base/process/process.cc | 203 + base-linux/src/base/thread/thread_linux.cc | 115 + base-linux/src/core/context_area.cc | 112 + .../src/core/include/cap_session_component.h | 47 + .../src/core/include/dataspace_component.h | 91 + .../core/include/io_mem_session_component.h | 64 + .../src/core/include/irq_session_component.h | 60 + .../src/core/include/pd_session_component.h | 61 + base-linux/src/core/include/platform.h | 62 + base-linux/src/core/include/platform_pd.h | 25 + base-linux/src/core/include/platform_thread.h | 65 + .../src/core/include/rm_session_component.h | 64 + .../src/core/io_mem_session_component.cc | 27 + .../src/core/io_port_session_component.cc | 59 + base-linux/src/core/pd_session_component.cc | 51 + base-linux/src/core/platform.cc | 62 + base-linux/src/core/platform_thread.cc | 86 + base-linux/src/core/ram_session_support.cc | 59 + base-linux/src/core/rom_session_component.cc | 71 + base-linux/src/core/target.mk | 36 + base-linux/src/core/thread_linux.cc | 55 + base-linux/src/platform/_main_helper.h | 40 + .../src/platform/context_area.nostdlib.ld | 25 + .../src/platform/context_area.stdlib.ld | 20 + base-linux/src/platform/linux_rpath.cc | 39 + base-linux/src/platform/linux_rpath.h | 25 + base-linux/src/platform/linux_syscalls.h | 501 + base-linux/src/platform/lx_hybrid.cc | 47 + base-linux/src/platform/x86_32/crt0.s | 70 + base-linux/src/platform/x86_32/lx_clone.S | 109 + base-linux/src/platform/x86_32/lx_syscall.S | 77 + base-linux/src/platform/x86_64/crt0.s | 74 + base-linux/src/platform/x86_64/lx_clone.S | 71 + .../src/platform/x86_64/lx_restore_rt.S | 16 + base-linux/src/platform/x86_64/lx_syscall.S | 29 + base-linux/src/test/lx_hybrid_ctors/main.cc | 39 + base-linux/src/test/lx_hybrid_ctors/target.mk | 22 + .../src/test/lx_hybrid_ctors/testlib.cc | 24 + .../src/test/lx_hybrid_exception/main.cc | 37 + .../src/test/lx_hybrid_exception/target.mk | 3 + base-linux/src/test/sub_rm/config.h | 22 + base-mb/README | 11 + base-mb/doc/getting_started.txt | 249 + base-mb/doc/microblaze.txt | 124 + base-mb/etc/specs.conf | 1 + base-mb/etc/tools.conf | 14 + base-mb/include/base/ipc_msgbuf.h | 62 + base-mb/include/base/ipc_pager.h | 207 + base-mb/include/base/native_types.h | 62 + base-mb/include/cpu/atomic.h | 71 + base-mb/include/cpu/config.h | 116 + base-mb/include/kernel/config.h | 79 + base-mb/include/kernel/syscalls.h | 568 + base-mb/include/kernel/types.h | 329 + base-mb/include/xilinx/xps_intc.h | 226 + base-mb/include/xilinx/xps_timer.h | 399 + base-mb/include/xilinx/xps_uartl.h | 111 + base-mb/lib/mk/cxx.mk | 87 + base-mb/lib/mk/ipc.mk | 6 + base-mb/lib/mk/kernel.inc | 41 + base-mb/lib/mk/kernel_core.mk | 13 + base-mb/lib/mk/kernel_test.inc | 7 + base-mb/lib/mk/lock.mk | 6 + base-mb/lib/mk/pager.mk | 4 + ...logix_s3adsp1800_mmu__atomic_operations.mk | 6 + ...talogix_s3adsp1800_mmu__kernel_support.inc | 29 + base-mb/lib/mk/printf_microblaze.mk | 5 + base-mb/lib/mk/startup.mk | 13 + base-mb/lib/mk/test_env.mk | 6 + base-mb/lib/mk/thread.mk | 8 + base-mb/lib/mk/thread_context.mk | 5 + base-mb/mk/spec-mb_ml507.mk | 7 + base-mb/mk/spec-mb_s3a_starter_kit.mk | 7 + base-mb/platform/mb_s3a_starter_kit/Makefile | 22 + .../platform/mb_s3a_starter_kit/system.bit | Bin 0 -> 341653 bytes base-mb/platform/mk/microblaze.mk | 19 + base-mb/platform/mk/ml507.mk | 19 + base-mb/platform/mk/s3a_starter_kit.mk | 22 + base-mb/platform/mk/xilinx.mk | 24 + base-mb/run/env | 211 + base-mb/run/hello.run | 15 + base-mb/run/nested_init.run | 34 + .../src/base/console/microblaze_console.cc | 64 + base-mb/src/base/cxx/atexit.cc | 19 + base-mb/src/base/ipc/ipc.cc | 201 + base-mb/src/base/ipc/pager.cc | 74 + base-mb/src/base/lock/lock_helper.h | 57 + base-mb/src/base/pager/pager.cc | 115 + base-mb/src/base/thread/thread.cc | 207 + base-mb/src/base/thread/thread_bootstrap.cc | 22 + base-mb/src/base/thread/thread_context.cc | 57 + base-mb/src/base/thread/thread_start.cc | 73 + base-mb/src/core/context_area.cc | 148 + base-mb/src/core/core_rm_session.cc | 36 + base-mb/src/core/include/core_rm_session.h | 52 + base-mb/src/core/include/cpu/prints.h | 67 + .../src/core/include/irq_session_component.h | 73 + base-mb/src/core/include/kernel/print.h | 98 + base-mb/src/core/include/map_local.h | 49 + base-mb/src/core/include/platform.h | 118 + base-mb/src/core/include/platform_pd.h | 253 + base-mb/src/core/include/platform_thread.h | 162 + base-mb/src/core/include/util.h | 55 + base-mb/src/core/include/util/array.h | 21 + base-mb/src/core/include/util/debug.h | 62 + base-mb/src/core/include/util/id_allocator.h | 122 + base-mb/src/core/include/util/math.h | 40 + base-mb/src/core/include/util/queue.h | 145 + base-mb/src/core/include/xilinx/microblaze.h | 403 + base-mb/src/core/io_mem_session_support.cc | 28 + base-mb/src/core/io_port_session_component.cc | 41 + base-mb/src/core/irq_session_component.cc | 68 + base-mb/src/core/platform.cc | 312 + base-mb/src/core/platform_thread.cc | 183 + base-mb/src/core/ram_session_support.cc | 45 + base-mb/src/core/rm_session_support.cc | 38 + base-mb/src/core/target.inc | 52 + base-mb/src/core/target.mk | 10 + base-mb/src/core/thread_roottask.cc | 110 + base-mb/src/kernel/generic/blocking.cc | 268 + .../src/kernel/generic/include/exception.h | 82 + .../src/kernel/generic/include/interrupt.h | 50 + base-mb/src/kernel/generic/include/thread.h | 360 + base-mb/src/kernel/generic/kernel.cc | 189 + base-mb/src/kernel/generic/scheduler.cc | 130 + base-mb/src/kernel/generic/syscall_events.cc | 187 + base-mb/src/kernel/generic/thread.cc | 117 + base-mb/src/kernel/include/generic/blocking.h | 417 + base-mb/src/kernel/include/generic/event.h | 103 + base-mb/src/kernel/include/generic/ipc.h | 323 + .../kernel/include/generic/irq_controller.h | 153 + base-mb/src/kernel/include/generic/printf.h | 21 + .../src/kernel/include/generic/scheduler.h | 372 + .../kernel/include/generic/syscall_events.h | 190 + base-mb/src/kernel/include/generic/timer.h | 197 + base-mb/src/kernel/include/generic/tlb.h | 152 + base-mb/src/kernel/include/generic/verbose.h | 107 + .../platform/platform.h | 729 + .../petalogix_s3adsp1800_mmu/atomic.s | 92 + .../platforms/petalogix_s3adsp1800_mmu/crt0.s | 45 + .../petalogix_s3adsp1800_mmu/crt0_kernel.s | 93 + .../petalogix_s3adsp1800_mmu/include/errors.s | 23 + .../include/exec_context.s | 527 + .../include/linker_commands.s | 34 + .../include/special_registers.s | 59 + .../petalogix_s3adsp1800_mmu/kernel_entry.s | 209 + .../petalogix_s3adsp1800_mmu/platform.cc | 205 + .../petalogix_s3adsp1800_mmu/userland_entry.s | 223 + base-mb/src/platform/_main_helper.h | 54 + base-mb/src/platform/genode.ld | 115 + base-mb/src/test/hello/main.cc | 23 + base-mb/src/test/hello/target.mk | 3 + base-nova/Makefile | 47 + base-nova/README | 10 + base-nova/doc/nova.txt | 254 + base-nova/etc/specs.conf | 5 + base-nova/include/base/cap_sel_alloc.h | 72 + base-nova/include/base/ipc.h | 36 + base-nova/include/base/ipc_msgbuf.h | 186 + base-nova/include/base/ipc_pager.h | 137 + base-nova/include/base/native_types.h | 88 + base-nova/include/base/pager.h | 154 + base-nova/include/base/sleep.h | 34 + base-nova/include/nova/stdint.h | 28 + base-nova/include/nova/syscalls.h | 673 + .../include/signal_session/nova_source.h | 34 + .../include/signal_session/source_client.h | 82 + .../signal_session/source_rpc_object.h | 39 + base-nova/lib/mk/core_printf.mk | 5 + base-nova/lib/mk/env.mk | 7 + base-nova/lib/mk/ipc.mk | 6 + base-nova/lib/mk/lock.mk | 5 + base-nova/lib/mk/pager.mk | 3 + base-nova/lib/mk/printf_stdio.mk | 3 + base-nova/lib/mk/raw_server.mk | 4 + base-nova/lib/mk/server.mk | 3 + base-nova/lib/mk/test_env.mk | 11 + base-nova/lib/mk/thread.mk | 6 + base-nova/lib/mk/thread_context.mk | 3 + base-nova/lib/mk/x86_32/startup.mk | 8 + base-nova/mk/spec-nova.mk | 16 + base-nova/patches/README | 21 + base-nova/patches/utcb.patch | 18 + base-nova/run/env | 82 + base-nova/src/base/console/core_console.h | 129 + base-nova/src/base/env/cap_sel_alloc.cc | 121 + base-nova/src/base/env/main_thread.cc | 43 + base-nova/src/base/ipc/ipc.cc | 200 + base-nova/src/base/ipc/pager.cc | 67 + base-nova/src/base/lock/lock_helper.h | 88 + base-nova/src/base/pager/pager.cc | 177 + base-nova/src/base/server/server.cc | 187 + base-nova/src/base/thread/thread_context.cc | 38 + base-nova/src/base/thread/thread_nova.cc | 141 + base-nova/src/core/core_rm_session.cc | 53 + base-nova/src/core/echo.cc | 62 + .../src/core/include/cap_session_component.h | 48 + base-nova/src/core/include/core_rm_session.h | 52 + base-nova/src/core/include/echo.h | 54 + .../src/core/include/irq_session_component.h | 74 + base-nova/src/core/include/map_local.h | 45 + base-nova/src/core/include/nova_util.h | 141 + base-nova/src/core/include/platform.h | 81 + base-nova/src/core/include/platform_pd.h | 79 + base-nova/src/core/include/platform_thread.h | 125 + base-nova/src/core/include/util.h | 74 + base-nova/src/core/io_mem_session_support.cc | 59 + base-nova/src/core/irq_session_component.cc | 74 + base-nova/src/core/platform.cc | 352 + base-nova/src/core/platform_pd.cc | 57 + base-nova/src/core/platform_thread.cc | 143 + base-nova/src/core/ram_session_support.cc | 77 + base-nova/src/core/rm_session_support.cc | 55 + base-nova/src/core/signal_source_component.cc | 73 + base-nova/src/core/target.inc | 56 + base-nova/src/core/target.mk | 4 + base-nova/src/core/thread_start.cc | 63 + base-nova/src/kernel/target.mk | 35 + .../src/lib/printf_stdio/printf_stdio.cc | 35 + base-nova/src/platform/_main_helper.h | 58 + base-nova/src/platform/_main_parent_cap.h | 46 + base-nova/src/platform/roottask.ld | 104 + base-okl4/Makefile | 49 + base-okl4/README | 10 + base-okl4/contrib/generated/README | 8 + base-okl4/contrib/generated/x86/asmsyms.h | 69 + .../contrib/generated/x86/kdb_class_helper.h | 119 + base-okl4/contrib/generated/x86/ktcb_layout.h | 71 + base-okl4/contrib/generated/x86/linker.ld | 148 + base-okl4/contrib/generated/x86/macro_sets.cc | 401 + base-okl4/contrib/generated/x86/tcb_layout.h | 105 + base-okl4/doc/notes.txt | 863 ++ base-okl4/doc/okl4.txt | 131 + base-okl4/etc/specs.conf | 1 + base-okl4/include/base/ipc_msgbuf.h | 68 + base-okl4/include/base/ipc_pager.h | 180 + base-okl4/include/base/native_types.h | 127 + base-okl4/include/base/thread_state.h | 33 + base-okl4/include/okl4_pd_session/client.h | 41 + .../include/okl4_pd_session/connection.h | 41 + .../include/okl4_pd_session/okl4_pd_session.h | 62 + base-okl4/lib/mk/bootinfo.mk | 5 + base-okl4/lib/mk/core_printf.mk | 5 + base-okl4/lib/mk/ipc.mk | 3 + base-okl4/lib/mk/kernel.inc | 97 + base-okl4/lib/mk/lock.mk | 4 + base-okl4/lib/mk/pager.mk | 3 + base-okl4/lib/mk/platform.inc | 57 + base-okl4/lib/mk/thread.mk | 5 + base-okl4/lib/mk/x86/kernel.mk | 112 + base-okl4/lib/mk/x86/platform.mk | 16 + base-okl4/lib/mk/x86/startup.mk | 8 + base-okl4/mk/spec-okl4.mk | 50 + base-okl4/mk/spec-okl4_x86.mk | 17 + base-okl4/patches/README | 71 + base-okl4/patches/char_bit.patch | 25 + base-okl4/patches/eabi_build.patch | 232 + base-okl4/patches/elfweaver.patch | 11 + base-okl4/patches/gcc_4.4.5.patch | 54 + base-okl4/patches/gdt_init.patch | 12 + base-okl4/patches/kdb_reboot.patch | 54 + base-okl4/patches/reply_tid.patch | 21 + base-okl4/patches/suspend_resume.patch | 28 + base-okl4/patches/syscall_pic.patch | 411 + base-okl4/run/env | 200 + base-okl4/run/priority.run | 42 + base-okl4/src/base/bootinfo/README | 2 + base-okl4/src/base/bootinfo/stdint.h | 20 + base-okl4/src/base/bootinfo/stdio.h | 12 + base-okl4/src/base/console/core_console.h | 31 + base-okl4/src/base/ipc/ipc.cc | 287 + base-okl4/src/base/ipc/pager.cc | 143 + base-okl4/src/base/lock/lock_helper.h | 102 + base-okl4/src/base/pager/pager.cc | 120 + base-okl4/src/base/thread/thread_bootstrap.cc | 29 + base-okl4/src/core/core_rm_session.cc | 62 + base-okl4/src/core/include/core_rm_session.h | 57 + base-okl4/src/core/include/map_local.h | 126 + .../src/core/include/pd_session_component.h | 59 + base-okl4/src/core/include/platform.h | 134 + base-okl4/src/core/include/platform_pd.h | 191 + base-okl4/src/core/include/platform_thread.h | 150 + base-okl4/src/core/include/stdint.h | 22 + base-okl4/src/core/include/util.h | 131 + base-okl4/src/core/io_mem_session_support.cc | 27 + base-okl4/src/core/irq_session_component.cc | 316 + .../src/core/okl4_pd_session_component.cc | 28 + base-okl4/src/core/platform.cc | 318 + base-okl4/src/core/platform_pd.cc | 315 + base-okl4/src/core/platform_thread.cc | 197 + base-okl4/src/core/ram_session_support.cc | 66 + base-okl4/src/core/rm_session_support.cc | 90 + base-okl4/src/core/target.inc | 55 + base-okl4/src/core/thread_start.cc | 59 + base-okl4/src/core/x86/platform_thread_x86.cc | 57 + base-okl4/src/core/x86/target.mk | 8 + base-okl4/src/kernel/target.inc | 67 + base-okl4/src/kernel/x86/target.mk | 56 + base-okl4/src/platform/_main_helper.h | 50 + base-okl4/src/test/create_thread.h | 144 + base-okl4/src/test/io_port.h | 36 + base-okl4/src/test/mini_env.h | 83 + base-okl4/src/test/okl4_01_hello_raw/Makefile | 27 + base-okl4/src/test/okl4_01_hello_raw/crt0.s | 54 + .../src/test/okl4_01_hello_raw/genode.ld | 89 + base-okl4/src/test/okl4_01_hello_raw/hello.cc | 76 + .../src/test/okl4_01_hello_raw/weaver.xml | 61 + base-okl4/src/test/okl4_02_hello/hello.cc | 37 + base-okl4/src/test/okl4_02_hello/target.mk | 4 + base-okl4/src/test/okl4_03_thread/main.cc | 76 + base-okl4/src/test/okl4_03_thread/target.mk | 4 + .../src/test/okl4_04_ipc_send_wait/main.cc | 79 + .../src/test/okl4_04_ipc_send_wait/target.mk | 4 + base-okl4/src/test/okl4_05_ipc_call/main.cc | 90 + base-okl4/src/test/okl4_05_ipc_call/target.mk | 4 + base-okl4/src/test/okl4_06_pager/main.cc | 142 + base-okl4/src/test/okl4_06_pager/target.mk | 4 + base-okl4/src/test/okl4_07_boot_info/main.cc | 103 + base-okl4/src/test/okl4_07_boot_info/stdint.h | 22 + .../src/test/okl4_07_boot_info/target.mk | 5 + base-okl4/src/test/okl4_08_timer_pit/main.cc | 135 + .../src/test/okl4_08_timer_pit/target.mk | 4 + base-okl4/tool/README | 3 + base-okl4/tool/weaver_x86.xml | 76 + base-pistachio/Makefile | 40 + base-pistachio/README | 3 + base-pistachio/config/kernel | 153 + base-pistachio/doc/pistachio.txt | 78 + base-pistachio/etc/specs.conf | 1 + base-pistachio/include/base/clock.h | 37 + base-pistachio/include/base/ipc_msgbuf.h | 65 + base-pistachio/include/base/ipc_pager.h | 195 + base-pistachio/include/base/native_types.h | 111 + base-pistachio/include/pistachio/kip.h | 56 + .../include/pistachio/thread_helper.h | 45 + base-pistachio/include/util/hexdump.h | 39 + base-pistachio/include/x86/cpu/rdtsc.h | 30 + base-pistachio/include/x86/util/smath.h | 54 + base-pistachio/lib/mk/core_printf.mk | 5 + base-pistachio/lib/mk/hexdump.mk | 3 + base-pistachio/lib/mk/ipc.mk | 3 + base-pistachio/lib/mk/kip.mk | 5 + base-pistachio/lib/mk/l4.mk | 11 + base-pistachio/lib/mk/lock.mk | 4 + base-pistachio/lib/mk/pager.mk | 3 + base-pistachio/lib/mk/platform.mk | 25 + base-pistachio/lib/mk/x86/startup.mk | 8 + base-pistachio/mk/spec-pistachio.mk | 41 + base-pistachio/mk/spec-pistachio_x86.mk | 14 + base-pistachio/patches/README | 20 + base-pistachio/patches/syscalls_ia32.patch | 280 + base-pistachio/run/env | 108 + .../src/base/console/core_console.h | 31 + base-pistachio/src/base/ipc/ipc.cc | 355 + base-pistachio/src/base/ipc/pager.cc | 139 + base-pistachio/src/base/kip/kip.cc | 57 + base-pistachio/src/base/lock/lock_helper.h | 102 + base-pistachio/src/base/pager/pager.cc | 118 + .../src/core/cpu_session_platform.cc | 15 + base-pistachio/src/core/include/map_local.h | 82 + base-pistachio/src/core/include/platform.h | 156 + base-pistachio/src/core/include/platform_pd.h | 214 + .../src/core/include/platform_thread.h | 151 + base-pistachio/src/core/include/util.h | 126 + .../src/core/io_mem_session_support.cc | 123 + .../src/core/irq_session_component.cc | 134 + base-pistachio/src/core/multiboot_info.cc | 162 + base-pistachio/src/core/platform.cc | 648 + base-pistachio/src/core/platform_pd.cc | 387 + base-pistachio/src/core/platform_thread.cc | 235 + .../src/core/ram_session_support.cc | 27 + base-pistachio/src/core/rm_session_support.cc | 50 + base-pistachio/src/core/target.inc | 52 + base-pistachio/src/core/thread_start.cc | 62 + base-pistachio/src/core/x86/platform_x86.cc | 32 + base-pistachio/src/core/x86/target.mk | 7 + base-pistachio/src/kernel/target.mk | 61 + base-pistachio/src/platform/_main_helper.h | 26 + base-pistachio/src/util/hexdump/hexdump.cc | 55 + base/README | 12 + base/etc/README | 16 + base/etc/tools.conf | 57 + base/include/32bit/base/fixed_stdint.h | 60 + base/include/64bit/base/fixed_stdint.h | 50 + base/include/README | 3 + base/include/arm/cpu/cpu_state.h | 32 + base/include/base/allocator.h | 205 + base/include/base/allocator_avl.h | 325 + base/include/base/allocator_guard.h | 93 + base/include/base/blocking.h | 28 + base/include/base/cancelable_lock.h | 94 + base/include/base/capability.h | 291 + base/include/base/child.h | 583 + base/include/base/connection.h | 98 + base/include/base/console.h | 60 + base/include/base/cpu_state.h | 35 + base/include/base/crt0.h | 45 + base/include/base/elf.h | 157 + base/include/base/env.h | 88 + base/include/base/errno.h | 22 + base/include/base/exception.h | 19 + base/include/base/heap.h | 182 + base/include/base/ipc.h | 41 + base/include/base/ipc_generic.h | 624 + base/include/base/lock.h | 47 + base/include/base/lock_guard.h | 46 + base/include/base/object_pool.h | 130 + base/include/base/pager.h | 199 + base/include/base/platform_env.h | 150 + base/include/base/printf.h | 105 + base/include/base/process.h | 92 + base/include/base/rpc.h | 287 + base/include/base/rpc_args.h | 121 + base/include/base/rpc_client.h | 135 + base/include/base/rpc_server.h | 394 + base/include/base/semaphore.h | 173 + base/include/base/service.h | 414 + base/include/base/signal.h | 284 + base/include/base/slab.h | 255 + base/include/base/sleep.h | 29 + base/include/base/snprintf.h | 82 + base/include/base/stdint.h | 43 + base/include/base/sync_allocator.h | 168 + base/include/base/thread.h | 367 + base/include/base/thread_state.h | 26 + base/include/base/tslab.h | 35 + base/include/cap_session/cap_session.h | 59 + base/include/cap_session/capability.h | 22 + base/include/cap_session/client.h | 35 + base/include/cap_session/connection.h | 32 + base/include/cpu_session/capability.h | 22 + base/include/cpu_session/client.h | 65 + base/include/cpu_session/connection.h | 42 + base/include/cpu_session/cpu_session.h | 230 + base/include/dataspace/capability.h | 22 + base/include/dataspace/client.h | 33 + base/include/dataspace/dataspace.h | 54 + base/include/io_mem_session/capability.h | 22 + base/include/io_mem_session/client.h | 31 + base/include/io_mem_session/connection.h | 42 + base/include/io_mem_session/io_mem_session.h | 51 + base/include/io_port_session/capability.h | 22 + base/include/io_port_session/client.h | 47 + base/include/io_port_session/connection.h | 42 + .../include/io_port_session/io_port_session.h | 116 + base/include/irq_session/capability.h | 22 + base/include/irq_session/client.h | 31 + base/include/irq_session/connection.h | 39 + base/include/irq_session/irq_session.h | 47 + base/include/log_session/capability.h | 22 + base/include/log_session/client.h | 32 + base/include/log_session/connection.h | 32 + base/include/log_session/log_session.h | 49 + base/include/pager/capability.h | 30 + base/include/parent/capability.h | 22 + base/include/parent/client.h | 43 + base/include/parent/parent.h | 152 + base/include/pd_session/capability.h | 22 + base/include/pd_session/client.h | 35 + base/include/pd_session/connection.h | 41 + base/include/pd_session/pd_session.h | 63 + base/include/ram_session/capability.h | 29 + base/include/ram_session/client.h | 45 + base/include/ram_session/connection.h | 40 + base/include/ram_session/ram_session.h | 127 + base/include/rm_session/capability.h | 22 + base/include/rm_session/client.h | 51 + base/include/rm_session/connection.h | 40 + base/include/rm_session/rm_session.h | 196 + base/include/rom_session/capability.h | 22 + base/include/rom_session/client.h | 32 + base/include/rom_session/connection.h | 60 + base/include/rom_session/rom_session.h | 55 + base/include/root/capability.h | 22 + base/include/root/client.h | 38 + base/include/root/component.h | 250 + base/include/root/root.h | 93 + base/include/session/capability.h | 22 + base/include/session/session.h | 38 + base/include/signal_session/capability.h | 25 + base/include/signal_session/client.h | 43 + base/include/signal_session/connection.h | 32 + base/include/signal_session/signal_session.h | 96 + base/include/signal_session/source.h | 75 + base/include/signal_session/source_client.h | 33 + .../signal_session/source_rpc_object.h | 28 + base/include/thread/capability.h | 29 + base/include/util/arg_string.h | 325 + base/include/util/avl_string.h | 77 + base/include/util/avl_tree.h | 203 + base/include/util/fifo.h | 108 + base/include/util/list.h | 126 + base/include/util/meta.h | 636 + base/include/util/misc_math.h | 72 + base/include/util/string.h | 417 + base/include/util/token.h | 210 + base/include/util/touch.h | 35 + base/include/x86/cpu/atomic.h | 53 + base/include/x86/cpu/consts.h | 35 + base/include/x86_32/cpu/cpu_state.h | 48 + base/include/x86_64/cpu/cpu_state.h | 54 + base/lib/README | 1 + base/lib/import/import-stdcxx.mk | 21 + base/lib/mk/README | 24 + base/lib/mk/allocator_avl.mk | 4 + base/lib/mk/avl_tree.mk | 3 + base/lib/mk/console.mk | 3 + base/lib/mk/cxx.mk | 68 + base/lib/mk/elf.mk | 3 + base/lib/mk/env.mk | 5 + base/lib/mk/heap.mk | 4 + base/lib/mk/host/cxx.mk | 3 + base/lib/mk/log_console.mk | 4 + base/lib/mk/platform.mk | 0 base/lib/mk/process.mk | 4 + base/lib/mk/raw_server.mk | 3 + base/lib/mk/raw_signal.mk | 3 + base/lib/mk/server.mk | 3 + base/lib/mk/signal.mk | 3 + base/lib/mk/slab.mk | 3 + base/lib/mk/stdcxx.mk | 7 + base/lib/mk/thread.mk | 3 + base/mk/README | 14 + base/mk/base-libs.mk | 14 + base/mk/dep_lib.mk | 146 + base/mk/dep_prg.mk | 71 + base/mk/generic.mk | 85 + base/mk/global.mk | 175 + base/mk/lib.mk | 178 + base/mk/prg.mk | 183 + base/mk/spec-32bit.mk | 4 + base/mk/spec-64bit.mk | 4 + base/mk/spec-arm.mk | 14 + base/mk/spec-arm_v5.mk | 8 + base/mk/spec-arm_v7a.mk | 8 + base/mk/spec-experimental.mk | 1 + base/mk/spec-host.mk | 8 + base/mk/spec-platform_pbxa9.mk | 16 + base/mk/spec-platform_vea9x4.mk | 16 + base/mk/spec-platform_vpb926.mk | 18 + base/mk/spec-release.mk | 1 + base/mk/spec-x86_32.mk | 19 + base/mk/spec-x86_64.mk | 12 + base/run/rm_fault.run | 35 + base/run/sub_rm.run | 63 + base/src/README | 1 + base/src/base/README | 7 + base/src/base/allocator/README | 5 + base/src/base/allocator/allocator_avl.cc | 357 + base/src/base/allocator/slab.cc | 296 + base/src/base/avl_tree/avl_tree.cc | 164 + base/src/base/console/console.cc | 335 + base/src/base/console/core_printf.cc | 73 + base/src/base/console/log_console.cc | 127 + base/src/base/cxx/exception.cc | 38 + base/src/base/cxx/guard.cc | 72 + base/src/base/cxx/malloc_free.cc | 92 + base/src/base/cxx/misc.cc | 202 + base/src/base/cxx/new_delete.cc | 35 + base/src/base/cxx/unwind.cc | 45 + base/src/base/elf/elf.h | 254 + base/src/base/elf/elf_binary.cc | 159 + base/src/base/env/context_area.cc | 47 + base/src/base/env/env.cc | 31 + base/src/base/heap/heap.cc | 150 + base/src/base/heap/sliced_heap.cc | 114 + base/src/base/lock/lock.cc | 214 + base/src/base/process/process.cc | 279 + base/src/base/server/common.cc | 151 + base/src/base/server/server.cc | 39 + base/src/base/signal/signal.cc | 299 + base/src/base/thread/thread.cc | 208 + base/src/base/thread/thread_bootstrap.cc | 18 + base/src/base/thread/thread_start.cc | 71 + .../src/core/arm/io_port_session_component.cc | 63 + base/src/core/context_area.cc | 153 + base/src/core/core_mem_alloc.cc | 63 + base/src/core/cpu_session_component.cc | 196 + base/src/core/dataspace_component.cc | 52 + base/src/core/dump_alloc.cc | 58 + base/src/core/include/cap_root.h | 47 + base/src/core/include/cap_session_component.h | 47 + base/src/core/include/core_env.h | 179 + base/src/core/include/core_mem_alloc.h | 172 + base/src/core/include/core_parent.h | 61 + base/src/core/include/core_rm_session.h | 63 + base/src/core/include/cpu_root.h | 59 + base/src/core/include/cpu_session_component.h | 144 + base/src/core/include/dataspace_component.h | 139 + base/src/core/include/io_mem_root.h | 63 + .../core/include/io_mem_session_component.h | 139 + base/src/core/include/io_port_root.h | 71 + .../core/include/io_port_session_component.h | 77 + base/src/core/include/irq_root.h | 113 + base/src/core/include/irq_session_component.h | 140 + base/src/core/include/log_root.h | 54 + base/src/core/include/log_session_component.h | 85 + base/src/core/include/multiboot.h | 69 + base/src/core/include/pd_root.h | 54 + base/src/core/include/pd_session_component.h | 49 + base/src/core/include/platform_generic.h | 109 + base/src/core/include/ram_root.h | 65 + base/src/core/include/ram_session_component.h | 163 + base/src/core/include/rm_root.h | 97 + base/src/core/include/rm_session_component.h | 354 + base/src/core/include/rom_fs.h | 100 + base/src/core/include/rom_root.h | 55 + base/src/core/include/rom_session_component.h | 72 + base/src/core/include/signal_root.h | 75 + .../core/include/signal_session_component.h | 166 + base/src/core/io_mem_session_component.cc | 114 + base/src/core/main.cc | 245 + base/src/core/mb_info.h | 175 + base/src/core/multiboot_info.cc | 143 + base/src/core/pd_session_component.cc | 50 + base/src/core/ram_session_component.cc | 275 + base/src/core/rm_session_component.cc | 725 + base/src/core/rom_session_component.cc | 42 + base/src/core/signal_session_component.cc | 101 + base/src/core/signal_source_component.cc | 84 + .../src/core/x86/io_port_session_component.cc | 138 + base/src/platform/_main.cc | 259 + base/src/platform/_main_parent_cap.h | 33 + base/src/platform/arm/crt0.s | 37 + base/src/platform/genode.ld | 117 + base/src/platform/x86_32/crt0.s | 61 + base/src/platform/x86_64/crt0.s | 65 + base/src/test/rm_fault/main.cc | 213 + base/src/test/rm_fault/target.mk | 3 + base/src/test/rm_nested/main.cc | 123 + base/src/test/rm_nested/target.mk | 4 + base/src/test/sub_rm/config.h | 15 + base/src/test/sub_rm/main.cc | 191 + base/src/test/sub_rm/target.mk | 6 + dde_ipxe/Makefile | 54 + dde_ipxe/README | 18 + dde_ipxe/include/dde_ipxe/nic.h | 67 + dde_ipxe/lib/mk/dde_ipxe_nic.mk | 49 + dde_ipxe/lib/mk/dde_ipxe_support.mk | 5 + dde_ipxe/patches/dde_ipxe.patch | 42 + dde_ipxe/src/drivers/nic/main.cc | 136 + dde_ipxe/src/drivers/nic/target.mk | 3 + dde_ipxe/src/lib/dde_ipxe/dde.c | 350 + dde_ipxe/src/lib/dde_ipxe/dde_support.cc | 86 + dde_ipxe/src/lib/dde_ipxe/dummies.c | 30 + .../src/lib/dde_ipxe/include/bits/byteswap.h | 1 + .../src/lib/dde_ipxe/include/bits/compiler.h | 1 + dde_ipxe/src/lib/dde_ipxe/include/bits/cpu.h | 0 .../src/lib/dde_ipxe/include/bits/eltorito.h | 0 .../src/lib/dde_ipxe/include/bits/endian.h | 1 + .../src/lib/dde_ipxe/include/bits/errfile.h | 1 + dde_ipxe/src/lib/dde_ipxe/include/bits/io.h | 1 + dde_ipxe/src/lib/dde_ipxe/include/bits/nap.h | 0 .../src/lib/dde_ipxe/include/bits/pci_io.h | 3 + .../src/lib/dde_ipxe/include/bits/smbios.h | 0 .../src/lib/dde_ipxe/include/bits/stdint.h | 1 + .../src/lib/dde_ipxe/include/bits/string.h | 1 + .../src/lib/dde_ipxe/include/bits/timer.h | 6 + .../src/lib/dde_ipxe/include/bits/uaccess.h | 1 + .../src/lib/dde_ipxe/include/bits/umalloc.h | 0 .../dde_ipxe/include/config/local/console.h | 0 .../dde_ipxe/include/config/local/general.h | 0 .../lib/dde_ipxe/include/config/local/ioapi.h | 0 .../lib/dde_ipxe/include/config/local/nap.h | 0 .../dde_ipxe/include/config/local/serial.h | 0 .../lib/dde_ipxe/include/config/local/timer.h | 0 .../dde_ipxe/include/config/local/umalloc.h | 0 .../src/lib/dde_ipxe/include/env_dde_kit.h | 68 + dde_ipxe/src/lib/dde_ipxe/local.h | 28 + dde_ipxe/src/lib/dde_ipxe/nic.c | 337 + demo/doc/demo.txt | 216 + demo/doc/img/genode_logo.png | Bin 0 -> 17091 bytes demo/doc/img/launchpad.png | Bin 0 -> 58017 bytes demo/doc/img/liquid_fb.png | Bin 0 -> 240040 bytes demo/doc/img/liquid_fb_small.png | Bin 0 -> 288612 bytes demo/doc/img/setup.png | Bin 0 -> 82437 bytes demo/doc/img/x-ray.png | Bin 0 -> 99749 bytes demo/doc/img/x-ray_small.png | Bin 0 -> 225817 bytes demo/include/launchpad/launchpad.h | 231 + demo/include/libpng_static/png.h | 3481 +++++ demo/include/libpng_static/pngconf.h | 1472 ++ demo/include/libpng_static/pngusr.h | 19 + demo/include/libz_static/zconf.h | 332 + demo/include/libz_static/zlib.h | 1357 ++ demo/include/mini_c/errno.h | 21 + demo/include/mini_c/limits.h | 14 + demo/include/mini_c/stdio.h | 41 + demo/include/mini_c/stdlib.h | 30 + demo/include/mini_c/string.h | 31 + demo/include/mini_c/sys/types.h | 14 + demo/lib/import/import-libpng_static.mk | 1 + demo/lib/import/import-libz_static.mk | 1 + demo/lib/import/import-mini_c.mk | 1 + demo/lib/mk/launchpad.mk | 4 + demo/lib/mk/libpng_static.mk | 12 + demo/lib/mk/libz_static.mk | 9 + demo/lib/mk/mini_c.mk | 10 + demo/lib/mk/scout_widgets.mk | 42 + demo/src/app/backdrop/README | 19 + demo/src/app/backdrop/main.cc | 264 + demo/src/app/backdrop/target.mk | 4 + demo/src/app/launchpad/README | 34 + demo/src/app/launchpad/child_entry.h | 146 + demo/src/app/launchpad/launch_entry.h | 90 + demo/src/app/launchpad/launcher.cc | 28 + demo/src/app/launchpad/launchpad_window.cc | 173 + demo/src/app/launchpad/launchpad_window.h | 171 + demo/src/app/launchpad/loadbar.h | 254 + demo/src/app/launchpad/main.cc | 250 + demo/src/app/launchpad/section.h | 73 + demo/src/app/launchpad/status_entry.h | 71 + demo/src/app/launchpad/target.mk | 11 + demo/src/app/scout/common/about.cc | 191 + demo/src/app/scout/common/browser_window.cc | 460 + demo/src/app/scout/common/doc.cc | 456 + demo/src/app/scout/common/elements.cc | 453 + demo/src/app/scout/common/main.cc | 158 + demo/src/app/scout/common/navbar.cc | 200 + demo/src/app/scout/common/png_image.cc | 148 + demo/src/app/scout/common/refracted_icon.cc | 184 + demo/src/app/scout/common/scrollbar.cc | 327 + demo/src/app/scout/common/sky_texture.cc | 363 + demo/src/app/scout/common/test.txt | 11 + demo/src/app/scout/common/tick.cc | 125 + demo/src/app/scout/common/widgets.cc | 474 + demo/src/app/scout/data/about.rgba | Bin 0 -> 16384 bytes demo/src/app/scout/data/backward.rgba | Bin 0 -> 16384 bytes demo/src/app/scout/data/closed_icon.rgba | Bin 0 -> 1024 bytes demo/src/app/scout/data/cover.rgba | 1 + demo/src/app/scout/data/downarrow.rgba | Bin 0 -> 4096 bytes demo/src/app/scout/data/forward.rgba | Bin 0 -> 16384 bytes demo/src/app/scout/data/home.rgba | Bin 0 -> 16384 bytes demo/src/app/scout/data/index.rgba | Bin 0 -> 16384 bytes demo/src/app/scout/data/ior.map | Bin 0 -> 32768 bytes demo/src/app/scout/data/kill_icon.rgba | Bin 0 -> 1024 bytes demo/src/app/scout/data/loadbar.rgba | Bin 0 -> 1024 bytes demo/src/app/scout/data/mono16.tff | Bin 0 -> 45832 bytes demo/src/app/scout/data/nav_next.rgba | Bin 0 -> 16384 bytes demo/src/app/scout/data/nav_prev.rgba | Bin 0 -> 16384 bytes demo/src/app/scout/data/opened_icon.rgba | Bin 0 -> 1024 bytes demo/src/app/scout/data/pointer.rgba | Bin 0 -> 4096 bytes demo/src/app/scout/data/redbar.rgba | Bin 0 -> 1024 bytes demo/src/app/scout/data/sizer.rgba | Bin 0 -> 4096 bytes demo/src/app/scout/data/slider.rgba | Bin 0 -> 4096 bytes demo/src/app/scout/data/test.png | Bin 0 -> 45070 bytes demo/src/app/scout/data/titlebar.rgba | Bin 0 -> 4096 bytes demo/src/app/scout/data/uparrow.rgba | Bin 0 -> 4096 bytes demo/src/app/scout/data/vera16.tff | Bin 0 -> 52036 bytes demo/src/app/scout/data/vera18.tff | Bin 0 -> 63612 bytes demo/src/app/scout/data/vera20.tff | Bin 0 -> 80131 bytes demo/src/app/scout/data/vera22.tff | Bin 0 -> 96920 bytes demo/src/app/scout/data/vera24.tff | Bin 0 -> 112576 bytes demo/src/app/scout/data/verabi10.tff | Bin 0 -> 26458 bytes demo/src/app/scout/data/verai16.tff | Bin 0 -> 52656 bytes demo/src/app/scout/data/whitebar.rgba | Bin 0 -> 1024 bytes demo/src/app/scout/genode/launcher.cc | 29 + demo/src/app/scout/genode/platform_genode.cc | 382 + demo/src/app/scout/genode/startup.cc | 17 + demo/src/app/scout/genode/target.mk | 35 + demo/src/app/scout/include/browser.h | 162 + demo/src/app/scout/include/browser_window.h | 159 + demo/src/app/scout/include/canvas.h | 325 + demo/src/app/scout/include/canvas_rgb565.h | 236 + demo/src/app/scout/include/color.h | 52 + demo/src/app/scout/include/config.h | 26 + demo/src/app/scout/include/elements.h | 810 ++ demo/src/app/scout/include/event.h | 82 + demo/src/app/scout/include/fade_icon.h | 95 + demo/src/app/scout/include/fader.h | 78 + demo/src/app/scout/include/font.h | 62 + demo/src/app/scout/include/genode/alloc.h | 30 + .../scout/include/genode/launcher_config.h | 38 + demo/src/app/scout/include/genode/printf.h | 30 + demo/src/app/scout/include/genode/string.h | 28 + demo/src/app/scout/include/history.h | 112 + demo/src/app/scout/include/miscmath.h | 35 + demo/src/app/scout/include/platform.h | 161 + demo/src/app/scout/include/redraw_manager.h | 150 + demo/src/app/scout/include/refracted_icon.h | 76 + demo/src/app/scout/include/scout_types.h | 21 + demo/src/app/scout/include/scrollbar.h | 106 + demo/src/app/scout/include/sky_texture.h | 43 + demo/src/app/scout/include/styles.h | 53 + demo/src/app/scout/include/tick.h | 87 + demo/src/app/scout/include/titlebar.h | 91 + demo/src/app/scout/include/user_state.h | 164 + demo/src/app/scout/include/widgets.h | 165 + demo/src/app/scout/include/window.h | 222 + demo/src/lib/launchpad/launchpad.cc | 370 + demo/src/lib/libpng/contrib/png.c | 847 ++ demo/src/lib/libpng/contrib/pngerror.c | 313 + demo/src/lib/libpng/contrib/pngget.c | 937 ++ demo/src/lib/libpng/contrib/pngmem.c | 598 + demo/src/lib/libpng/contrib/pngpread.c | 1578 +++ demo/src/lib/libpng/contrib/pngread.c | 1461 ++ demo/src/lib/libpng/contrib/pngrio.c | 164 + demo/src/lib/libpng/contrib/pngrtran.c | 4219 ++++++ demo/src/lib/libpng/contrib/pngrutil.c | 3123 +++++ demo/src/lib/libpng/contrib/pngset.c | 1265 ++ demo/src/lib/libpng/contrib/pngtrans.c | 652 + demo/src/lib/libpng/contrib/pngwio.c | 228 + demo/src/lib/libpng/contrib/pngwrite.c | 1513 ++ demo/src/lib/libpng/contrib/pngwtran.c | 572 + demo/src/lib/libpng/contrib/pngwutil.c | 2750 ++++ demo/src/lib/libpng/main.cc | 63 + demo/src/lib/libpng/stdio.h | 0 demo/src/lib/libpng/target.mk | 7 + demo/src/lib/libz/contrib/adler32.c | 149 + demo/src/lib/libz/contrib/compress.c | 79 + demo/src/lib/libz/contrib/crc32.c | 423 + demo/src/lib/libz/contrib/crc32.h | 441 + demo/src/lib/libz/contrib/deflate.c | 1736 +++ demo/src/lib/libz/contrib/deflate.h | 331 + demo/src/lib/libz/contrib/gzio.c | 1026 ++ demo/src/lib/libz/contrib/infback.c | 623 + demo/src/lib/libz/contrib/inffast.c | 318 + demo/src/lib/libz/contrib/inffast.h | 11 + demo/src/lib/libz/contrib/inffixed.h | 94 + demo/src/lib/libz/contrib/inflate.c | 1368 ++ demo/src/lib/libz/contrib/inflate.h | 115 + demo/src/lib/libz/contrib/inftrees.c | 329 + demo/src/lib/libz/contrib/inftrees.h | 55 + demo/src/lib/libz/contrib/trees.c | 1219 ++ demo/src/lib/libz/contrib/trees.h | 128 + demo/src/lib/libz/contrib/uncompr.c | 61 + demo/src/lib/libz/contrib/zutil.c | 318 + demo/src/lib/libz/contrib/zutil.h | 269 + demo/src/lib/mini_c/README | 10 + demo/src/lib/mini_c/abort.cc | 22 + demo/src/lib/mini_c/atol.cc | 21 + demo/src/lib/mini_c/malloc_free.cc | 50 + demo/src/lib/mini_c/memcmp.cc | 19 + demo/src/lib/mini_c/memset.cc | 19 + demo/src/lib/mini_c/mini_c.c | 84 + demo/src/lib/mini_c/printf.cc | 24 + demo/src/lib/mini_c/snprintf.cc | 27 + demo/src/lib/mini_c/strlen.cc | 19 + demo/src/lib/mini_c/strtod.cc | 27 + demo/src/lib/mini_c/strtol.cc | 31 + demo/src/lib/mini_c/vsnprintf.cc | 21 + demo/src/server/liquid_framebuffer/README | 31 + .../liquid_framebuffer/framebuffer_window.h | 108 + demo/src/server/liquid_framebuffer/main.cc | 214 + .../src/server/liquid_framebuffer/services.cc | 256 + demo/src/server/liquid_framebuffer/services.h | 23 + demo/src/server/liquid_framebuffer/target.mk | 9 + demo/src/server/nitlog/main.cc | 412 + demo/src/server/nitlog/mono.tff | Bin 0 -> 20488 bytes demo/src/server/nitlog/target.mk | 5 + doc/Makefile | 38 + doc/build_system.txt | 466 + doc/coding_style.txt | 267 + doc/components.txt | 357 + doc/conventions.txt | 78 + doc/future_optimizations.txt | 68 + doc/getting_started.txt | 116 + doc/release_notes-08-11.txt | 830 ++ doc/release_notes-09-02.txt | 460 + doc/release_notes-09-05.txt | 585 + doc/release_notes-09-08.txt | 573 + doc/release_notes-09-11.txt | 1017 ++ doc/release_notes-10-02.txt | 1224 ++ doc/release_notes-10-05.txt | 1211 ++ doc/release_notes-10-08.txt | 618 + doc/release_notes-10-11.txt | 871 ++ doc/release_notes-11-02.txt | 876 ++ doc/release_notes-11-05.txt | 1289 ++ doc/release_notes-11-08.txt | 703 + doc/release_notes-11-11.txt | 1008 ++ gems/README | 6 + gems/include/terminal/character_screen.h | 219 + .../terminal/character_screen_tracer.h | 80 + gems/include/terminal/decoder.h | 421 + gems/include/terminal/types.h | 127 + gems/run/tcp_terminal.run | 122 + gems/run/terminal_decoder.run | 25 + gems/run/terminal_echo.run | 104 + gems/src/server/http_block/README | 26 + gems/src/server/http_block/http.cc | 305 + gems/src/server/http_block/http.h | 122 + gems/src/server/http_block/main.cc | 289 + gems/src/server/http_block/target.mk | 5 + gems/src/server/tcp_terminal/README | 12 + gems/src/server/tcp_terminal/main.cc | 514 + gems/src/server/tcp_terminal/target.mk | 3 + gems/src/server/terminal/main.cc | 1367 ++ gems/src/server/terminal/mono.tff | Bin 0 -> 34824 bytes gems/src/server/terminal/target.mk | 4 + gems/src/test/terminal_decoder/main.cc | 275 + gems/src/test/terminal_decoder/target.mk | 4 + gems/src/test/terminal_decoder/vim.vt | 1 + hello_tutorial/README | 2 + hello_tutorial/config/config | 12 + hello_tutorial/doc/hello_tutorial.txt | 406 + hello_tutorial/include/hello_session/client.h | 41 + .../include/hello_session/connection.h | 34 + .../include/hello_session/hello_session.h | 41 + hello_tutorial/run/hello.run | 48 + hello_tutorial/src/hello/client/main.cc | 38 + hello_tutorial/src/hello/client/target.mk | 3 + hello_tutorial/src/hello/server/main.cc | 93 + hello_tutorial/src/hello/server/target.mk | 3 + libports/Makefile | 69 + libports/README | 45 + libports/doc/libc.txt | 72 + libports/include/EGL/eglplatform.h | 34 + libports/include/freetype-genode/ftconfig.h | 500 + libports/include/freetype-genode/ftmodule.h | 32 + libports/include/gcc/README | 2 + libports/include/gcc/longlong.h | 1525 ++ libports/include/gmp/config.h | 522 + libports/include/gmp/x86_32/fac_ui.h | 19 + libports/include/gmp/x86_32/fib_table.h | 8 + libports/include/gmp/x86_32/gmp.h | 2230 +++ libports/include/gmp/x86_32/mp_bases.h | 11 + libports/include/gmp/x86_32/perfsqr.h | 50 + libports/include/jpeg/jconfig.h | 46 + libports/include/libc-genode/mntent.h | 13 + libports/include/libc-genode/sys/syscall.h | 4 + libports/include/libc-genode/timeconv.h | 0 libports/include/libc-plugin/fd_alloc.h | 69 + libports/include/libc-plugin/plugin.h | 124 + .../include/libc-plugin/plugin_registry.h | 46 + libports/include/lwip/arch/cc.h | 61 + libports/include/lwip/arch/perf.h | 20 + libports/include/lwip/arch/sys_arch.h | 30 + libports/include/lwip/genode.h | 52 + libports/include/lwip/lwipopts.h | 77 + libports/include/ncurses/ncurses_cfg.h | 201 + libports/include/python/osreldate.h | 3 + libports/include/python/pyconfig.h | 1092 ++ libports/include/python/x86_32/genode_defs.h | 20 + libports/include/python/x86_64/genode_defs.h | 20 + libports/include/readline/config.h | 269 + libports/lib/import/import-gmp.mk | 14 + libports/lib/import/import-jpeg.mk | 3 + libports/lib/import/import-libc.mk | 51 + libports/lib/import/import-libpng.mk | 1 + libports/lib/import/import-lwip.mk | 1 + libports/lib/import/import-mpfr.mk | 1 + libports/lib/import/import-ncurses.mk | 1 + libports/lib/import/import-python.mk | 13 + libports/lib/import/import-zlib.mk | 1 + libports/lib/mk/arm/libc-gen.mk | 18 + libports/lib/mk/arm/libm.mk | 19 + libports/lib/mk/ffat.mk | 12 + libports/lib/mk/ffat_block.mk | 13 + libports/lib/mk/freetype.mk | 51 + libports/lib/mk/gallium-aux.mk | 52 + libports/lib/mk/gallium-egl.mk | 44 + libports/lib/mk/gallium-failover.mk | 5 + libports/lib/mk/gallium-i915.mk | 30 + libports/lib/mk/gallium-identity.mk | 5 + libports/lib/mk/gallium-softpipe.mk | 5 + libports/lib/mk/gallium-trace.mk | 5 + libports/lib/mk/gallium.inc | 28 + libports/lib/mk/gallium.mk | 8 + libports/lib/mk/gmp-mpf.mk | 5 + libports/lib/mk/gmp-mpq.mk | 5 + libports/lib/mk/gmp-mpz.mk | 5 + libports/lib/mk/gmp.inc | 13 + libports/lib/mk/gmp.mk | 25 + libports/lib/mk/history.mk | 24 + libports/lib/mk/jpeg.mk | 27 + libports/lib/mk/libc-common.inc | 26 + libports/lib/mk/libc-gdtoa.mk | 13 + libports/lib/mk/libc-gen.inc | 13 + libports/lib/mk/libc-inet.mk | 9 + libports/lib/mk/libc-locale.mk | 7 + libports/lib/mk/libc-stdio.mk | 7 + libports/lib/mk/libc-stdlib.mk | 9 + libports/lib/mk/libc-stdtime.mk | 7 + libports/lib/mk/libc-string.mk | 18 + libports/lib/mk/libc.mk | 32 + libports/lib/mk/libc_ffat.mk | 6 + libports/lib/mk/libc_lock_pipe.mk | 6 + libports/lib/mk/libc_log.mk | 6 + libports/lib/mk/libc_lwip.mk | 5 + libports/lib/mk/libc_lwip_loopback.mk | 5 + libports/lib/mk/libc_lwip_nic_dhcp.mk | 5 + libports/lib/mk/libc_terminal.mk | 6 + libports/lib/mk/libdrm.mk | 11 + libports/lib/mk/libm.mk | 79 + libports/lib/mk/libpng.mk | 16 + libports/lib/mk/lwip.mk | 45 + libports/lib/mk/mesa-egl.mk | 13 + libports/lib/mk/mesa.inc | 21 + libports/lib/mk/mesa.mk | 65 + libports/lib/mk/mpfr.mk | 20 + libports/lib/mk/ncurses.mk | 34 + libports/lib/mk/python.inc | 143 + libports/lib/mk/readline.mk | 30 + libports/lib/mk/sdl.mk | 131 + libports/lib/mk/test-ldso.mk | 7 + libports/lib/mk/test-ldso2.mk | 5 + libports/lib/mk/x86_32/gmp-mpn.mk | 54 + libports/lib/mk/x86_32/libc-gen.mk | 23 + libports/lib/mk/x86_32/python.mk | 3 + libports/lib/mk/x86_64/libc-gen.mk | 18 + libports/lib/mk/x86_64/python.mk | 3 + libports/lib/mk/zlib.mk | 12 + libports/ports/ffat.mk | 43 + libports/ports/freetype.mk | 32 + libports/ports/gmp.mk | 39 + libports/ports/jpeg.mk | 24 + libports/ports/libc.mk | 468 + libports/ports/libdrm.mk | 27 + libports/ports/libpng.mk | 32 + libports/ports/lwip.mk | 37 + libports/ports/mesa.mk | 48 + libports/ports/mpfr.mk | 35 + libports/ports/ncurses.mk | 185 + libports/ports/python.mk | 33 + libports/ports/readline.mk | 30 + libports/ports/sdl.mk | 41 + libports/ports/zlib.mk | 32 + libports/run/eglgears.run | 130 + libports/run/libc_ffat.run | 126 + libports/run/lwip.run | 136 + libports/run/lwip_lx.run | 68 + libports/run/python.run | 92 + libports/run/test-libc.run | 36 + libports/src/app/eglgears/eglgears.c | 422 + libports/src/app/eglgears/target.mk | 3 + libports/src/lib/egl/driver.cc | 790 ++ libports/src/lib/egl/select_driver.cc | 122 + libports/src/lib/egl/select_driver.h | 25 + libports/src/lib/egl/st_opengl.c | 20 + libports/src/lib/ffat/config.patch | 30 + libports/src/lib/ffat/diskio.c | 146 + libports/src/lib/ffat/diskio_block.cc | 176 + libports/src/lib/gallium/README | 7 + libports/src/lib/gallium/dummy_trace.c | 26 + .../src/lib/gallium/i915/query_device_id.cc | 98 + libports/src/lib/gallium/i915/target.mk | 6 + libports/src/lib/gallium/main.cc | 14 + libports/src/lib/gallium/p_state_config.patch | 20 + libports/src/lib/gmp/config.m4 | 39 + libports/src/lib/gmp/mpn/x86/add_n.asm | 145 + libports/src/lib/gmp/mpn/x86/dummy.c | 21 + libports/src/lib/gmp/mpn/x86/fib_table.c | 61 + libports/src/lib/gmp/mpn/x86/mp_bases.c | 269 + libports/src/lib/libc/Version.def | 9 + libports/src/lib/libc/atexit.cc | 24 + libports/src/lib/libc/clock_gettime.cc | 32 + libports/src/lib/libc/dummies.cc | 143 + libports/src/lib/libc/environ.cc | 20 + libports/src/lib/libc/errno.cc | 21 + libports/src/lib/libc/exit.cc | 39 + libports/src/lib/libc/fd_alloc.cc | 80 + libports/src/lib/libc/file_operations.cc | 389 + libports/src/lib/libc/gai_strerror.cc | 21 + libports/src/lib/libc/gettimeofday.cc | 28 + libports/src/lib/libc/ioctl.cc | 20 + libports/src/lib/libc/issetugid.cc | 20 + libports/src/lib/libc/libc_debug.h | 35 + libports/src/lib/libc/malloc.cc | 93 + libports/src/lib/libc/munmap.cc | 22 + libports/src/lib/libc/patches/README | 19 + libports/src/lib/libc/patches/malloc_c.patch | 11 + .../src/lib/libc/patches/math_private.patch | 18 + .../src/lib/libc/patches/pthread_cancel.patch | 11 + .../lib/libc/patches/vfwprintf_c_warn.patch | 32 + libports/src/lib/libc/plugin.cc | 174 + libports/src/lib/libc/plugin_registry.cc | 80 + libports/src/lib/libc/progname.cc | 14 + libports/src/lib/libc/readlink.cc | 33 + libports/src/lib/libc/rlimit.cc | 36 + libports/src/lib/libc/select.cc | 274 + libports/src/lib/libc/sysctl.cc | 68 + libports/src/lib/libc_ffat/plugin.cc | 724 + libports/src/lib/libc_lock_pipe/plugin.cc | 336 + libports/src/lib/libc_log/plugin.cc | 108 + libports/src/lib/libc_lwip/init.cc | 23 + libports/src/lib/libc_lwip/plugin.cc | 598 + libports/src/lib/libc_lwip_loopback/init.cc | 29 + libports/src/lib/libc_lwip_nic_dhcp/init.cc | 34 + libports/src/lib/libc_terminal/README | 6 + libports/src/lib/libc_terminal/plugin.cc | 268 + libports/src/lib/libdrm/ioctl.cc | 198 + libports/src/lib/libpng/config.h | 87 + libports/src/lib/lwip/errno.patch | 16 + libports/src/lib/lwip/include/nic.h | 24 + libports/src/lib/lwip/include/ring_buffer.h | 119 + libports/src/lib/lwip/include/thread.h | 43 + libports/src/lib/lwip/include/timer.h | 55 + .../src/lib/lwip/libc_select_notify.patch | 40 + libports/src/lib/lwip/platform/nic.cc | 277 + libports/src/lib/lwip/platform/printf.cc | 36 + libports/src/lib/lwip/platform/sys_arch.cc | 572 + libports/src/lib/python/config.c | 87 + libports/src/lib/python/dup.c | 20 + libports/src/lib/python/libc_plugin.cc | 109 + libports/src/lib/python/libc_plugin_init.cc | 18 + libports/src/lib/python/posixmodule.patch | 23 + libports/src/lib/readline/genode.cc | 81 + libports/src/lib/sdl/SDL_config.h | 37 + libports/src/lib/sdl/SDL_config_genode.h | 83 + libports/src/lib/sdl/SDL_video.patch | 35 + .../src/lib/sdl/video/SDL_genode_fb_events.cc | 486 + .../src/lib/sdl/video/SDL_genode_fb_events.h | 37 + .../src/lib/sdl/video/SDL_genode_fb_video.cc | 327 + .../src/lib/sdl/video/SDL_genode_fb_video.h | 68 + libports/src/test/ldso/include/test-ldso.h | 24 + libports/src/test/ldso/lib/test-rtld.cc | 81 + libports/src/test/ldso/lib/test_lib.cc | 16 + libports/src/test/ldso/main.cc | 87 + libports/src/test/ldso/target.mk | 4 + libports/src/test/libc/main.cc | 40 + libports/src/test/libc/target.mk | 3 + libports/src/test/libc_ffat/main.cc | 96 + libports/src/test/libc_ffat/target.mk | 3 + libports/src/test/libports/freetype/target.mk | 5 + libports/src/test/libports/gmp/target.mk | 8 + libports/src/test/libports/jpeg/target.mk | 5 + libports/src/test/libports/libpng/target.mk | 5 + libports/src/test/libports/main.cc | 14 + libports/src/test/libports/mesa/target.mk | 6 + libports/src/test/libports/mpfr/target.mk | 9 + libports/src/test/libports/ncurses/target.mk | 5 + libports/src/test/libports/readline/target.mk | 5 + libports/src/test/libports/zlib/target.mk | 5 + libports/src/test/lwip/http_srv/main.cc | 126 + libports/src/test/lwip/http_srv/target.mk | 5 + libports/src/test/lwip/loopback/main.cc | 198 + libports/src/test/lwip/loopback/target.mk | 3 + libports/src/test/python/README | 20 + libports/src/test/python/hello.py | 13 + libports/src/test/python/main.cc | 77 + libports/src/test/python/target.mk | 6 + libports/src/test/sdl/main.cc | 82 + libports/src/test/sdl/target.mk | 3 + libports/tool/mesa/Makefile | 18 + os/README | 20 + os/config/bomb | 26 + os/config/demo | 40 + os/config/gta01 | 35 + os/config/linux_demo | 43 + os/config/mixer | 67 + os/config/nested_config | 79 + os/config/priority | 88 + os/doc/init.txt | 287 + .../audio_out_session/audio_out_session.h | 98 + os/include/audio_out_session/capability.h | 30 + os/include/audio_out_session/client.h | 74 + os/include/audio_out_session/connection.h | 49 + os/include/audio_out_session/rpc_object.h | 57 + os/include/blit/blit.h | 35 + os/include/block/component.h | 207 + os/include/block/driver.h | 114 + os/include/block_session/block_session.h | 151 + os/include/block_session/capability.h | 22 + os/include/block_session/client.h | 69 + os/include/block_session/connection.h | 43 + os/include/block_session/rpc_object.h | 54 + os/include/dde_kit/assert.h | 37 + os/include/dde_kit/dde_kit.h | 24 + os/include/dde_kit/initcall.h | 33 + os/include/dde_kit/interrupt.h | 60 + os/include/dde_kit/lock.h | 65 + os/include/dde_kit/memory.h | 134 + os/include/dde_kit/panic.h | 33 + os/include/dde_kit/pci.h | 144 + os/include/dde_kit/pgtab.h | 72 + os/include/dde_kit/printf.h | 56 + os/include/dde_kit/resources.h | 113 + os/include/dde_kit/semaphore.h | 73 + os/include/dde_kit/thread.h | 138 + os/include/dde_kit/timer.h | 75 + os/include/dde_kit/types.h | 41 + os/include/framebuffer_session/capability.h | 22 + os/include/framebuffer_session/client.h | 38 + os/include/framebuffer_session/connection.h | 75 + .../framebuffer_session/framebuffer_session.h | 72 + os/include/gpu/driver.h | 67 + os/include/init/child.h | 571 + os/include/init/child_config.h | 134 + os/include/init/child_policy.h | 302 + os/include/input/component.h | 116 + os/include/input/event.h | 59 + os/include/input/keycodes.h | 455 + os/include/input_session/capability.h | 22 + os/include/input_session/client.h | 38 + os/include/input_session/connection.h | 32 + os/include/input_session/input_session.h | 61 + os/include/ldso/arch.h | 24 + os/include/loader_session/capability.h | 22 + os/include/loader_session/client.h | 56 + os/include/loader_session/connection.h | 38 + os/include/loader_session/loader_session.h | 74 + os/include/net/arp.h | 294 + os/include/net/dhcp.h | 280 + os/include/net/ethernet.h | 181 + os/include/net/ipv4.h | 171 + os/include/net/netaddress.h | 73 + os/include/net/udp.h | 156 + os/include/nic/component.h | 239 + os/include/nic/driver.h | 90 + os/include/nic_session/capability.h | 22 + os/include/nic_session/client.h | 61 + os/include/nic_session/connection.h | 46 + os/include/nic_session/nic_session.h | 103 + os/include/nic_session/rpc_object.h | 55 + os/include/nitpicker_gfx/README | 3 + os/include/nitpicker_gfx/canvas.h | 119 + os/include/nitpicker_gfx/chunky_canvas.h | 203 + os/include/nitpicker_gfx/color.h | 36 + os/include/nitpicker_gfx/font.h | 61 + os/include/nitpicker_gfx/geometry.h | 161 + os/include/nitpicker_gfx/miscmath.h | 23 + os/include/nitpicker_gfx/nitpicker_types.h | 21 + os/include/nitpicker_gfx/pixel_rgb.h | 73 + os/include/nitpicker_gfx/pixel_rgb565.h | 56 + os/include/nitpicker_session/capability.h | 26 + os/include/nitpicker_session/client.h | 44 + os/include/nitpicker_session/connection.h | 126 + .../nitpicker_session/nitpicker_session.h | 75 + os/include/nitpicker_view/capability.h | 25 + os/include/nitpicker_view/client.h | 39 + os/include/nitpicker_view/nitpicker_view.h | 77 + os/include/os/alarm.h | 137 + os/include/os/attached_io_mem_dataspace.h | 83 + os/include/os/attached_ram_dataspace.h | 91 + os/include/os/config.h | 74 + os/include/os/irq_activation.h | 83 + os/include/os/packet_stream.h | 783 ++ os/include/os/ring_buffer.h | 94 + os/include/os/session_policy.h | 105 + os/include/os/timed_semaphore.h | 243 + os/include/packet_stream_rx/client.h | 66 + .../packet_stream_rx/packet_stream_rx.h | 61 + os/include/packet_stream_rx/rpc_object.h | 80 + os/include/packet_stream_tx/client.h | 73 + .../packet_stream_tx/packet_stream_tx.h | 65 + os/include/packet_stream_tx/rpc_object.h | 77 + os/include/pci_device/capability.h | 22 + os/include/pci_device/client.h | 51 + os/include/pci_device/pci_device.h | 206 + os/include/pci_session/capability.h | 22 + os/include/pci_session/client.h | 38 + os/include/pci_session/connection.h | 32 + os/include/pci_session/pci_session.h | 65 + os/include/platform/pbxa9/lan9118_defs.h | 38 + os/include/platform/pbxa9/pl011_defs.h | 63 + os/include/platform/pbxa9/pl050_defs.h | 28 + os/include/platform/pbxa9/pl11x_defs.h | 34 + os/include/platform/pbxa9/pl180_defs.h | 27 + os/include/platform/pbxa9/sp810_defs.h | 27 + os/include/platform/vea9x4/bus.h | 31 + os/include/platform/vea9x4/lan9118_defs.h | 41 + os/include/platform/vea9x4/pl011_defs.h | 66 + os/include/platform/vea9x4/pl050_defs.h | 31 + os/include/platform/vea9x4/pl11x_defs.h | 38 + os/include/platform/vea9x4/pl180_defs.h | 30 + os/include/platform/vea9x4/sp810_defs.h | 30 + os/include/platform/vpb926/pl011_defs.h | 67 + os/include/platform/vpb926/pl050_defs.h | 32 + os/include/platform/vpb926/pl11x_defs.h | 34 + os/include/platform/vpb926/sp810_defs.h | 28 + os/include/terminal_session/client.h | 120 + os/include/terminal_session/connection.h | 52 + .../terminal_session/terminal_session.h | 104 + os/include/timer_session/capability.h | 22 + os/include/timer_session/client.h | 31 + os/include/timer_session/connection.h | 38 + os/include/timer_session/server.h | 25 + os/include/timer_session/timer_session.h | 43 + os/include/util/endian.h | 30 + os/include/util/xml_node.h | 677 + os/include/xev_track/xev_track.h | 75 + os/lib/mk/alarm.mk | 3 + os/lib/mk/arm/ld.mk | 12 + os/lib/mk/arm/ldso-startup.mk | 5 + os/lib/mk/arm/ldso_crt0.mk | 3 + os/lib/mk/blit.mk | 3 + os/lib/mk/codezero/ldso-arch.mk | 7 + os/lib/mk/dde_kit.mk | 6 + os/lib/mk/ldso-arch.mk | 5 + os/lib/mk/ldso-startup.mk | 3 + os/lib/mk/linux/ldso-arch.mk | 6 + os/lib/mk/net.mk | 3 + os/lib/mk/pistachio/ldso-arch.mk | 5 + os/lib/mk/timed_semaphore.mk | 4 + os/lib/mk/x86_32/blit.mk | 5 + os/lib/mk/x86_32/ld.mk | 12 + os/lib/mk/x86_32/ldso_crt0.mk | 3 + os/lib/mk/x86_32/ldso_crt0_lx.mk | 3 + os/lib/mk/x86_64/blit.mk | 5 + os/lib/mk/x86_64/ld.mk | 14 + os/lib/mk/x86_64/ldso_crt0.mk | 3 + os/lib/mk/x86_64/ldso_crt0_lx.mk | 3 + os/lib/mk/xev_track.mk | 6 + os/run/ahci.run | 133 + os/run/demo.run | 111 + os/run/ldso.run | 55 + os/run/part_blk.run | 145 + os/run/rom_blk.run | 42 + os/run/sd_card.run | 80 + os/run/signal.run | 38 + os/run/tar_rom.run | 75 + os/run/timed_semaphore.run | 61 + os/run/uart.run | 90 + os/src/app/xvfb/README | 48 + os/src/app/xvfb/inject_input.cc | 99 + os/src/app/xvfb/inject_input.h | 38 + os/src/app/xvfb/main.cc | 340 + os/src/app/xvfb/target.mk | 9 + os/src/drivers/ahci/README | 19 + os/src/drivers/ahci/main.cc | 771 + os/src/drivers/ahci/target.mk | 4 + os/src/drivers/atapi/README | 24 + os/src/drivers/atapi/ata_bus_master.cc | 140 + os/src/drivers/atapi/ata_bus_master.h | 75 + os/src/drivers/atapi/ata_device.cc | 225 + os/src/drivers/atapi/ata_device.h | 160 + os/src/drivers/atapi/atapi_device.cc | 143 + .../drivers/atapi/contrib/mindrvr-guide.txt | 387 + os/src/drivers/atapi/contrib/mindrvr.c | 2831 ++++ os/src/drivers/atapi/contrib/mindrvr.h | 450 + os/src/drivers/atapi/endian.h | 29 + os/src/drivers/atapi/io.cc | 256 + os/src/drivers/atapi/io.h | 78 + os/src/drivers/atapi/main.cc | 265 + os/src/drivers/atapi/pio.h | 43 + os/src/drivers/atapi/target.mk | 9 + os/src/drivers/audio_out/linux/alsa.c | 88 + os/src/drivers/audio_out/linux/alsa.h | 32 + os/src/drivers/audio_out/linux/main.cc | 270 + os/src/drivers/audio_out/linux/target.mk | 6 + .../framebuffer/fiasco_ux/framebuffer.cc | 129 + .../framebuffer/fiasco_ux/framebuffer.h | 32 + os/src/drivers/framebuffer/fiasco_ux/main.cc | 125 + .../drivers/framebuffer/fiasco_ux/target.mk | 4 + os/src/drivers/framebuffer/pl11x/main.cc | 236 + .../drivers/framebuffer/pl11x/pbxa9/target.mk | 7 + os/src/drivers/framebuffer/pl11x/target.mk | 0 .../framebuffer/pl11x/vea9x4/target.mk | 7 + .../framebuffer/pl11x/vea9x4/video_memory.cc | 27 + .../drivers/framebuffer/pl11x/video_memory.cc | 22 + .../drivers/framebuffer/pl11x/video_memory.h | 26 + .../framebuffer/pl11x/vpb926/target.mk | 7 + os/src/drivers/framebuffer/sdl/fb_sdl.cc | 181 + os/src/drivers/framebuffer/sdl/input.cc | 204 + os/src/drivers/framebuffer/sdl/target.mk | 6 + os/src/drivers/framebuffer/vesa/README | 79 + .../drivers/framebuffer/vesa/contrib/LICENSE | 17 + .../framebuffer/vesa/contrib/Makefile.old | 19 + .../drivers/framebuffer/vesa/contrib/debug.c | 432 + .../drivers/framebuffer/vesa/contrib/decode.c | 1092 ++ .../drivers/framebuffer/vesa/contrib/fgets.c | 56 + os/src/drivers/framebuffer/vesa/contrib/fpu.c | 965 ++ os/src/drivers/framebuffer/vesa/contrib/ops.c | 11697 ++++++++++++++++ .../drivers/framebuffer/vesa/contrib/ops2.c | 2805 ++++ .../framebuffer/vesa/contrib/prim_ops.c | 2655 ++++ .../drivers/framebuffer/vesa/contrib/printk.c | 12 + os/src/drivers/framebuffer/vesa/contrib/sys.c | 661 + .../framebuffer/vesa/contrib/x86emu/debug.h | 210 + .../framebuffer/vesa/contrib/x86emu/decode.h | 88 + .../framebuffer/vesa/contrib/x86emu/fpu.h | 61 + .../vesa/contrib/x86emu/fpu_regs.h | 119 + .../framebuffer/vesa/contrib/x86emu/ops.h | 45 + .../vesa/contrib/x86emu/prim_asm.h | 970 ++ .../vesa/contrib/x86emu/prim_ops.h | 141 + .../framebuffer/vesa/contrib/x86emu/x86emui.h | 103 + .../drivers/framebuffer/vesa/framebuffer.cc | 253 + os/src/drivers/framebuffer/vesa/hw_emul.cc | 311 + os/src/drivers/framebuffer/vesa/ifx86emu.cc | 522 + .../framebuffer/vesa/include/framebuffer.h | 53 + .../framebuffer/vesa/include/hw_emul.h | 34 + .../framebuffer/vesa/include/ifx86emu.h | 90 + os/src/drivers/framebuffer/vesa/include/vbe.h | 103 + .../drivers/framebuffer/vesa/include/vesa.h | 105 + .../framebuffer/vesa/include/x86emu/regs.h | 339 + .../framebuffer/vesa/include/x86emu/types.h | 108 + .../framebuffer/vesa/include/x86emu/x86emu.h | 198 + os/src/drivers/framebuffer/vesa/main.cc | 258 + os/src/drivers/framebuffer/vesa/target.mk | 10 + os/src/drivers/input/dummy/README | 1 + os/src/drivers/input/dummy/main.cc | 95 + os/src/drivers/input/dummy/target.mk | 3 + os/src/drivers/input/fiasco_ux/input.cc | 224 + os/src/drivers/input/fiasco_ux/input.h | 37 + os/src/drivers/input/fiasco_ux/main.cc | 113 + os/src/drivers/input/fiasco_ux/target.mk | 4 + os/src/drivers/input/fiasco_ux/test.cc | 560 + os/src/drivers/input/ps2/event_queue.h | 69 + os/src/drivers/input/ps2/input_driver.h | 28 + os/src/drivers/input/ps2/irq_handler.h | 49 + os/src/drivers/input/ps2/pl050/irq_handler.h | 66 + os/src/drivers/input/ps2/pl050/main.cc | 76 + os/src/drivers/input/ps2/pl050/pl050.h | 159 + os/src/drivers/input/ps2/pl050/stdio.h | 1 + os/src/drivers/input/ps2/pl050/string.h | 1 + os/src/drivers/input/ps2/pl050/target.mk | 6 + os/src/drivers/input/ps2/ps2_keyboard.h | 459 + os/src/drivers/input/ps2/ps2_mouse.h | 267 + os/src/drivers/input/ps2/scan_code_set_1.h | 191 + os/src/drivers/input/ps2/scan_code_set_2.h | 321 + os/src/drivers/input/ps2/serial_interface.h | 33 + os/src/drivers/input/ps2/x86/i8042.h | 291 + os/src/drivers/input/ps2/x86/main.cc | 76 + os/src/drivers/input/ps2/x86/target.mk | 6 + os/src/drivers/nic/lan9118/lan9118.h | 369 + os/src/drivers/nic/lan9118/main.cc | 62 + os/src/drivers/nic/lan9118/target.mk | 5 + os/src/drivers/nic/linux/main.cc | 188 + os/src/drivers/nic/linux/target.mk | 4 + os/src/drivers/pci/main.cc | 51 + os/src/drivers/pci/pci_config_access.h | 134 + os/src/drivers/pci/pci_device_component.h | 94 + os/src/drivers/pci/pci_device_config.h | 189 + os/src/drivers/pci/pci_session_component.h | 255 + os/src/drivers/pci/x86/target.mk | 8 + os/src/drivers/platform/gta01/main.cc | 306 + os/src/drivers/platform/gta01/target.mk | 4 + os/src/drivers/rtc/x86/main.cc | 153 + os/src/drivers/rtc/x86/target.mk | 4 + os/src/drivers/sd_card/host_driver.h | 86 + os/src/drivers/sd_card/pl180/main.cc | 66 + os/src/drivers/sd_card/pl180/pl180.h | 290 + os/src/drivers/sd_card/pl180/target.mk | 6 + os/src/drivers/sd_card/sd_card.h | 135 + .../drivers/timer/codezero/platform_timer.cc | 41 + os/src/drivers/timer/codezero/target.mk | 14 + os/src/drivers/timer/fiasco/platform_timer.cc | 72 + os/src/drivers/timer/fiasco/target.mk | 8 + os/src/drivers/timer/foc/target.mk | 8 + os/src/drivers/timer/foc/timer_root.h | 95 + .../timer/foc/timer_session_component.h | 115 + os/src/drivers/timer/include/timer_root.h | 70 + .../timer/include/timer_session_component.h | 252 + .../timer/include_periodic/platform_timer.h | 94 + .../timer/include_pit/platform_timer.h | 183 + os/src/drivers/timer/linux/platform_timer.cc | 53 + os/src/drivers/timer/linux/target.mk | 7 + os/src/drivers/timer/main.cc | 54 + os/src/drivers/timer/nova/target.mk | 8 + os/src/drivers/timer/nova/timer_root.h | 106 + .../timer/nova/timer_session_component.h | 198 + .../drivers/timer/okl4_arm/platform_timer.cc | 40 + os/src/drivers/timer/okl4_arm/target.mk | 8 + os/src/drivers/timer/okl4_x86/target.mk | 8 + .../drivers/timer/pistachio/platform_timer.cc | 57 + os/src/drivers/timer/pistachio/target.mk | 8 + os/src/drivers/uart/README | 22 + os/src/drivers/uart/i8250/i8250.h | 142 + os/src/drivers/uart/i8250/main.cc | 104 + os/src/drivers/uart/i8250/target.mk | 6 + os/src/drivers/uart/pl011/calc_brd_values.py | 16 + os/src/drivers/uart/pl011/main.cc | 87 + os/src/drivers/uart/pl011/pl011.h | 168 + os/src/drivers/uart/pl011/target.mk | 6 + os/src/drivers/uart/terminal_component.h | 177 + os/src/drivers/uart/terminal_driver.h | 75 + os/src/init/config.explicit_routing | 91 + os/src/init/config.wildcard | 69 + os/src/init/main.cc | 177 + os/src/init/target.mk | 3 + os/src/lib/alarm/alarm.cc | 211 + os/src/lib/blit/blit.cc | 26 + os/src/lib/blit/x86/blit.cc | 116 + os/src/lib/blit/x86/x86_32/mmx.h | 47 + os/src/lib/blit/x86/x86_64/mmx.h | 46 + os/src/lib/dde_kit/dde_kit.cc | 20 + os/src/lib/dde_kit/interrupt.cc | 193 + os/src/lib/dde_kit/lock.cc | 67 + os/src/lib/dde_kit/memory.cc | 301 + os/src/lib/dde_kit/panic.cc | 45 + os/src/lib/dde_kit/pci.cc | 151 + os/src/lib/dde_kit/pci_tree.cc | 74 + os/src/lib/dde_kit/pci_tree.h | 247 + os/src/lib/dde_kit/pgtab.cc | 194 + os/src/lib/dde_kit/printf.cc | 40 + os/src/lib/dde_kit/resources.cc | 339 + os/src/lib/dde_kit/semaphore.cc | 66 + os/src/lib/dde_kit/thread.cc | 308 + os/src/lib/dde_kit/thread.h | 69 + os/src/lib/dde_kit/timer.cc | 207 + os/src/lib/ldso/README | 38 + os/src/lib/ldso/arch/binary_name.cc | 22 + os/src/lib/ldso/arch/codezero/dummy.c | 22 + os/src/lib/ldso/arch/linux/binary_name.cc | 25 + os/src/lib/ldso/arch/linux/parent_cap.cc | 18 + os/src/lib/ldso/arch/parent_cap.cc | 24 + os/src/lib/ldso/arm/crt0.s | 25 + os/src/lib/ldso/arm/platform.c | 125 + os/src/lib/ldso/contrib/amd64/reloc.c | 407 + os/src/lib/ldso/contrib/amd64/rtld_machdep.h | 78 + os/src/lib/ldso/contrib/amd64/rtld_start.S | 114 + os/src/lib/ldso/contrib/arm/reloc.c | 398 + os/src/lib/ldso/contrib/arm/rtld_machdep.h | 74 + os/src/lib/ldso/contrib/arm/rtld_start.S | 101 + os/src/lib/ldso/contrib/debug.c | 143 + os/src/lib/ldso/contrib/debug.h | 66 + os/src/lib/ldso/contrib/i386/reloc.c | 387 + os/src/lib/ldso/contrib/i386/rtld_machdep.h | 79 + os/src/lib/ldso/contrib/i386/rtld_start.S | 91 + os/src/lib/ldso/contrib/libmap.c | 369 + os/src/lib/ldso/contrib/libmap.h | 10 + os/src/lib/ldso/contrib/malloc.c | 518 + os/src/lib/ldso/contrib/map_object.c | 283 + os/src/lib/ldso/contrib/rtld.c | 3477 +++++ os/src/lib/ldso/contrib/rtld.h | 285 + os/src/lib/ldso/contrib/rtld_lock.c | 334 + os/src/lib/ldso/contrib/rtld_lock.h | 73 + os/src/lib/ldso/contrib/rtld_tls.h | 69 + os/src/lib/ldso/contrib/xmalloc.c | 59 + os/src/lib/ldso/dl_extensions.h | 30 + os/src/lib/ldso/environ.cc | 22 + os/src/lib/ldso/err.cc | 28 + os/src/lib/ldso/file.cc | 366 + os/src/lib/ldso/file.h | 39 + os/src/lib/ldso/include/arm/call_main.h | 24 + os/src/lib/ldso/include/libc/dlfcn.h | 139 + os/src/lib/ldso/include/libc/elf-hints.h | 50 + .../include/libc/libc-amd64/machine/elf.h | 123 + .../ldso/include/libc/libc-arm/machine/asm.h | 174 + .../ldso/include/libc/libc-arm/machine/elf.h | 103 + .../ldso/include/libc/libc-i386/machine/elf.h | 122 + os/src/lib/ldso/include/libc/sys/cdefs.h | 564 + os/src/lib/ldso/include/libc/sys/elf.h | 41 + os/src/lib/ldso/include/libc/sys/elf32.h | 245 + os/src/lib/ldso/include/libc/sys/elf64.h | 248 + os/src/lib/ldso/include/libc/sys/elf_common.h | 865 ++ .../lib/ldso/include/libc/sys/elf_generic.h | 88 + os/src/lib/ldso/include/libc/sys/link_elf.h | 99 + os/src/lib/ldso/include/libc/sys/queue.h | 627 + os/src/lib/ldso/include/libc_emu/err.h | 29 + os/src/lib/ldso/include/libc_emu/errno.h | 12 + os/src/lib/ldso/include/libc_emu/fcntl.h | 23 + os/src/lib/ldso/include/libc_emu/ldso_types.h | 63 + os/src/lib/ldso/include/libc_emu/link.h | 14 + .../ldso/include/libc_emu/machine/atomic.h | 13 + .../ldso/include/libc_emu/machine/segments.h | 12 + .../ldso/include/libc_emu/machine/sysarch.h | 23 + os/src/lib/ldso/include/libc_emu/stdio.h | 44 + os/src/lib/ldso/include/libc_emu/stdlib.h | 43 + os/src/lib/ldso/include/libc_emu/string.h | 142 + os/src/lib/ldso/include/libc_emu/sys/_types.h | 25 + os/src/lib/ldso/include/libc_emu/sys/ktrace.h | 20 + os/src/lib/ldso/include/libc_emu/sys/mman.h | 53 + os/src/lib/ldso/include/libc_emu/sys/mount.h | 12 + os/src/lib/ldso/include/libc_emu/sys/param.h | 35 + os/src/lib/ldso/include/libc_emu/sys/queue.h | 12 + os/src/lib/ldso/include/libc_emu/sys/stat.h | 6 + os/src/lib/ldso/include/libc_emu/sys/types.h | 13 + os/src/lib/ldso/include/libc_emu/sys/uio.h | 12 + os/src/lib/ldso/include/libc_emu/unistd.h | 67 + os/src/lib/ldso/include/x86_32/call_main.h | 32 + os/src/lib/ldso/include/x86_64/call_main.h | 32 + os/src/lib/ldso/ldso.ld | 18 + os/src/lib/ldso/ldso_types.c | 17 + os/src/lib/ldso/lock.cc | 82 + os/src/lib/ldso/main.c | 107 + os/src/lib/ldso/platform.c | 13 + os/src/lib/ldso/rtld_dummies.c | 54 + os/src/lib/ldso/startup/startup.cc | 31 + os/src/lib/ldso/startup/unwind_exidx.cc | 48 + os/src/lib/ldso/stdio.cc | 63 + os/src/lib/ldso/stdlib.cc | 71 + os/src/lib/ldso/string.cc | 57 + os/src/lib/ldso/symbol.map | 85 + os/src/lib/ldso/target.inc | 62 + os/src/lib/ldso/test.cc | 18 + os/src/lib/ldso/x86_32/crt0.s | 50 + os/src/lib/ldso/x86_32/linux/crt0.s | 54 + os/src/lib/ldso/x86_64/crt0.s | 43 + os/src/lib/ldso/x86_64/linux/crt0.s | 52 + os/src/lib/net/ethernet.cc | 16 + os/src/lib/net/ipv4.cc | 17 + os/src/lib/timed_semaphore/timed_semaphore.cc | 33 + os/src/lib/xev_track/xev_track.cc | 529 + os/src/platform/genode_dyn.ld | 276 + os/src/platform/genode_rel.ld | 256 + os/src/server/iso9660/README | 27 + os/src/server/iso9660/backing_store.h | 257 + os/src/server/iso9660/iso9660.cc | 496 + os/src/server/iso9660/iso9660.h | 81 + os/src/server/iso9660/main.cc | 321 + os/src/server/iso9660/target.mk | 3 + os/src/server/loader/README | 9 + os/src/server/loader/input_root.h | 70 + .../server/loader/input_session_component.cc | 91 + .../server/loader/input_session_component.h | 59 + os/src/server/loader/loader_child.h | 250 + os/src/server/loader/loader_root.h | 51 + .../server/loader/loader_session_component.cc | 165 + .../server/loader/loader_session_component.h | 86 + os/src/server/loader/loader_view_component.h | 87 + os/src/server/loader/main.cc | 33 + os/src/server/loader/nitpicker_root.h | 82 + .../loader/nitpicker_session_component.cc | 152 + .../loader/nitpicker_session_component.h | 107 + .../server/loader/nitpicker_view_component.h | 99 + os/src/server/loader/rom_root.h | 60 + os/src/server/loader/rom_session_component.cc | 70 + os/src/server/loader/rom_session_component.h | 60 + os/src/server/loader/tar_server_child.h | 171 + os/src/server/loader/target.mk | 8 + os/src/server/mixer/mixer.cc | 692 + os/src/server/mixer/target.mk | 3 + os/src/server/nic_bridge/address_node.cc | 54 + os/src/server/nic_bridge/address_node.h | 111 + os/src/server/nic_bridge/avl_safe.h | 47 + os/src/server/nic_bridge/component.cc | 146 + os/src/server/nic_bridge/component.h | 235 + os/src/server/nic_bridge/list_safe.h | 45 + os/src/server/nic_bridge/mac.cc | 21 + os/src/server/nic_bridge/mac.h | 80 + os/src/server/nic_bridge/main.cc | 53 + os/src/server/nic_bridge/packet_handler.cc | 235 + os/src/server/nic_bridge/packet_handler.h | 168 + os/src/server/nic_bridge/target.mk | 6 + os/src/server/nic_bridge/vlan.cc | 20 + os/src/server/nic_bridge/vlan.h | 56 + os/src/server/nic_loopback/main.cc | 210 + os/src/server/nic_loopback/target.mk | 3 + os/src/server/nit_fb/README | 11 + os/src/server/nit_fb/main.cc | 232 + os/src/server/nit_fb/target.mk | 3 + os/src/server/nitpicker/README | 26 + os/src/server/nitpicker/TODO | 7 + os/src/server/nitpicker/common/user_state.cc | 208 + os/src/server/nitpicker/common/view.cc | 121 + os/src/server/nitpicker/common/view_stack.cc | 319 + os/src/server/nitpicker/data/big_mouse.h | 36 + os/src/server/nitpicker/data/default.tff | Bin 0 -> 20568 bytes os/src/server/nitpicker/genode/main.cc | 914 ++ os/src/server/nitpicker/genode/target.mk | 14 + os/src/server/nitpicker/include/background.h | 48 + .../server/nitpicker/include/chunky_menubar.h | 52 + os/src/server/nitpicker/include/clip_guard.h | 50 + os/src/server/nitpicker/include/draw_label.h | 70 + os/src/server/nitpicker/include/list.h | 109 + os/src/server/nitpicker/include/menubar.h | 67 + os/src/server/nitpicker/include/mode.h | 61 + .../server/nitpicker/include/mouse_cursor.h | 68 + os/src/server/nitpicker/include/session.h | 109 + os/src/server/nitpicker/include/string.h | 34 + os/src/server/nitpicker/include/user_state.h | 81 + os/src/server/nitpicker/include/view.h | 141 + os/src/server/nitpicker/include/view_stack.h | 201 + os/src/server/part_blk/README | 65 + os/src/server/part_blk/back_end.cc | 232 + os/src/server/part_blk/main.cc | 234 + os/src/server/part_blk/part_blk.h | 75 + os/src/server/part_blk/target.mk | 3 + os/src/server/rom_loopdev/README | 6 + os/src/server/rom_loopdev/main.cc | 273 + os/src/server/rom_loopdev/target.mk | 3 + os/src/server/rom_prefetcher/main.cc | 122 + os/src/server/rom_prefetcher/target.mk | 3 + os/src/server/tar_rom/README | 13 + os/src/server/tar_rom/main.cc | 249 + os/src/server/tar_rom/target.mk | 3 + os/src/test/ahci/main.cc | 183 + os/src/test/ahci/target.mk | 3 + os/src/test/alarm/main.cc | 117 + os/src/test/alarm/target.mk | 3 + os/src/test/audio_out/README | 14 + os/src/test/audio_out/main.cc | 219 + os/src/test/audio_out/target.mk | 3 + os/src/test/block/main.cc | 119 + os/src/test/block/target.mk | 3 + os/src/test/bomb/main.cc | 266 + os/src/test/bomb/target.mk | 3 + os/src/test/dde_kit/i8042.h | 186 + os/src/test/dde_kit/target.mk | 3 + os/src/test/dde_kit/test.cc | 699 + os/src/test/fb_block_adapter/main.cc | 251 + os/src/test/fb_block_adapter/target.mk | 3 + os/src/test/input/key_strings.h | 514 + os/src/test/input/target.mk | 3 + os/src/test/input/test.cc | 61 + os/src/test/iso/main.cc | 155 + os/src/test/iso/target.mk | 3 + os/src/test/iso/test.txt | 1 + os/src/test/loader/main.cc | 67 + os/src/test/loader/target.mk | 3 + os/src/test/nic_loopback/main.cc | 186 + os/src/test/nic_loopback/target.mk | 3 + os/src/test/nitpicker/target.mk | 3 + os/src/test/nitpicker/test.cc | 191 + os/src/test/packet_stream/main.cc | 331 + os/src/test/packet_stream/target.mk | 3 + os/src/test/part_blk/main.cc | 115 + os/src/test/part_blk/target.mk | 3 + os/src/test/pci/target.mk | 3 + os/src/test/pci/test.cc | 86 + os/src/test/rom_blk/main.cc | 121 + os/src/test/rom_blk/target.mk | 3 + os/src/test/signal/main.cc | 529 + os/src/test/signal/target.mk | 3 + os/src/test/terminal_echo/main.cc | 46 + os/src/test/terminal_echo/target.mk | 3 + os/src/test/timed_semaphore/main.cc | 102 + os/src/test/timed_semaphore/target.mk | 3 + os/src/test/timer/main.cc | 120 + os/src/test/timer/target.mk | 3 + os/src/test/timer_accuracy/main.cc | 57 + os/src/test/timer_accuracy/target.mk | 4 + os/src/test/uart/main.cc | 38 + os/src/test/uart/target.mk | 3 + os/src/test/xev_track/main.cc | 97 + os/src/test/xev_track/target.mk | 6 + os/tool/dde_kit_adapt_sources | 9 + os/tool/dde_kit_find_initcalls | 24 + ports-foc/Makefile | 89 + ports-foc/README | 221 + ports-foc/config/android_config.arm | 1241 ++ ports-foc/config/android_config.x86_32 | 1322 ++ ports-foc/config/linux_config.arm | 1143 ++ ports-foc/config/linux_config.x86_32 | 1327 ++ ports-foc/include/32-bit/l4/util/l4_macros.h | 21 + ports-foc/include/64-bit/l4/util/l4_macros.h | 21 + ports-foc/include/genode/block.h | 52 + ports-foc/include/genode/framebuffer.h | 97 + ports-foc/include/genode/input.h | 50 + ports-foc/include/genode/linkage.h | 23 + ports-foc/include/genode/net.h | 40 + ports-foc/include/genode/terminal.h | 35 + ports-foc/include/l4/io/io.h | 103 + ports-foc/include/l4/log/log.h | 32 + ports-foc/include/l4/re/c/dataspace.h | 52 + ports-foc/include/l4/re/c/debug.h | 29 + ports-foc/include/l4/re/c/mem_alloc.h | 41 + ports-foc/include/l4/re/c/namespace.h | 32 + ports-foc/include/l4/re/c/rm.h | 50 + ports-foc/include/l4/re/c/util/cap.h | 29 + ports-foc/include/l4/re/c/util/cap_alloc.h | 30 + ports-foc/include/l4/re/consts.h | 23 + ports-foc/include/l4/re/env.h | 57 + ports-foc/include/l4/util/atomic.h | 17 + ports-foc/include/l4/util/cpu.h | 29 + ports-foc/include/l4/util/kip.h | 31 + ports-foc/include/l4/util/kprintf.h | 17 + ports-foc/include/l4/util/util.h | 53 + ports-foc/lib/mk/l4lx.mk | 35 + ports-foc/lib/mk/l4sys.mk | 3 + ports-foc/mk/l4lx.mk | 53 + ports-foc/patches/l4android_genode.patch | 924 ++ ports-foc/patches/l4lx_genode.patch | 938 ++ ports-foc/run/l4android.run | 200 + ports-foc/run/l4linux.run | 144 + ports-foc/src/drivers/Makefile | 5 + ports-foc/src/drivers/genode_block.c | 295 + ports-foc/src/drivers/genode_fb.c | 525 + ports-foc/src/drivers/genode_net.c | 191 + ports-foc/src/drivers/genode_rtc.c | 147 + ports-foc/src/drivers/genode_serial.c | 340 + ports-foc/src/l4android/arm/target.mk | 7 + ports-foc/src/l4android/x86_32/target.mk | 6 + ports-foc/src/l4linux/arm/target.mk | 7 + ports-foc/src/l4linux/x86_32/target.mk | 6 + ports-foc/src/lib/l4lx/dataspace.cc | 30 + ports-foc/src/lib/l4lx/env.cc | 51 + ports-foc/src/lib/l4lx/genode_block.cc | 312 + ports-foc/src/lib/l4lx/genode_framebuffer.cc | 87 + ports-foc/src/lib/l4lx/genode_input.cc | 158 + ports-foc/src/lib/l4lx/genode_net.cc | 226 + ports-foc/src/lib/l4lx/genode_terminal.cc | 112 + ports-foc/src/lib/l4lx/include/dataspace.h | 95 + ports-foc/src/lib/l4lx/include/env.h | 49 + ports-foc/src/lib/l4lx/include/l4lx_irq.h | 80 + ports-foc/src/lib/l4lx/include/l4lx_memory.h | 32 + ports-foc/src/lib/l4lx/include/l4lx_task.h | 46 + ports-foc/src/lib/l4lx/include/l4lx_thread.h | 54 + ports-foc/src/lib/l4lx/include/linux.h | 57 + ports-foc/src/lib/l4lx/include/rm.h | 165 + ports-foc/src/lib/l4lx/include/task.h | 86 + ports-foc/src/lib/l4lx/include/vcpu.h | 125 + ports-foc/src/lib/l4lx/l4_io.cc | 123 + ports-foc/src/lib/l4lx/l4_log.cc | 42 + ports-foc/src/lib/l4lx/l4_re_c_dataspace.cc | 135 + ports-foc/src/lib/l4lx/l4_re_c_debug.cc | 30 + ports-foc/src/lib/l4lx/l4_re_c_mem_alloc.cc | 54 + ports-foc/src/lib/l4lx/l4_re_c_namespace.cc | 33 + ports-foc/src/lib/l4lx/l4_re_c_rm.cc | 161 + ports-foc/src/lib/l4lx/l4_re_c_util_cap.cc | 53 + ports-foc/src/lib/l4lx/l4_re_env.cc | 79 + ports-foc/src/lib/l4lx/l4_util_cpu.cc | 30 + ports-foc/src/lib/l4lx/l4_util_kip.cc | 59 + ports-foc/src/lib/l4lx/l4_util_util.cc | 36 + ports-foc/src/lib/l4lx/l4lx_irq.cc | 279 + ports-foc/src/lib/l4lx/l4lx_memory.cc | 86 + ports-foc/src/lib/l4lx/l4lx_task.cc | 172 + ports-foc/src/lib/l4lx/l4lx_thread.cc | 176 + ports-foc/src/lib/l4lx/rm.cc | 254 + ports-foc/src/lib/l4lx/startup.cc | 149 + ports-okl4/Makefile | 74 + ports-okl4/README | 121 + ports-okl4/config/elfweaver_config | 72 + ports-okl4/config/init_config | 76 + ports-okl4/config/linux_config | 607 + ports-okl4/include/oklx_kernel/oklx/ioctl.h | 47 + ports-okl4/include/oklx_lib/genode/audio.h | 37 + ports-okl4/include/oklx_lib/genode/block.h | 30 + ports-okl4/include/oklx_lib/genode/config.h | 42 + ports-okl4/include/oklx_lib/genode/exit.h | 23 + .../include/oklx_lib/genode/framebuffer.h | 85 + ports-okl4/include/oklx_lib/genode/input.h | 32 + ports-okl4/include/oklx_lib/genode/lock.h | 45 + ports-okl4/include/oklx_lib/genode/memory.h | 40 + ports-okl4/include/oklx_lib/genode/net.h | 26 + ports-okl4/include/oklx_lib/genode/open.h | 26 + ports-okl4/include/oklx_lib/genode/printf.h | 22 + ports-okl4/include/oklx_lib/genode/sleep.h | 29 + ports-okl4/include/oklx_lib/iguana/eas.h | 66 + ports-okl4/include/oklx_lib/iguana/hardware.h | 39 + .../include/oklx_lib/iguana/memsection.h | 110 + ports-okl4/include/oklx_lib/iguana/pd.h | 47 + ports-okl4/include/oklx_lib/iguana/stdint.h | 20 + ports-okl4/include/oklx_lib/iguana/thread.h | 56 + ports-okl4/include/oklx_lib/iguana/tls.h | 24 + ports-okl4/include/oklx_lib/iguana/types.h | 25 + ports-okl4/lib/mk/oklx.mk | 32 + ports-okl4/patches/oklx_genode.patch | 3350 +++++ ports-okl4/patches/unionfs.patch | 11419 +++++++++++++++ ports-okl4/run/lx_block.run | 104 + ports-okl4/src/app/xev_track/bounding_box.h | 60 + ports-okl4/src/app/xev_track/main.cc | 187 + ports-okl4/src/app/xev_track/target.mk | 6 + .../src/lib/oklx/genode/genode_audio.cc | 303 + .../src/lib/oklx/genode/genode_block.cc | 179 + .../src/lib/oklx/genode/genode_config.cc | 145 + ports-okl4/src/lib/oklx/genode/genode_exit.cc | 29 + .../src/lib/oklx/genode/genode_framebuffer.cc | 173 + .../src/lib/oklx/genode/genode_input.cc | 153 + ports-okl4/src/lib/oklx/genode/genode_lock.cc | 53 + .../src/lib/oklx/genode/genode_memory.cc | 71 + ports-okl4/src/lib/oklx/genode/genode_net.cc | 176 + ports-okl4/src/lib/oklx/genode/genode_open.cc | 56 + .../src/lib/oklx/genode/genode_printf.cc | 29 + .../src/lib/oklx/genode/genode_sleep.cc | 34 + .../src/lib/oklx/genode/genode_threads.cc | 191 + ports-okl4/src/lib/oklx/iguana/iguana_eas.cc | 159 + .../src/lib/oklx/iguana/iguana_hardware.cc | 35 + .../src/lib/oklx/iguana/iguana_memsection.cc | 132 + ports-okl4/src/lib/oklx/iguana/iguana_pd.cc | 41 + .../src/lib/oklx/iguana/iguana_thread.cc | 54 + ports-okl4/src/lib/oklx/iguana/iguana_tls.cc | 34 + .../src/lib/oklx/include/oklx_memory_maps.h | 76 + .../src/lib/oklx/include/oklx_screens.h | 146 + .../src/lib/oklx/include/oklx_threads.h | 169 + ports-okl4/src/oklinux/main.cc | 1 + ports-okl4/src/oklinux/target.mk | 53 + ports/Makefile | 73 + ports/README | 15 + ports/doc/gdb.txt | 503 + ports/include/noux_session/capability.h | 22 + ports/include/noux_session/client.h | 47 + ports/include/noux_session/connection.h | 31 + ports/include/noux_session/noux_session.h | 99 + ports/include/noux_session/sysio.h | 256 + ports/lib/mk/fiasco_x86/gdbserver_platform.mk | 3 + ports/lib/mk/foc_arm/gdbserver_platform.mk | 7 + ports/lib/mk/foc_x86_32/gdbserver_platform.mk | 3 + ports/lib/mk/gdbserver_libc_support.mk | 7 + ports/lib/mk/gdbserver_platform.inc | 25 + ports/lib/mk/libc_noux.mk | 7 + .../lib/mk/linux_x86_32/gdbserver_platform.mk | 3 + ports/lib/mk/okl4_x86/gdbserver_platform.mk | 3 + .../mk/pistachio_x86/gdbserver_platform.mk | 3 + .../mk/x86_32/gdbserver_platform_x86_32.inc | 7 + ports/mk/noux.mk | 192 + ports/ports/arora.mk | 27 + ports/ports/bash.mk | 21 + ports/ports/binutils.mk | 21 + ports/ports/coreutils.mk | 20 + ports/ports/dash.mk | 21 + ports/ports/findutils.mk | 20 + ports/ports/gcc.mk | 28 + ports/ports/gdb.mk | 80 + ports/ports/make.mk | 20 + ports/ports/vancouver.mk | 21 + ports/ports/vim.mk | 32 + ports/run/debug_nitpicker.run | 147 + ports/run/gdb_monitor.run | 101 + ports/run/noux.run | 77 + ports/run/noux_vim.run | 148 + ports/run/vancouver.run | 126 + ports/src/app/arora/arora.pro | 1 + ports/src/app/arora/arora_bookmarks.patch | 29 + .../src/app/arora/arora_disable_adblock.patch | 42 + .../arora/arora_disable_program_exit.patch | 13 + ports/src/app/arora/arora_genode.patch | 401 + ports/src/app/arora/arora_move_window.patch | 14 + .../app/arora/arora_nitpicker_plugin.patch | 28 + ports/src/app/arora/arora_startpage.patch | 24 + ports/src/app/arora/demo/bg.png | Bin 0 -> 521 bytes ports/src/app/arora/demo/bg_content.png | Bin 0 -> 676 bytes ports/src/app/arora/demo/bg_top.png | Bin 0 -> 3022 bytes ports/src/app/arora/demo/busybox.html | 70 + ports/src/app/arora/demo/demo.html | 7 + ports/src/app/arora/demo/http_block.png | Bin 0 -> 76084 bytes ports/src/app/arora/demo/intro.html | 81 + ports/src/app/arora/demo/nitpicker.html | 142 + ports/src/app/arora/demo/nitpicker_plugin.png | Bin 0 -> 96674 bytes ports/src/app/arora/demo/tetrix.html | 15 + ports/src/app/arora/demo/tinycore.html | 120 + ports/src/app/arora/demo/title_bg.png | Bin 0 -> 785 bytes ports/src/app/arora/demo_html.qrc | 14 + .../arora/qwebplugins/nitpicker/nitpicker.pri | 13 + .../qwebplugins/nitpicker/nitpickerplugin.cpp | 78 + .../qwebplugins/nitpicker/nitpickerplugin.h | 27 + .../nitpicker/nitpickerpluginwidget.cpp | 80 + .../nitpicker/nitpickerpluginwidget.h | 39 + ports/src/app/arora/target.mk | 251 + ports/src/app/gdb_monitor/app_child.h | 311 + ports/src/app/gdb_monitor/append_list.h | 110 + ports/src/app/gdb_monitor/cpu_root.h | 63 + .../app/gdb_monitor/cpu_session_component.cc | 191 + .../app/gdb_monitor/cpu_session_component.h | 73 + ports/src/app/gdb_monitor/dataspace_object.h | 43 + ports/src/app/gdb_monitor/gdb_stub_thread.cc | 40 + ports/src/app/gdb_monitor/gdb_stub_thread.h | 61 + ports/src/app/gdb_monitor/gdbserver/config.h | 31 + .../app/gdb_monitor/gdbserver/genode-low.cc | 345 + .../app/gdb_monitor/gdbserver/genode-low.h | 41 + .../app/gdb_monitor/gdbserver_genode.patch | 938 ++ ports/src/app/gdb_monitor/main.cc | 106 + ports/src/app/gdb_monitor/ram_root.h | 48 + .../app/gdb_monitor/ram_session_component.cc | 67 + .../app/gdb_monitor/ram_session_component.h | 54 + ports/src/app/gdb_monitor/rm_root.h | 65 + .../app/gdb_monitor/rm_session_component.cc | 157 + .../app/gdb_monitor/rm_session_component.h | 106 + ports/src/app/gdb_monitor/rom.h | 122 + .../app/gdb_monitor/signal_handler_thread.cc | 61 + .../app/gdb_monitor/signal_handler_thread.h | 40 + ports/src/app/gdb_monitor/target.mk | 42 + ports/src/app/gdb_monitor/thread_info.h | 43 + .../lib/gdbserver_libc_support/elf/common.h | 20 + .../gdbserver_libc_support.c | 43 + .../gdbserver_libc_support.h | 36 + .../lib/gdbserver_libc_support/sys/procfs.h | 25 + .../lib/gdbserver_libc_support/sys/ptrace.h | 40 + .../src/lib/gdbserver_libc_support/sys/vfs.h | 19 + .../lib/gdbserver_platform/fiasco_x86_low.cc | 75 + .../src/lib/gdbserver_platform/foc_arm_low.cc | 122 + .../lib/gdbserver_platform/foc_x86_32_low.cc | 111 + .../gdbserver_platform_helper.cc | 40 + .../gdbserver_platform_helper.h | 19 + ports/src/lib/gdbserver_platform/i386.h | 37 + .../gdbserver_platform/linux_x86_32_low.cc | 56 + .../lib/gdbserver_platform/okl4_x86_low.cc | 75 + .../gdbserver_platform/pistachio_x86_low.cc | 77 + ports/src/lib/gdbserver_platform/reg-arm.h | 47 + ports/src/lib/libc_noux/plugin.cc | 731 + ports/src/lib/libc_noux/target.mk | 2 + ports/src/noux-pkg/bash/build.patch | 40 + ports/src/noux-pkg/bash/target.mk | 11 + ports/src/noux-pkg/binutils/build.patch | 11 + ports/src/noux-pkg/binutils/target.mk | 15 + ports/src/noux-pkg/coreutils/target.mk | 10 + ports/src/noux-pkg/dash/build.patch | 18 + ports/src/noux-pkg/dash/target.mk | 8 + ports/src/noux-pkg/findutils/target.mk | 1 + ports/src/noux-pkg/gcc/build.patch | 129 + ports/src/noux-pkg/gcc/target.mk | 207 + ports/src/noux-pkg/make/target.mk | 6 + ports/src/noux-pkg/vim/target.mk | 56 + ports/src/noux/args.h | 116 + ports/src/noux/child.h | 302 + ports/src/noux/directory_service.h | 43 + ports/src/noux/dummy_input_io_channel.h | 28 + ports/src/noux/environment.h | 90 + ports/src/noux/file_descriptor_registry.h | 116 + ports/src/noux/file_io_service.h | 32 + ports/src/noux/file_system.h | 96 + ports/src/noux/io_channel.h | 111 + ports/src/noux/main.cc | 451 + ports/src/noux/path.h | 303 + ports/src/noux/pwd.h | 42 + ports/src/noux/range_checked_index.h | 46 + ports/src/noux/root_file_system.h | 93 + ports/src/noux/shared_pointer.h | 142 + ports/src/noux/signal_dispatcher.h | 28 + ports/src/noux/tar_file_system.h | 408 + ports/src/noux/target.mk | 4 + ports/src/noux/terminal_io_channel.h | 128 + ports/src/noux/vfs.h | 87 + ports/src/noux/vfs_handle.h | 98 + ports/src/noux/vfs_io_channel.h | 102 + ports/src/noux/wake_up_notifier.h | 37 + ports/src/test/gdb_monitor/main.cc | 79 + ports/src/test/gdb_monitor/target.mk | 3 + ports/src/vancouver/README | 9 + ports/src/vancouver/boot_module_provider.h | 199 + ports/src/vancouver/device_model_registry.cc | 88 + ports/src/vancouver/device_model_registry.h | 66 + ports/src/vancouver/main.cc | 992 ++ ports/src/vancouver/nova_user_env.cc | 133 + ports/src/vancouver/service/profile.h | 22 + ports/src/vancouver/target.mk | 41 + qt4/Makefile | 77 + qt4/README | 27 + qt4/include/genode/thread_qt.h | 116 + .../qnitpickerviewwidget.h | 53 + qt4/include/qpluginwidget/qpluginwidget.h | 107 + .../private/qeventdispatcher_genode_p.h | 208 + qt4/include/qt4/QtCore/qconfig-genode.h | 585 + qt4/include/qt4/QtCore/qconfig.h | 369 + .../private/qwindowsurface_nitpicker_qws_p.h | 80 + qt4/include/qt4/QtGui/qinputnitpicker_qws.h | 52 + qt4/include/qt4/QtGui/qkbdnitpicker_qws.h | 69 + qt4/include/qt4/QtGui/qkbdpc101_qws.h | 95 + qt4/include/qt4/QtGui/qmousenitpicker_qws.h | 71 + qt4/include/qt4/QtGui/qscreennitpicker_qws.h | 83 + qt4/lib/import/import-qt4.mk | 143 + qt4/lib/import/import-qt_core.mk | 3 + qt4/lib/import/import-qt_gui.mk | 3 + qt4/lib/import/import-qt_javascriptcore.mk | 3 + qt4/lib/import/import-qt_jscore.mk | 3 + qt4/lib/import/import-qt_network.mk | 3 + qt4/lib/import/import-qt_script.mk | 3 + qt4/lib/import/import-qt_scriptclassic.mk | 3 + qt4/lib/import/import-qt_scripttools.mk | 3 + qt4/lib/import/import-qt_svg.mk | 3 + qt4/lib/import/import-qt_ui_tools.mk | 3 + qt4/lib/import/import-qt_webcore.mk | 3 + qt4/lib/import/import-qt_xml.mk | 3 + qt4/lib/mk/dejavusans.mk | 7 + qt4/lib/mk/qgif.mk | 10 + qt4/lib/mk/qjpeg.mk | 10 + qt4/lib/mk/qnitpickerviewwidget.mk | 10 + qt4/lib/mk/qpluginwidget.mk | 10 + qt4/lib/mk/qt_core.mk | 312 + qt4/lib/mk/qt_gui.mk | 767 + qt4/lib/mk/qt_jscore.mk | 284 + qt4/lib/mk/qt_network.mk | 150 + qt4/lib/mk/qt_script.mk | 1 + qt4/lib/mk/qt_script46.mk | 346 + qt4/lib/mk/qt_scriptclassic.mk | 94 + qt4/lib/mk/qt_scripttools.mk | 160 + qt4/lib/mk/qt_svg.mk | 66 + qt4/lib/mk/qt_ui_tools.mk | 66 + qt4/lib/mk/qt_webcore.mk | 1870 +++ qt4/lib/mk/qt_xml.mk | 50 + qt4/run/qt4.run | 132 + .../calculatorform/calculatorform.cpp | 57 + .../examples/calculatorform/calculatorform.h | 59 + .../calculatorform/calculatorform.pro | 8 + .../examples/calculatorform/calculatorform.ui | 284 + qt4/src/app/examples/calculatorform/font.qrc | 6 + qt4/src/app/examples/calculatorform/main.cpp | 50 + qt4/src/app/examples/calculatorform/target.mk | 77 + qt4/src/app/examples/previewer/target.mk | 99 + qt4/src/app/examples/tetrix/font.qrc | 6 + qt4/src/app/examples/tetrix/main.cpp | 117 + qt4/src/app/examples/tetrix/target.mk | 77 + qt4/src/app/examples/tetrix/tetrix.pro | 11 + qt4/src/app/examples/tetrix/tetrix.qrc | 8 + qt4/src/app/examples/tetrix/tetrixboard.cpp | 127 + qt4/src/app/examples/tetrix/tetrixboard.h | 96 + qt4/src/app/examples/tetrix/tetrixboard.js | 249 + qt4/src/app/examples/tetrix/tetrixpiece.js | 131 + qt4/src/app/examples/tetrix/tetrixwindow.js | 16 + qt4/src/app/examples/tetrix/tetrixwindow.ui | 164 + qt4/src/app/examples/textedit/target.mk | 98 + qt4/src/app/qt_launchpad/child_entry.cpp | 29 + qt4/src/app/qt_launchpad/child_entry.h | 43 + qt4/src/app/qt_launchpad/child_entry.ui | 148 + qt4/src/app/qt_launchpad/font.qrc | 6 + qt4/src/app/qt_launchpad/kbyte_loadbar.cpp | 24 + qt4/src/app/qt_launchpad/kbyte_loadbar.h | 33 + qt4/src/app/qt_launchpad/launch_entry.cpp | 31 + qt4/src/app/qt_launchpad/launch_entry.h | 43 + qt4/src/app/qt_launchpad/launch_entry.ui | 133 + qt4/src/app/qt_launchpad/main.cpp | 47 + qt4/src/app/qt_launchpad/qt_launchpad.cpp | 113 + qt4/src/app/qt_launchpad/qt_launchpad.h | 46 + qt4/src/app/qt_launchpad/qt_launchpad.pro | 11 + qt4/src/app/qt_launchpad/qt_launchpad.ui | 152 + qt4/src/app/qt_launchpad/target.mk | 83 + qt4/src/app/tmpl/target.mk.example | 85 + qt4/src/lib/dejavusans/dejavusans.qrc | 6 + .../qnitpickerviewwidget.cpp | 277 + qt4/src/lib/qpluginwidget/qpluginwidget.cpp | 313 + qt4/src/lib/qt4/mkspecs/genode-g++/qmake.conf | 14 + .../qt4/mkspecs/genode-g++/qplatformdefs.h | 100 + .../qt4/mkspecs/qws/genode-x86-g++/qmake.conf | 9 + .../qws/genode-x86-g++/qplatformdefs.h | 42 + qt4/src/lib/qt4/previewer_example.patch | 23 + qt4/src/lib/qt4/qt4_genode.patch | 1644 +++ qt4/src/lib/qt4/qt4_include_time_h.patch | 38 + .../qt4_lwip_connect_semantics_adaption.patch | 26 + .../lib/qt4/qt4_no_exit_on_window_close.patch | 30 + .../qt4/qt4_no_search_for_resolv_lib.patch | 19 + .../qt4_no_separate_host_lookup_threads.patch | 72 + qt4/src/lib/qt4/qt4_nonblocking_sockets.patch | 21 + qt4/src/lib/qt4/qt4_renderwidget.patch | 17 + qt4/src/lib/qt4/qt4_virtual_deletelater.patch | 12 + .../lib/qt4/src/corelib/global/qconfig.cpp | 32 + .../qt4/src/corelib/io/qprocess_genode.cpp | 275 + .../kernel/qeventdispatcher_genode.cpp | 990 ++ .../qt4/src/corelib/thread/qmutex_genode.cpp | 88 + .../qt4/src/corelib/thread/qthread_genode.cpp | 431 + .../corelib/thread/qwaitcondition_genode.cpp | 264 + .../src/gui/embedded/qinputnitpicker_qws.cpp | 106 + .../src/gui/embedded/qkbdnitpicker_qws.cpp | 73 + .../qt4/src/gui/embedded/qkbdpc101_qws.cpp | 493 + .../src/gui/embedded/qmousenitpicker_qws.cpp | 126 + .../src/gui/embedded/qscreennitpicker_qws.cpp | 293 + .../painting/qwindowsurface_nitpicker_qws.cpp | 169 + qt4/src/lib/qt4/textedit_example.patch | 32 + qt4/src/lib/qt_main/qt_main.cc | 77 + qt4/tool/Makefile | 76 + qt4/tool/qmake/Makefile | 150 + tool/README | 50 + tool/autopilot | 360 + tool/beautify | 1770 +++ tool/boot/chain.c32 | Bin 0 -> 20192 bytes tool/boot/isolinux.bin | Bin 0 -> 24576 bytes tool/boot/isolinux.cfg | 5 + tool/boot/stage2_eltorito | Bin 0 -> 165928 bytes tool/builddir/build.mk | 280 + tool/builddir/etc/README | 2 + tool/builddir/etc/build.conf.codezero_vpb926 | 1 + tool/builddir/etc/build.conf.drivers_x86 | 12 + tool/builddir/etc/build.conf.fiasco_x86 | 1 + tool/builddir/etc/build.conf.foc_pbxa9 | 1 + tool/builddir/etc/build.conf.foc_vea9x4 | 1 + tool/builddir/etc/build.conf.foc_x86_32 | 1 + tool/builddir/etc/build.conf.foc_x86_64 | 1 + tool/builddir/etc/build.conf.generic | 42 + tool/builddir/etc/build.conf.linux_x86 | 1 + tool/builddir/etc/build.conf.mb_ml507 | 2 + .../etc/build.conf.mb_s3a_starter_kit | 2 + tool/builddir/etc/build.conf.nova_x86 | 6 + tool/builddir/etc/build.conf.okl4_x86 | 1 + tool/builddir/etc/build.conf.pistachio_x86 | 1 + tool/builddir/etc/build.conf.ports-foc | 5 + tool/builddir/etc/build.conf.ports-okl4 | 5 + tool/builddir/etc/build.conf.qemu_no_kvm | 10 + tool/create_builddir | 145 + tool/create_iso | 70 + tool/fix_include_ifndef | 119 + tool/libgcc_libc_stub.h | 641 + tool/parse_cxx | 906 ++ tool/run | 484 + tool/tool_chain | 579 + 2462 files changed, 320115 insertions(+), 3 deletions(-) create mode 100644 LICENSE create mode 100644 base-codezero/Makefile create mode 100644 base-codezero/README create mode 100644 base-codezero/config/vpb926.cml create mode 100644 base-codezero/doc/codezero.txt create mode 100644 base-codezero/etc/specs.conf create mode 100644 base-codezero/include/arm/cpu/atomic.h create mode 100644 base-codezero/include/base/ipc_msgbuf.h create mode 100644 base-codezero/include/base/ipc_pager.h create mode 100644 base-codezero/include/base/native_types.h create mode 100644 base-codezero/include/codezero/dummies/stdio.h create mode 100644 base-codezero/include/codezero/dummies/string.h create mode 100644 base-codezero/include/codezero/syscalls.h create mode 100644 base-codezero/lib/mk/arm/startup.mk create mode 100644 base-codezero/lib/mk/arm_v5/l4.mk create mode 100644 base-codezero/lib/mk/arm_v5/l4_arm_v5.mk create mode 100644 base-codezero/lib/mk/codezero_cml.inc create mode 100644 base-codezero/lib/mk/cxx.mk create mode 100644 base-codezero/lib/mk/ipc.mk create mode 100644 base-codezero/lib/mk/l4.inc create mode 100644 base-codezero/lib/mk/lock.mk create mode 100644 base-codezero/lib/mk/pager.mk create mode 100644 base-codezero/lib/mk/pl011/core_printf.mk create mode 100644 base-codezero/lib/mk/platform.mk create mode 100644 base-codezero/lib/mk/thread.mk create mode 100644 base-codezero/mk/spec-codezero.mk create mode 100644 base-codezero/mk/spec-codezero_arm.mk create mode 100644 base-codezero/mk/spec-codezero_arm_v5.mk create mode 100644 base-codezero/mk/spec-codezero_platform_vpb926.mk create mode 100644 base-codezero/patches/README create mode 100644 base-codezero/patches/binutils-2.21.patch create mode 100644 base-codezero/patches/gcc_4_6_1_fixes.patch create mode 100644 base-codezero/patches/gcc_shared_enabled.patch create mode 100644 base-codezero/patches/libc_search_dir.patch create mode 100644 base-codezero/patches/scons-2.0.1.patch create mode 100644 base-codezero/patches/set_fixed_pager.patch create mode 100644 base-codezero/run/env create mode 100644 base-codezero/src/base/console/pl011/core_console.h create mode 100644 base-codezero/src/base/cxx/exception.cc create mode 100644 base-codezero/src/base/cxx/memcmp.cc create mode 100644 base-codezero/src/base/ipc/ipc.cc create mode 100644 base-codezero/src/base/ipc/pager.cc create mode 100644 base-codezero/src/base/lock/cmpxchg.cc create mode 100644 base-codezero/src/base/lock/lock.cc create mode 100644 base-codezero/src/base/lock/lock_helper.h create mode 100644 base-codezero/src/base/pager/pager.cc create mode 100644 base-codezero/src/base/thread/thread_start.cc create mode 100644 base-codezero/src/core/core_rm_session.cc create mode 100644 base-codezero/src/core/include/core_rm_session.h create mode 100644 base-codezero/src/core/include/irq_session_component.h create mode 100644 base-codezero/src/core/include/map_local.h create mode 100644 base-codezero/src/core/include/platform.h create mode 100644 base-codezero/src/core/include/platform_pd.h create mode 100644 base-codezero/src/core/include/platform_thread.h create mode 100644 base-codezero/src/core/include/util.h create mode 100644 base-codezero/src/core/io_mem_session_support.cc create mode 100644 base-codezero/src/core/io_port_session_component.cc create mode 100644 base-codezero/src/core/irq_session_component.cc create mode 100644 base-codezero/src/core/platform.cc create mode 100644 base-codezero/src/core/platform_pd.cc create mode 100644 base-codezero/src/core/platform_thread.cc create mode 100644 base-codezero/src/core/ram_session_support.cc create mode 100644 base-codezero/src/core/rm_session_support.cc create mode 100644 base-codezero/src/core/target.inc create mode 100644 base-codezero/src/core/target.mk create mode 100644 base-codezero/src/core/thread_start.cc create mode 100644 base-codezero/src/kernel/target.mk create mode 100644 base-codezero/src/platform/_main_helper.h create mode 100644 base-codezero/src/platform/genode.ld create mode 100755 base-codezero/tool/gen_romfs create mode 100644 base-fiasco/Makefile create mode 100644 base-fiasco/README create mode 100644 base-fiasco/config/kernel-config.x86 create mode 100644 base-fiasco/config/l4env-config.x86 create mode 100644 base-fiasco/doc/fiasco.txt create mode 100644 base-fiasco/etc/fiasco.conf create mode 100644 base-fiasco/etc/specs.conf create mode 100644 base-fiasco/etc/tools.conf create mode 100644 base-fiasco/include/arm/cpu/atomic.h create mode 100644 base-fiasco/include/base/cancelable_lock.h create mode 100644 base-fiasco/include/base/ipc_msgbuf.h create mode 100644 base-fiasco/include/base/ipc_pager.h create mode 100644 base-fiasco/include/base/native_types.h create mode 100644 base-fiasco/include/fiasco/thread_helper.h create mode 100644 base-fiasco/lib/mk/arm/startup.mk create mode 100644 base-fiasco/lib/mk/core_printf.mk create mode 100644 base-fiasco/lib/mk/ipc.mk create mode 100644 base-fiasco/lib/mk/l4v2_support.mk create mode 100644 base-fiasco/lib/mk/lock.mk create mode 100644 base-fiasco/lib/mk/pager.mk create mode 100644 base-fiasco/lib/mk/platform.inc create mode 100644 base-fiasco/lib/mk/x86/platform.mk create mode 100644 base-fiasco/lib/mk/x86/startup.mk create mode 100644 base-fiasco/mk/l4_pkg.mk create mode 100644 base-fiasco/mk/spec-fiasco.mk create mode 100644 base-fiasco/mk/spec-fiasco_arm.mk create mode 100644 base-fiasco/mk/spec-fiasco_x86.mk create mode 100644 base-fiasco/mk/spec-platform_imx.mk create mode 100644 base-fiasco/mk/spec-platform_integrator.mk create mode 100644 base-fiasco/mk/spec-platform_mmsp2.mk create mode 100644 base-fiasco/run/env create mode 100644 base-fiasco/src/base/console/core_console.h create mode 100644 base-fiasco/src/base/ipc/ipc.cc create mode 100644 base-fiasco/src/base/ipc/pager.cc create mode 100644 base-fiasco/src/base/lock/lock.cc create mode 100644 base-fiasco/src/base/pager/pager.cc create mode 100644 base-fiasco/src/bootstrap/target.mk create mode 100644 base-fiasco/src/core/arm/platform_arm.cc create mode 100644 base-fiasco/src/core/arm/target.mk create mode 100644 base-fiasco/src/core/include/map_local.h create mode 100644 base-fiasco/src/core/include/platform.h create mode 100644 base-fiasco/src/core/include/platform_pd.h create mode 100644 base-fiasco/src/core/include/platform_thread.h create mode 100644 base-fiasco/src/core/include/util.h create mode 100644 base-fiasco/src/core/io_mem_session_support.cc create mode 100644 base-fiasco/src/core/irq_session_component.cc create mode 100644 base-fiasco/src/core/platform.cc create mode 100644 base-fiasco/src/core/platform_pd.cc create mode 100644 base-fiasco/src/core/platform_thread.cc create mode 100644 base-fiasco/src/core/ram_session_support.cc create mode 100644 base-fiasco/src/core/rm_session_support.cc create mode 100644 base-fiasco/src/core/target.inc create mode 100644 base-fiasco/src/core/thread_start.cc create mode 100644 base-fiasco/src/core/x86/platform_x86.cc create mode 100644 base-fiasco/src/core/x86/target.mk create mode 100644 base-fiasco/src/kernel/target.inc create mode 100644 base-fiasco/src/kernel/x86/target.mk create mode 100644 base-fiasco/src/platform/_main_helper.h create mode 100644 base-fiasco/src/platform/arm/Makefile create mode 100644 base-fiasco/src/platform/arm/_main.cc create mode 100644 base-fiasco/src/platform/arm/crt0.s create mode 100644 base-fiasco/src/sigma0/target.mk create mode 100644 base-foc/Makefile create mode 100644 base-foc/README create mode 100644 base-foc/config/pbxa9.kernel create mode 100644 base-foc/config/rva9.user create mode 100644 base-foc/config/vea9x4.kernel create mode 100644 base-foc/config/x86_32.kernel create mode 100644 base-foc/config/x86_64.kernel create mode 100644 base-foc/doc/foc.txt create mode 100644 base-foc/etc/foc.conf create mode 100644 base-foc/etc/specs.conf create mode 100644 base-foc/include/arm/cpu/atomic.h create mode 100644 base-foc/include/base/cap_sel_alloc.h create mode 100644 base-foc/include/base/ipc.h create mode 100644 base-foc/include/base/ipc_msgbuf.h create mode 100644 base-foc/include/base/ipc_pager.h create mode 100644 base-foc/include/base/native_types.h create mode 100644 base-foc/include/base/thread_state.h create mode 100644 base-foc/include/foc_cpu_session/client.h create mode 100644 base-foc/include/foc_cpu_session/connection.h create mode 100644 base-foc/include/foc_cpu_session/foc_cpu_session.h create mode 100644 base-foc/include/foc_pd_session/client.h create mode 100644 base-foc/include/foc_pd_session/connection.h create mode 100644 base-foc/include/foc_pd_session/foc_pd_session.h create mode 100644 base-foc/include/signal_session/foc_source.h create mode 100644 base-foc/include/signal_session/source_client.h create mode 100644 base-foc/include/signal_session/source_rpc_object.h create mode 100644 base-foc/lib/mk/arm/ipc.mk create mode 100644 base-foc/lib/mk/arm/platform.inc create mode 100644 base-foc/lib/mk/arm/startup.mk create mode 100644 base-foc/lib/mk/arm/syscalls.mk create mode 100644 base-foc/lib/mk/cap_alloc.mk create mode 100644 base-foc/lib/mk/core_printf.mk create mode 100644 base-foc/lib/mk/env.mk create mode 100644 base-foc/lib/mk/ipc.inc create mode 100644 base-foc/lib/mk/l4re_support.mk create mode 100644 base-foc/lib/mk/lock.mk create mode 100644 base-foc/lib/mk/pager.mk create mode 100644 base-foc/lib/mk/platform.inc create mode 100644 base-foc/lib/mk/platform_pbxa9/platform.mk create mode 100644 base-foc/lib/mk/platform_vea9x4/platform.mk create mode 100644 base-foc/lib/mk/raw_server.mk create mode 100644 base-foc/lib/mk/server.mk create mode 100644 base-foc/lib/mk/thread.mk create mode 100644 base-foc/lib/mk/x86/syscalls.mk create mode 100644 base-foc/lib/mk/x86_32/ipc.mk create mode 100644 base-foc/lib/mk/x86_32/platform.mk create mode 100644 base-foc/lib/mk/x86_32/startup.mk create mode 100644 base-foc/lib/mk/x86_64/ipc.mk create mode 100644 base-foc/lib/mk/x86_64/platform.mk create mode 100644 base-foc/lib/mk/x86_64/startup.mk create mode 100644 base-foc/mk/l4_pkg.mk create mode 100644 base-foc/mk/spec-foc.mk create mode 100644 base-foc/mk/spec-foc_arm.mk create mode 100644 base-foc/mk/spec-foc_pbxa9.mk create mode 100644 base-foc/mk/spec-foc_vea9x4.mk create mode 100644 base-foc/mk/spec-foc_x86_32.mk create mode 100644 base-foc/mk/spec-foc_x86_64.mk create mode 100644 base-foc/patches/README create mode 100644 base-foc/patches/crtn_arm_binutils_2.21.1.patch create mode 100644 base-foc/patches/fix_exception_ip.patch create mode 100644 base-foc/patches/foc_single_step_x86.patch create mode 100644 base-foc/patches/timer_arm.patch create mode 100644 base-foc/patches/vexpress_detection.patch create mode 100644 base-foc/run/env create mode 100644 base-foc/src/base/console/core_console.h create mode 100644 base-foc/src/base/env/cap_sel_alloc.cc create mode 100644 base-foc/src/base/ipc/arm/pager.cc create mode 100644 base-foc/src/base/ipc/arm/pager_exception.cc create mode 100644 base-foc/src/base/ipc/ipc.cc create mode 100644 base-foc/src/base/ipc/pager.cc create mode 100644 base-foc/src/base/ipc/x86/pager_exception.cc create mode 100644 base-foc/src/base/ipc/x86_32/pager.cc create mode 100644 base-foc/src/base/ipc/x86_64/pager.cc create mode 100644 base-foc/src/base/lock/lock_helper.h create mode 100644 base-foc/src/base/pager/pager.cc create mode 100644 base-foc/src/base/server/server.cc create mode 100644 base-foc/src/base/thread/thread.cc create mode 100644 base-foc/src/base/thread/thread_bootstrap.cc create mode 100644 base-foc/src/base/thread/thread_start.cc create mode 100644 base-foc/src/bootstrap/target.mk create mode 100644 base-foc/src/core/arm/platform_arm.cc create mode 100644 base-foc/src/core/arm/platform_thread.cc create mode 100644 base-foc/src/core/arm/target.mk create mode 100644 base-foc/src/core/cap_session_component.cc create mode 100644 base-foc/src/core/cpu_session_extension.cc create mode 100644 base-foc/src/core/include/cap_session_component.h create mode 100644 base-foc/src/core/include/cpu_session_component.h create mode 100644 base-foc/src/core/include/irq_session_component.h create mode 100644 base-foc/src/core/include/map_local.h create mode 100644 base-foc/src/core/include/pd_session_component.h create mode 100644 base-foc/src/core/include/platform.h create mode 100644 base-foc/src/core/include/platform_pd.h create mode 100644 base-foc/src/core/include/platform_thread.h create mode 100644 base-foc/src/core/include/util.h create mode 100644 base-foc/src/core/io_mem_session_support.cc create mode 100644 base-foc/src/core/irq_session_component.cc create mode 100644 base-foc/src/core/pd_session_extension.cc create mode 100644 base-foc/src/core/platform.cc create mode 100644 base-foc/src/core/platform_pd.cc create mode 100644 base-foc/src/core/platform_thread.cc create mode 100644 base-foc/src/core/ram_session_support.cc create mode 100644 base-foc/src/core/rm_session_support.cc create mode 100644 base-foc/src/core/signal_source_component.cc create mode 100644 base-foc/src/core/target.inc create mode 100644 base-foc/src/core/thread_start.cc create mode 100644 base-foc/src/core/x86/platform_thread.cc create mode 100644 base-foc/src/core/x86/platform_x86.cc create mode 100644 base-foc/src/core/x86/target.mk create mode 100644 base-foc/src/kernel/pbxa9/target.mk create mode 100644 base-foc/src/kernel/target.inc create mode 100644 base-foc/src/kernel/vea9x4/target.mk create mode 100644 base-foc/src/kernel/x86_32/target.mk create mode 100644 base-foc/src/kernel/x86_64/target.mk create mode 100644 base-foc/src/platform/_main_helper.h create mode 100644 base-foc/src/platform/_main_parent_cap.h create mode 100644 base-foc/src/sigma0/target.mk create mode 100644 base-host/README create mode 100644 base-host/etc/specs.conf create mode 100644 base-host/etc/tools.conf create mode 100644 base-host/include/base/ipc_msgbuf.h create mode 100644 base-host/include/base/ipc_pager.h create mode 100644 base-host/include/base/native_types.h create mode 100644 base-host/lib/mk/core_printf.mk create mode 100644 base-host/lib/mk/env.mk create mode 100644 base-host/lib/mk/ipc.mk create mode 100644 base-host/lib/mk/lock.mk create mode 100644 base-host/lib/mk/pager.mk create mode 100644 base-host/lib/mk/printf_stdio.mk create mode 100644 base-host/src/base/env/parent.cc create mode 100644 base-host/src/base/ipc/ipc.cc create mode 100644 base-host/src/base/lock/lock_helper.h create mode 100644 base-host/src/base/pager/pager.cc create mode 100644 base-host/src/core/context_area.cc create mode 100644 base-host/src/core/core_rm_session.cc create mode 100644 base-host/src/core/include/core_rm_session.h create mode 100644 base-host/src/core/include/platform.h create mode 100644 base-host/src/core/include/platform_pd.h create mode 100644 base-host/src/core/include/platform_thread.h create mode 100644 base-host/src/core/include/util.h create mode 100644 base-host/src/core/io_mem_session_support.cc create mode 100644 base-host/src/core/io_port_session_component.cc create mode 100644 base-host/src/core/irq_session_component.cc create mode 100644 base-host/src/core/platform.cc create mode 100644 base-host/src/core/platform_pd.cc create mode 100644 base-host/src/core/platform_thread.cc create mode 100644 base-host/src/core/ram_session_support.cc create mode 100644 base-host/src/core/rm_session_support.cc create mode 100644 base-host/src/core/target.inc create mode 100644 base-host/src/core/target.mk create mode 100644 base-host/src/core/thread_host.cc create mode 100644 base-host/src/lib/printf_stdio/printf_stdio.cc create mode 100644 base-linux/README create mode 100644 base-linux/etc/specs.conf create mode 100644 base-linux/include/base/ipc_msgbuf.h create mode 100644 base-linux/include/base/local_interface.h create mode 100644 base-linux/include/base/native_types.h create mode 100644 base-linux/include/base/pager.h create mode 100644 base-linux/include/base/platform_env.h create mode 100644 base-linux/include/linux_dataspace/client.h create mode 100644 base-linux/include/linux_dataspace/linux_dataspace.h create mode 100644 base-linux/include/rm_session/client.h create mode 100644 base-linux/lib/import/import-lx_hybrid.mk create mode 100644 base-linux/lib/import/import-syscall.mk create mode 100644 base-linux/lib/mk/core_printf.mk create mode 100644 base-linux/lib/mk/env.mk create mode 100644 base-linux/lib/mk/ipc.mk create mode 100644 base-linux/lib/mk/lock.mk create mode 100644 base-linux/lib/mk/lx_hybrid.mk create mode 100644 base-linux/lib/mk/process.mk create mode 100644 base-linux/lib/mk/rpath.mk create mode 100644 base-linux/lib/mk/thread.mk create mode 100644 base-linux/lib/mk/x86_32/startup.mk create mode 100644 base-linux/lib/mk/x86_32/syscall.mk create mode 100644 base-linux/lib/mk/x86_64/startup.mk create mode 100644 base-linux/lib/mk/x86_64/syscall.mk create mode 100644 base-linux/mk/spec-linux.mk create mode 100644 base-linux/mk/spec-linux_x86_32.mk create mode 100644 base-linux/mk/spec-linux_x86_64.mk create mode 100644 base-linux/run/env create mode 100644 base-linux/run/lx_hybrid_ctors.run create mode 100644 base-linux/run/lx_hybrid_exception.run create mode 100644 base-linux/src/base/console/core_console.h create mode 100644 base-linux/src/base/env/debug.cc create mode 100644 base-linux/src/base/env/platform_env.cc create mode 100644 base-linux/src/base/env/rm_session_mmap.cc create mode 100644 base-linux/src/base/ipc/ipc.cc create mode 100644 base-linux/src/base/lock/lock_helper.h create mode 100644 base-linux/src/base/process/process.cc create mode 100644 base-linux/src/base/thread/thread_linux.cc create mode 100644 base-linux/src/core/context_area.cc create mode 100644 base-linux/src/core/include/cap_session_component.h create mode 100644 base-linux/src/core/include/dataspace_component.h create mode 100644 base-linux/src/core/include/io_mem_session_component.h create mode 100644 base-linux/src/core/include/irq_session_component.h create mode 100644 base-linux/src/core/include/pd_session_component.h create mode 100644 base-linux/src/core/include/platform.h create mode 100644 base-linux/src/core/include/platform_pd.h create mode 100644 base-linux/src/core/include/platform_thread.h create mode 100644 base-linux/src/core/include/rm_session_component.h create mode 100644 base-linux/src/core/io_mem_session_component.cc create mode 100644 base-linux/src/core/io_port_session_component.cc create mode 100644 base-linux/src/core/pd_session_component.cc create mode 100644 base-linux/src/core/platform.cc create mode 100644 base-linux/src/core/platform_thread.cc create mode 100644 base-linux/src/core/ram_session_support.cc create mode 100644 base-linux/src/core/rom_session_component.cc create mode 100644 base-linux/src/core/target.mk create mode 100644 base-linux/src/core/thread_linux.cc create mode 100644 base-linux/src/platform/_main_helper.h create mode 100644 base-linux/src/platform/context_area.nostdlib.ld create mode 100644 base-linux/src/platform/context_area.stdlib.ld create mode 100644 base-linux/src/platform/linux_rpath.cc create mode 100644 base-linux/src/platform/linux_rpath.h create mode 100644 base-linux/src/platform/linux_syscalls.h create mode 100644 base-linux/src/platform/lx_hybrid.cc create mode 100644 base-linux/src/platform/x86_32/crt0.s create mode 100644 base-linux/src/platform/x86_32/lx_clone.S create mode 100644 base-linux/src/platform/x86_32/lx_syscall.S create mode 100644 base-linux/src/platform/x86_64/crt0.s create mode 100644 base-linux/src/platform/x86_64/lx_clone.S create mode 100644 base-linux/src/platform/x86_64/lx_restore_rt.S create mode 100644 base-linux/src/platform/x86_64/lx_syscall.S create mode 100644 base-linux/src/test/lx_hybrid_ctors/main.cc create mode 100644 base-linux/src/test/lx_hybrid_ctors/target.mk create mode 100644 base-linux/src/test/lx_hybrid_ctors/testlib.cc create mode 100644 base-linux/src/test/lx_hybrid_exception/main.cc create mode 100644 base-linux/src/test/lx_hybrid_exception/target.mk create mode 100644 base-linux/src/test/sub_rm/config.h create mode 100644 base-mb/README create mode 100644 base-mb/doc/getting_started.txt create mode 100644 base-mb/doc/microblaze.txt create mode 100755 base-mb/etc/specs.conf create mode 100755 base-mb/etc/tools.conf create mode 100755 base-mb/include/base/ipc_msgbuf.h create mode 100755 base-mb/include/base/ipc_pager.h create mode 100755 base-mb/include/base/native_types.h create mode 100755 base-mb/include/cpu/atomic.h create mode 100755 base-mb/include/cpu/config.h create mode 100755 base-mb/include/kernel/config.h create mode 100755 base-mb/include/kernel/syscalls.h create mode 100755 base-mb/include/kernel/types.h create mode 100644 base-mb/include/xilinx/xps_intc.h create mode 100644 base-mb/include/xilinx/xps_timer.h create mode 100644 base-mb/include/xilinx/xps_uartl.h create mode 100755 base-mb/lib/mk/cxx.mk create mode 100755 base-mb/lib/mk/ipc.mk create mode 100755 base-mb/lib/mk/kernel.inc create mode 100755 base-mb/lib/mk/kernel_core.mk create mode 100755 base-mb/lib/mk/kernel_test.inc create mode 100755 base-mb/lib/mk/lock.mk create mode 100755 base-mb/lib/mk/pager.mk create mode 100644 base-mb/lib/mk/petalogix_s3adsp1800_mmu__atomic_operations.mk create mode 100755 base-mb/lib/mk/petalogix_s3adsp1800_mmu__kernel_support.inc create mode 100755 base-mb/lib/mk/printf_microblaze.mk create mode 100755 base-mb/lib/mk/startup.mk create mode 100755 base-mb/lib/mk/test_env.mk create mode 100644 base-mb/lib/mk/thread.mk create mode 100755 base-mb/lib/mk/thread_context.mk create mode 100644 base-mb/mk/spec-mb_ml507.mk create mode 100644 base-mb/mk/spec-mb_s3a_starter_kit.mk create mode 100644 base-mb/platform/mb_s3a_starter_kit/Makefile create mode 100644 base-mb/platform/mb_s3a_starter_kit/system.bit create mode 100644 base-mb/platform/mk/microblaze.mk create mode 100644 base-mb/platform/mk/ml507.mk create mode 100644 base-mb/platform/mk/s3a_starter_kit.mk create mode 100644 base-mb/platform/mk/xilinx.mk create mode 100755 base-mb/run/env create mode 100755 base-mb/run/hello.run create mode 100755 base-mb/run/nested_init.run create mode 100755 base-mb/src/base/console/microblaze_console.cc create mode 100755 base-mb/src/base/cxx/atexit.cc create mode 100755 base-mb/src/base/ipc/ipc.cc create mode 100755 base-mb/src/base/ipc/pager.cc create mode 100755 base-mb/src/base/lock/lock_helper.h create mode 100644 base-mb/src/base/pager/pager.cc create mode 100644 base-mb/src/base/thread/thread.cc create mode 100755 base-mb/src/base/thread/thread_bootstrap.cc create mode 100755 base-mb/src/base/thread/thread_context.cc create mode 100644 base-mb/src/base/thread/thread_start.cc create mode 100644 base-mb/src/core/context_area.cc create mode 100644 base-mb/src/core/core_rm_session.cc create mode 100644 base-mb/src/core/include/core_rm_session.h create mode 100644 base-mb/src/core/include/cpu/prints.h create mode 100644 base-mb/src/core/include/irq_session_component.h create mode 100644 base-mb/src/core/include/kernel/print.h create mode 100755 base-mb/src/core/include/map_local.h create mode 100755 base-mb/src/core/include/platform.h create mode 100755 base-mb/src/core/include/platform_pd.h create mode 100755 base-mb/src/core/include/platform_thread.h create mode 100755 base-mb/src/core/include/util.h create mode 100644 base-mb/src/core/include/util/array.h create mode 100644 base-mb/src/core/include/util/debug.h create mode 100644 base-mb/src/core/include/util/id_allocator.h create mode 100644 base-mb/src/core/include/util/math.h create mode 100644 base-mb/src/core/include/util/queue.h create mode 100644 base-mb/src/core/include/xilinx/microblaze.h create mode 100755 base-mb/src/core/io_mem_session_support.cc create mode 100755 base-mb/src/core/io_port_session_component.cc create mode 100644 base-mb/src/core/irq_session_component.cc create mode 100644 base-mb/src/core/platform.cc create mode 100755 base-mb/src/core/platform_thread.cc create mode 100755 base-mb/src/core/ram_session_support.cc create mode 100755 base-mb/src/core/rm_session_support.cc create mode 100755 base-mb/src/core/target.inc create mode 100755 base-mb/src/core/target.mk create mode 100755 base-mb/src/core/thread_roottask.cc create mode 100755 base-mb/src/kernel/generic/blocking.cc create mode 100755 base-mb/src/kernel/generic/include/exception.h create mode 100755 base-mb/src/kernel/generic/include/interrupt.h create mode 100755 base-mb/src/kernel/generic/include/thread.h create mode 100755 base-mb/src/kernel/generic/kernel.cc create mode 100755 base-mb/src/kernel/generic/scheduler.cc create mode 100755 base-mb/src/kernel/generic/syscall_events.cc create mode 100755 base-mb/src/kernel/generic/thread.cc create mode 100755 base-mb/src/kernel/include/generic/blocking.h create mode 100755 base-mb/src/kernel/include/generic/event.h create mode 100755 base-mb/src/kernel/include/generic/ipc.h create mode 100755 base-mb/src/kernel/include/generic/irq_controller.h create mode 100755 base-mb/src/kernel/include/generic/printf.h create mode 100755 base-mb/src/kernel/include/generic/scheduler.h create mode 100755 base-mb/src/kernel/include/generic/syscall_events.h create mode 100755 base-mb/src/kernel/include/generic/timer.h create mode 100755 base-mb/src/kernel/include/generic/tlb.h create mode 100755 base-mb/src/kernel/include/generic/verbose.h create mode 100755 base-mb/src/kernel/include/petalogix_s3adsp1800_mmu/platform/platform.h create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/atomic.s create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0.s create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0_kernel.s create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/errors.s create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/exec_context.s create mode 100644 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/linker_commands.s create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/special_registers.s create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/kernel_entry.s create mode 100644 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/platform.cc create mode 100755 base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/userland_entry.s create mode 100644 base-mb/src/platform/_main_helper.h create mode 100755 base-mb/src/platform/genode.ld create mode 100644 base-mb/src/test/hello/main.cc create mode 100644 base-mb/src/test/hello/target.mk create mode 100644 base-nova/Makefile create mode 100644 base-nova/README create mode 100644 base-nova/doc/nova.txt create mode 100644 base-nova/etc/specs.conf create mode 100644 base-nova/include/base/cap_sel_alloc.h create mode 100644 base-nova/include/base/ipc.h create mode 100644 base-nova/include/base/ipc_msgbuf.h create mode 100644 base-nova/include/base/ipc_pager.h create mode 100644 base-nova/include/base/native_types.h create mode 100644 base-nova/include/base/pager.h create mode 100644 base-nova/include/base/sleep.h create mode 100644 base-nova/include/nova/stdint.h create mode 100644 base-nova/include/nova/syscalls.h create mode 100644 base-nova/include/signal_session/nova_source.h create mode 100644 base-nova/include/signal_session/source_client.h create mode 100644 base-nova/include/signal_session/source_rpc_object.h create mode 100644 base-nova/lib/mk/core_printf.mk create mode 100644 base-nova/lib/mk/env.mk create mode 100644 base-nova/lib/mk/ipc.mk create mode 100644 base-nova/lib/mk/lock.mk create mode 100644 base-nova/lib/mk/pager.mk create mode 100644 base-nova/lib/mk/printf_stdio.mk create mode 100644 base-nova/lib/mk/raw_server.mk create mode 100644 base-nova/lib/mk/server.mk create mode 100644 base-nova/lib/mk/test_env.mk create mode 100644 base-nova/lib/mk/thread.mk create mode 100644 base-nova/lib/mk/thread_context.mk create mode 100644 base-nova/lib/mk/x86_32/startup.mk create mode 100644 base-nova/mk/spec-nova.mk create mode 100644 base-nova/patches/README create mode 100644 base-nova/patches/utcb.patch create mode 100644 base-nova/run/env create mode 100644 base-nova/src/base/console/core_console.h create mode 100644 base-nova/src/base/env/cap_sel_alloc.cc create mode 100644 base-nova/src/base/env/main_thread.cc create mode 100644 base-nova/src/base/ipc/ipc.cc create mode 100644 base-nova/src/base/ipc/pager.cc create mode 100644 base-nova/src/base/lock/lock_helper.h create mode 100644 base-nova/src/base/pager/pager.cc create mode 100644 base-nova/src/base/server/server.cc create mode 100644 base-nova/src/base/thread/thread_context.cc create mode 100644 base-nova/src/base/thread/thread_nova.cc create mode 100644 base-nova/src/core/core_rm_session.cc create mode 100644 base-nova/src/core/echo.cc create mode 100644 base-nova/src/core/include/cap_session_component.h create mode 100644 base-nova/src/core/include/core_rm_session.h create mode 100644 base-nova/src/core/include/echo.h create mode 100644 base-nova/src/core/include/irq_session_component.h create mode 100644 base-nova/src/core/include/map_local.h create mode 100644 base-nova/src/core/include/nova_util.h create mode 100644 base-nova/src/core/include/platform.h create mode 100644 base-nova/src/core/include/platform_pd.h create mode 100644 base-nova/src/core/include/platform_thread.h create mode 100644 base-nova/src/core/include/util.h create mode 100644 base-nova/src/core/io_mem_session_support.cc create mode 100644 base-nova/src/core/irq_session_component.cc create mode 100644 base-nova/src/core/platform.cc create mode 100644 base-nova/src/core/platform_pd.cc create mode 100644 base-nova/src/core/platform_thread.cc create mode 100644 base-nova/src/core/ram_session_support.cc create mode 100644 base-nova/src/core/rm_session_support.cc create mode 100644 base-nova/src/core/signal_source_component.cc create mode 100644 base-nova/src/core/target.inc create mode 100644 base-nova/src/core/target.mk create mode 100644 base-nova/src/core/thread_start.cc create mode 100644 base-nova/src/kernel/target.mk create mode 100644 base-nova/src/lib/printf_stdio/printf_stdio.cc create mode 100644 base-nova/src/platform/_main_helper.h create mode 100644 base-nova/src/platform/_main_parent_cap.h create mode 100644 base-nova/src/platform/roottask.ld create mode 100644 base-okl4/Makefile create mode 100644 base-okl4/README create mode 100644 base-okl4/contrib/generated/README create mode 100644 base-okl4/contrib/generated/x86/asmsyms.h create mode 100644 base-okl4/contrib/generated/x86/kdb_class_helper.h create mode 100644 base-okl4/contrib/generated/x86/ktcb_layout.h create mode 100644 base-okl4/contrib/generated/x86/linker.ld create mode 100644 base-okl4/contrib/generated/x86/macro_sets.cc create mode 100644 base-okl4/contrib/generated/x86/tcb_layout.h create mode 100644 base-okl4/doc/notes.txt create mode 100644 base-okl4/doc/okl4.txt create mode 100644 base-okl4/etc/specs.conf create mode 100644 base-okl4/include/base/ipc_msgbuf.h create mode 100644 base-okl4/include/base/ipc_pager.h create mode 100644 base-okl4/include/base/native_types.h create mode 100644 base-okl4/include/base/thread_state.h create mode 100644 base-okl4/include/okl4_pd_session/client.h create mode 100644 base-okl4/include/okl4_pd_session/connection.h create mode 100644 base-okl4/include/okl4_pd_session/okl4_pd_session.h create mode 100644 base-okl4/lib/mk/bootinfo.mk create mode 100644 base-okl4/lib/mk/core_printf.mk create mode 100644 base-okl4/lib/mk/ipc.mk create mode 100644 base-okl4/lib/mk/kernel.inc create mode 100644 base-okl4/lib/mk/lock.mk create mode 100644 base-okl4/lib/mk/pager.mk create mode 100644 base-okl4/lib/mk/platform.inc create mode 100644 base-okl4/lib/mk/thread.mk create mode 100644 base-okl4/lib/mk/x86/kernel.mk create mode 100644 base-okl4/lib/mk/x86/platform.mk create mode 100644 base-okl4/lib/mk/x86/startup.mk create mode 100644 base-okl4/mk/spec-okl4.mk create mode 100644 base-okl4/mk/spec-okl4_x86.mk create mode 100644 base-okl4/patches/README create mode 100644 base-okl4/patches/char_bit.patch create mode 100644 base-okl4/patches/eabi_build.patch create mode 100644 base-okl4/patches/elfweaver.patch create mode 100644 base-okl4/patches/gcc_4.4.5.patch create mode 100644 base-okl4/patches/gdt_init.patch create mode 100644 base-okl4/patches/kdb_reboot.patch create mode 100644 base-okl4/patches/reply_tid.patch create mode 100644 base-okl4/patches/suspend_resume.patch create mode 100644 base-okl4/patches/syscall_pic.patch create mode 100644 base-okl4/run/env create mode 100644 base-okl4/run/priority.run create mode 100644 base-okl4/src/base/bootinfo/README create mode 100644 base-okl4/src/base/bootinfo/stdint.h create mode 100644 base-okl4/src/base/bootinfo/stdio.h create mode 100644 base-okl4/src/base/console/core_console.h create mode 100644 base-okl4/src/base/ipc/ipc.cc create mode 100644 base-okl4/src/base/ipc/pager.cc create mode 100644 base-okl4/src/base/lock/lock_helper.h create mode 100644 base-okl4/src/base/pager/pager.cc create mode 100644 base-okl4/src/base/thread/thread_bootstrap.cc create mode 100644 base-okl4/src/core/core_rm_session.cc create mode 100644 base-okl4/src/core/include/core_rm_session.h create mode 100644 base-okl4/src/core/include/map_local.h create mode 100644 base-okl4/src/core/include/pd_session_component.h create mode 100644 base-okl4/src/core/include/platform.h create mode 100644 base-okl4/src/core/include/platform_pd.h create mode 100644 base-okl4/src/core/include/platform_thread.h create mode 100644 base-okl4/src/core/include/stdint.h create mode 100644 base-okl4/src/core/include/util.h create mode 100644 base-okl4/src/core/io_mem_session_support.cc create mode 100644 base-okl4/src/core/irq_session_component.cc create mode 100644 base-okl4/src/core/okl4_pd_session_component.cc create mode 100644 base-okl4/src/core/platform.cc create mode 100644 base-okl4/src/core/platform_pd.cc create mode 100644 base-okl4/src/core/platform_thread.cc create mode 100644 base-okl4/src/core/ram_session_support.cc create mode 100644 base-okl4/src/core/rm_session_support.cc create mode 100644 base-okl4/src/core/target.inc create mode 100644 base-okl4/src/core/thread_start.cc create mode 100644 base-okl4/src/core/x86/platform_thread_x86.cc create mode 100644 base-okl4/src/core/x86/target.mk create mode 100644 base-okl4/src/kernel/target.inc create mode 100644 base-okl4/src/kernel/x86/target.mk create mode 100644 base-okl4/src/platform/_main_helper.h create mode 100644 base-okl4/src/test/create_thread.h create mode 100644 base-okl4/src/test/io_port.h create mode 100644 base-okl4/src/test/mini_env.h create mode 100644 base-okl4/src/test/okl4_01_hello_raw/Makefile create mode 100644 base-okl4/src/test/okl4_01_hello_raw/crt0.s create mode 100644 base-okl4/src/test/okl4_01_hello_raw/genode.ld create mode 100644 base-okl4/src/test/okl4_01_hello_raw/hello.cc create mode 100644 base-okl4/src/test/okl4_01_hello_raw/weaver.xml create mode 100644 base-okl4/src/test/okl4_02_hello/hello.cc create mode 100644 base-okl4/src/test/okl4_02_hello/target.mk create mode 100644 base-okl4/src/test/okl4_03_thread/main.cc create mode 100644 base-okl4/src/test/okl4_03_thread/target.mk create mode 100644 base-okl4/src/test/okl4_04_ipc_send_wait/main.cc create mode 100644 base-okl4/src/test/okl4_04_ipc_send_wait/target.mk create mode 100644 base-okl4/src/test/okl4_05_ipc_call/main.cc create mode 100644 base-okl4/src/test/okl4_05_ipc_call/target.mk create mode 100644 base-okl4/src/test/okl4_06_pager/main.cc create mode 100644 base-okl4/src/test/okl4_06_pager/target.mk create mode 100644 base-okl4/src/test/okl4_07_boot_info/main.cc create mode 100644 base-okl4/src/test/okl4_07_boot_info/stdint.h create mode 100644 base-okl4/src/test/okl4_07_boot_info/target.mk create mode 100644 base-okl4/src/test/okl4_08_timer_pit/main.cc create mode 100644 base-okl4/src/test/okl4_08_timer_pit/target.mk create mode 100644 base-okl4/tool/README create mode 100644 base-okl4/tool/weaver_x86.xml create mode 100644 base-pistachio/Makefile create mode 100644 base-pistachio/README create mode 100644 base-pistachio/config/kernel create mode 100644 base-pistachio/doc/pistachio.txt create mode 100644 base-pistachio/etc/specs.conf create mode 100644 base-pistachio/include/base/clock.h create mode 100644 base-pistachio/include/base/ipc_msgbuf.h create mode 100644 base-pistachio/include/base/ipc_pager.h create mode 100644 base-pistachio/include/base/native_types.h create mode 100644 base-pistachio/include/pistachio/kip.h create mode 100644 base-pistachio/include/pistachio/thread_helper.h create mode 100644 base-pistachio/include/util/hexdump.h create mode 100644 base-pistachio/include/x86/cpu/rdtsc.h create mode 100644 base-pistachio/include/x86/util/smath.h create mode 100644 base-pistachio/lib/mk/core_printf.mk create mode 100644 base-pistachio/lib/mk/hexdump.mk create mode 100644 base-pistachio/lib/mk/ipc.mk create mode 100644 base-pistachio/lib/mk/kip.mk create mode 100644 base-pistachio/lib/mk/l4.mk create mode 100644 base-pistachio/lib/mk/lock.mk create mode 100644 base-pistachio/lib/mk/pager.mk create mode 100644 base-pistachio/lib/mk/platform.mk create mode 100644 base-pistachio/lib/mk/x86/startup.mk create mode 100644 base-pistachio/mk/spec-pistachio.mk create mode 100644 base-pistachio/mk/spec-pistachio_x86.mk create mode 100644 base-pistachio/patches/README create mode 100644 base-pistachio/patches/syscalls_ia32.patch create mode 100644 base-pistachio/run/env create mode 100644 base-pistachio/src/base/console/core_console.h create mode 100644 base-pistachio/src/base/ipc/ipc.cc create mode 100644 base-pistachio/src/base/ipc/pager.cc create mode 100644 base-pistachio/src/base/kip/kip.cc create mode 100644 base-pistachio/src/base/lock/lock_helper.h create mode 100644 base-pistachio/src/base/pager/pager.cc create mode 100644 base-pistachio/src/core/cpu_session_platform.cc create mode 100644 base-pistachio/src/core/include/map_local.h create mode 100644 base-pistachio/src/core/include/platform.h create mode 100644 base-pistachio/src/core/include/platform_pd.h create mode 100644 base-pistachio/src/core/include/platform_thread.h create mode 100644 base-pistachio/src/core/include/util.h create mode 100644 base-pistachio/src/core/io_mem_session_support.cc create mode 100644 base-pistachio/src/core/irq_session_component.cc create mode 100644 base-pistachio/src/core/multiboot_info.cc create mode 100644 base-pistachio/src/core/platform.cc create mode 100644 base-pistachio/src/core/platform_pd.cc create mode 100644 base-pistachio/src/core/platform_thread.cc create mode 100644 base-pistachio/src/core/ram_session_support.cc create mode 100644 base-pistachio/src/core/rm_session_support.cc create mode 100644 base-pistachio/src/core/target.inc create mode 100644 base-pistachio/src/core/thread_start.cc create mode 100644 base-pistachio/src/core/x86/platform_x86.cc create mode 100644 base-pistachio/src/core/x86/target.mk create mode 100644 base-pistachio/src/kernel/target.mk create mode 100644 base-pistachio/src/platform/_main_helper.h create mode 100644 base-pistachio/src/util/hexdump/hexdump.cc create mode 100644 base/README create mode 100644 base/etc/README create mode 100644 base/etc/tools.conf create mode 100644 base/include/32bit/base/fixed_stdint.h create mode 100644 base/include/64bit/base/fixed_stdint.h create mode 100644 base/include/README create mode 100644 base/include/arm/cpu/cpu_state.h create mode 100644 base/include/base/allocator.h create mode 100644 base/include/base/allocator_avl.h create mode 100644 base/include/base/allocator_guard.h create mode 100644 base/include/base/blocking.h create mode 100644 base/include/base/cancelable_lock.h create mode 100644 base/include/base/capability.h create mode 100644 base/include/base/child.h create mode 100644 base/include/base/connection.h create mode 100644 base/include/base/console.h create mode 100644 base/include/base/cpu_state.h create mode 100644 base/include/base/crt0.h create mode 100644 base/include/base/elf.h create mode 100644 base/include/base/env.h create mode 100644 base/include/base/errno.h create mode 100644 base/include/base/exception.h create mode 100644 base/include/base/heap.h create mode 100644 base/include/base/ipc.h create mode 100644 base/include/base/ipc_generic.h create mode 100644 base/include/base/lock.h create mode 100644 base/include/base/lock_guard.h create mode 100644 base/include/base/object_pool.h create mode 100644 base/include/base/pager.h create mode 100644 base/include/base/platform_env.h create mode 100644 base/include/base/printf.h create mode 100644 base/include/base/process.h create mode 100644 base/include/base/rpc.h create mode 100644 base/include/base/rpc_args.h create mode 100644 base/include/base/rpc_client.h create mode 100644 base/include/base/rpc_server.h create mode 100644 base/include/base/semaphore.h create mode 100644 base/include/base/service.h create mode 100644 base/include/base/signal.h create mode 100644 base/include/base/slab.h create mode 100644 base/include/base/sleep.h create mode 100644 base/include/base/snprintf.h create mode 100644 base/include/base/stdint.h create mode 100644 base/include/base/sync_allocator.h create mode 100644 base/include/base/thread.h create mode 100644 base/include/base/thread_state.h create mode 100644 base/include/base/tslab.h create mode 100644 base/include/cap_session/cap_session.h create mode 100644 base/include/cap_session/capability.h create mode 100644 base/include/cap_session/client.h create mode 100644 base/include/cap_session/connection.h create mode 100644 base/include/cpu_session/capability.h create mode 100644 base/include/cpu_session/client.h create mode 100644 base/include/cpu_session/connection.h create mode 100644 base/include/cpu_session/cpu_session.h create mode 100644 base/include/dataspace/capability.h create mode 100644 base/include/dataspace/client.h create mode 100644 base/include/dataspace/dataspace.h create mode 100644 base/include/io_mem_session/capability.h create mode 100644 base/include/io_mem_session/client.h create mode 100644 base/include/io_mem_session/connection.h create mode 100644 base/include/io_mem_session/io_mem_session.h create mode 100644 base/include/io_port_session/capability.h create mode 100644 base/include/io_port_session/client.h create mode 100644 base/include/io_port_session/connection.h create mode 100644 base/include/io_port_session/io_port_session.h create mode 100644 base/include/irq_session/capability.h create mode 100644 base/include/irq_session/client.h create mode 100644 base/include/irq_session/connection.h create mode 100644 base/include/irq_session/irq_session.h create mode 100644 base/include/log_session/capability.h create mode 100644 base/include/log_session/client.h create mode 100644 base/include/log_session/connection.h create mode 100644 base/include/log_session/log_session.h create mode 100644 base/include/pager/capability.h create mode 100644 base/include/parent/capability.h create mode 100644 base/include/parent/client.h create mode 100644 base/include/parent/parent.h create mode 100644 base/include/pd_session/capability.h create mode 100644 base/include/pd_session/client.h create mode 100644 base/include/pd_session/connection.h create mode 100644 base/include/pd_session/pd_session.h create mode 100644 base/include/ram_session/capability.h create mode 100644 base/include/ram_session/client.h create mode 100644 base/include/ram_session/connection.h create mode 100644 base/include/ram_session/ram_session.h create mode 100644 base/include/rm_session/capability.h create mode 100644 base/include/rm_session/client.h create mode 100644 base/include/rm_session/connection.h create mode 100644 base/include/rm_session/rm_session.h create mode 100644 base/include/rom_session/capability.h create mode 100644 base/include/rom_session/client.h create mode 100644 base/include/rom_session/connection.h create mode 100644 base/include/rom_session/rom_session.h create mode 100644 base/include/root/capability.h create mode 100644 base/include/root/client.h create mode 100644 base/include/root/component.h create mode 100644 base/include/root/root.h create mode 100644 base/include/session/capability.h create mode 100644 base/include/session/session.h create mode 100644 base/include/signal_session/capability.h create mode 100644 base/include/signal_session/client.h create mode 100644 base/include/signal_session/connection.h create mode 100644 base/include/signal_session/signal_session.h create mode 100644 base/include/signal_session/source.h create mode 100644 base/include/signal_session/source_client.h create mode 100644 base/include/signal_session/source_rpc_object.h create mode 100644 base/include/thread/capability.h create mode 100644 base/include/util/arg_string.h create mode 100644 base/include/util/avl_string.h create mode 100644 base/include/util/avl_tree.h create mode 100644 base/include/util/fifo.h create mode 100644 base/include/util/list.h create mode 100644 base/include/util/meta.h create mode 100644 base/include/util/misc_math.h create mode 100644 base/include/util/string.h create mode 100644 base/include/util/token.h create mode 100644 base/include/util/touch.h create mode 100644 base/include/x86/cpu/atomic.h create mode 100644 base/include/x86/cpu/consts.h create mode 100644 base/include/x86_32/cpu/cpu_state.h create mode 100644 base/include/x86_64/cpu/cpu_state.h create mode 100644 base/lib/README create mode 100644 base/lib/import/import-stdcxx.mk create mode 100644 base/lib/mk/README create mode 100644 base/lib/mk/allocator_avl.mk create mode 100644 base/lib/mk/avl_tree.mk create mode 100644 base/lib/mk/console.mk create mode 100644 base/lib/mk/cxx.mk create mode 100644 base/lib/mk/elf.mk create mode 100644 base/lib/mk/env.mk create mode 100644 base/lib/mk/heap.mk create mode 100644 base/lib/mk/host/cxx.mk create mode 100644 base/lib/mk/log_console.mk create mode 100644 base/lib/mk/platform.mk create mode 100644 base/lib/mk/process.mk create mode 100644 base/lib/mk/raw_server.mk create mode 100644 base/lib/mk/raw_signal.mk create mode 100644 base/lib/mk/server.mk create mode 100644 base/lib/mk/signal.mk create mode 100644 base/lib/mk/slab.mk create mode 100644 base/lib/mk/stdcxx.mk create mode 100644 base/lib/mk/thread.mk create mode 100644 base/mk/README create mode 100644 base/mk/base-libs.mk create mode 100644 base/mk/dep_lib.mk create mode 100644 base/mk/dep_prg.mk create mode 100644 base/mk/generic.mk create mode 100644 base/mk/global.mk create mode 100644 base/mk/lib.mk create mode 100644 base/mk/prg.mk create mode 100644 base/mk/spec-32bit.mk create mode 100644 base/mk/spec-64bit.mk create mode 100644 base/mk/spec-arm.mk create mode 100644 base/mk/spec-arm_v5.mk create mode 100644 base/mk/spec-arm_v7a.mk create mode 100644 base/mk/spec-experimental.mk create mode 100644 base/mk/spec-host.mk create mode 100644 base/mk/spec-platform_pbxa9.mk create mode 100644 base/mk/spec-platform_vea9x4.mk create mode 100644 base/mk/spec-platform_vpb926.mk create mode 100644 base/mk/spec-release.mk create mode 100644 base/mk/spec-x86_32.mk create mode 100644 base/mk/spec-x86_64.mk create mode 100644 base/run/rm_fault.run create mode 100644 base/run/sub_rm.run create mode 100644 base/src/README create mode 100644 base/src/base/README create mode 100644 base/src/base/allocator/README create mode 100644 base/src/base/allocator/allocator_avl.cc create mode 100644 base/src/base/allocator/slab.cc create mode 100644 base/src/base/avl_tree/avl_tree.cc create mode 100644 base/src/base/console/console.cc create mode 100644 base/src/base/console/core_printf.cc create mode 100644 base/src/base/console/log_console.cc create mode 100644 base/src/base/cxx/exception.cc create mode 100644 base/src/base/cxx/guard.cc create mode 100644 base/src/base/cxx/malloc_free.cc create mode 100644 base/src/base/cxx/misc.cc create mode 100644 base/src/base/cxx/new_delete.cc create mode 100644 base/src/base/cxx/unwind.cc create mode 100644 base/src/base/elf/elf.h create mode 100644 base/src/base/elf/elf_binary.cc create mode 100644 base/src/base/env/context_area.cc create mode 100644 base/src/base/env/env.cc create mode 100644 base/src/base/heap/heap.cc create mode 100644 base/src/base/heap/sliced_heap.cc create mode 100644 base/src/base/lock/lock.cc create mode 100644 base/src/base/process/process.cc create mode 100644 base/src/base/server/common.cc create mode 100644 base/src/base/server/server.cc create mode 100644 base/src/base/signal/signal.cc create mode 100644 base/src/base/thread/thread.cc create mode 100644 base/src/base/thread/thread_bootstrap.cc create mode 100644 base/src/base/thread/thread_start.cc create mode 100644 base/src/core/arm/io_port_session_component.cc create mode 100644 base/src/core/context_area.cc create mode 100644 base/src/core/core_mem_alloc.cc create mode 100644 base/src/core/cpu_session_component.cc create mode 100644 base/src/core/dataspace_component.cc create mode 100644 base/src/core/dump_alloc.cc create mode 100644 base/src/core/include/cap_root.h create mode 100644 base/src/core/include/cap_session_component.h create mode 100644 base/src/core/include/core_env.h create mode 100644 base/src/core/include/core_mem_alloc.h create mode 100644 base/src/core/include/core_parent.h create mode 100644 base/src/core/include/core_rm_session.h create mode 100644 base/src/core/include/cpu_root.h create mode 100644 base/src/core/include/cpu_session_component.h create mode 100644 base/src/core/include/dataspace_component.h create mode 100644 base/src/core/include/io_mem_root.h create mode 100644 base/src/core/include/io_mem_session_component.h create mode 100644 base/src/core/include/io_port_root.h create mode 100644 base/src/core/include/io_port_session_component.h create mode 100644 base/src/core/include/irq_root.h create mode 100644 base/src/core/include/irq_session_component.h create mode 100644 base/src/core/include/log_root.h create mode 100644 base/src/core/include/log_session_component.h create mode 100644 base/src/core/include/multiboot.h create mode 100644 base/src/core/include/pd_root.h create mode 100644 base/src/core/include/pd_session_component.h create mode 100644 base/src/core/include/platform_generic.h create mode 100644 base/src/core/include/ram_root.h create mode 100644 base/src/core/include/ram_session_component.h create mode 100644 base/src/core/include/rm_root.h create mode 100644 base/src/core/include/rm_session_component.h create mode 100644 base/src/core/include/rom_fs.h create mode 100644 base/src/core/include/rom_root.h create mode 100644 base/src/core/include/rom_session_component.h create mode 100644 base/src/core/include/signal_root.h create mode 100644 base/src/core/include/signal_session_component.h create mode 100644 base/src/core/io_mem_session_component.cc create mode 100644 base/src/core/main.cc create mode 100644 base/src/core/mb_info.h create mode 100644 base/src/core/multiboot_info.cc create mode 100644 base/src/core/pd_session_component.cc create mode 100644 base/src/core/ram_session_component.cc create mode 100644 base/src/core/rm_session_component.cc create mode 100644 base/src/core/rom_session_component.cc create mode 100644 base/src/core/signal_session_component.cc create mode 100644 base/src/core/signal_source_component.cc create mode 100644 base/src/core/x86/io_port_session_component.cc create mode 100644 base/src/platform/_main.cc create mode 100644 base/src/platform/_main_parent_cap.h create mode 100644 base/src/platform/arm/crt0.s create mode 100644 base/src/platform/genode.ld create mode 100644 base/src/platform/x86_32/crt0.s create mode 100644 base/src/platform/x86_64/crt0.s create mode 100644 base/src/test/rm_fault/main.cc create mode 100644 base/src/test/rm_fault/target.mk create mode 100644 base/src/test/rm_nested/main.cc create mode 100644 base/src/test/rm_nested/target.mk create mode 100644 base/src/test/sub_rm/config.h create mode 100644 base/src/test/sub_rm/main.cc create mode 100644 base/src/test/sub_rm/target.mk create mode 100644 dde_ipxe/Makefile create mode 100644 dde_ipxe/README create mode 100644 dde_ipxe/include/dde_ipxe/nic.h create mode 100644 dde_ipxe/lib/mk/dde_ipxe_nic.mk create mode 100644 dde_ipxe/lib/mk/dde_ipxe_support.mk create mode 100644 dde_ipxe/patches/dde_ipxe.patch create mode 100644 dde_ipxe/src/drivers/nic/main.cc create mode 100644 dde_ipxe/src/drivers/nic/target.mk create mode 100644 dde_ipxe/src/lib/dde_ipxe/dde.c create mode 100644 dde_ipxe/src/lib/dde_ipxe/dde_support.cc create mode 100644 dde_ipxe/src/lib/dde_ipxe/dummies.c create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/byteswap.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/compiler.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/cpu.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/eltorito.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/endian.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/errfile.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/io.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/nap.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/pci_io.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/smbios.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/stdint.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/string.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/timer.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/uaccess.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/bits/umalloc.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/config/local/console.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/config/local/general.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/config/local/ioapi.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/config/local/nap.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/config/local/serial.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/config/local/timer.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/config/local/umalloc.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/include/env_dde_kit.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/local.h create mode 100644 dde_ipxe/src/lib/dde_ipxe/nic.c create mode 100644 demo/doc/demo.txt create mode 100644 demo/doc/img/genode_logo.png create mode 100644 demo/doc/img/launchpad.png create mode 100644 demo/doc/img/liquid_fb.png create mode 100644 demo/doc/img/liquid_fb_small.png create mode 100644 demo/doc/img/setup.png create mode 100644 demo/doc/img/x-ray.png create mode 100644 demo/doc/img/x-ray_small.png create mode 100644 demo/include/launchpad/launchpad.h create mode 100644 demo/include/libpng_static/png.h create mode 100644 demo/include/libpng_static/pngconf.h create mode 100644 demo/include/libpng_static/pngusr.h create mode 100644 demo/include/libz_static/zconf.h create mode 100644 demo/include/libz_static/zlib.h create mode 100644 demo/include/mini_c/errno.h create mode 100644 demo/include/mini_c/limits.h create mode 100644 demo/include/mini_c/stdio.h create mode 100644 demo/include/mini_c/stdlib.h create mode 100644 demo/include/mini_c/string.h create mode 100644 demo/include/mini_c/sys/types.h create mode 100644 demo/lib/import/import-libpng_static.mk create mode 100644 demo/lib/import/import-libz_static.mk create mode 100644 demo/lib/import/import-mini_c.mk create mode 100644 demo/lib/mk/launchpad.mk create mode 100644 demo/lib/mk/libpng_static.mk create mode 100644 demo/lib/mk/libz_static.mk create mode 100644 demo/lib/mk/mini_c.mk create mode 100644 demo/lib/mk/scout_widgets.mk create mode 100644 demo/src/app/backdrop/README create mode 100644 demo/src/app/backdrop/main.cc create mode 100644 demo/src/app/backdrop/target.mk create mode 100644 demo/src/app/launchpad/README create mode 100644 demo/src/app/launchpad/child_entry.h create mode 100644 demo/src/app/launchpad/launch_entry.h create mode 100644 demo/src/app/launchpad/launcher.cc create mode 100644 demo/src/app/launchpad/launchpad_window.cc create mode 100644 demo/src/app/launchpad/launchpad_window.h create mode 100644 demo/src/app/launchpad/loadbar.h create mode 100644 demo/src/app/launchpad/main.cc create mode 100644 demo/src/app/launchpad/section.h create mode 100644 demo/src/app/launchpad/status_entry.h create mode 100644 demo/src/app/launchpad/target.mk create mode 100644 demo/src/app/scout/common/about.cc create mode 100644 demo/src/app/scout/common/browser_window.cc create mode 100644 demo/src/app/scout/common/doc.cc create mode 100644 demo/src/app/scout/common/elements.cc create mode 100644 demo/src/app/scout/common/main.cc create mode 100644 demo/src/app/scout/common/navbar.cc create mode 100644 demo/src/app/scout/common/png_image.cc create mode 100644 demo/src/app/scout/common/refracted_icon.cc create mode 100644 demo/src/app/scout/common/scrollbar.cc create mode 100644 demo/src/app/scout/common/sky_texture.cc create mode 100644 demo/src/app/scout/common/test.txt create mode 100644 demo/src/app/scout/common/tick.cc create mode 100644 demo/src/app/scout/common/widgets.cc create mode 100644 demo/src/app/scout/data/about.rgba create mode 100644 demo/src/app/scout/data/backward.rgba create mode 100644 demo/src/app/scout/data/closed_icon.rgba create mode 100644 demo/src/app/scout/data/cover.rgba create mode 100644 demo/src/app/scout/data/downarrow.rgba create mode 100644 demo/src/app/scout/data/forward.rgba create mode 100644 demo/src/app/scout/data/home.rgba create mode 100644 demo/src/app/scout/data/index.rgba create mode 100644 demo/src/app/scout/data/ior.map create mode 100644 demo/src/app/scout/data/kill_icon.rgba create mode 100644 demo/src/app/scout/data/loadbar.rgba create mode 100644 demo/src/app/scout/data/mono16.tff create mode 100644 demo/src/app/scout/data/nav_next.rgba create mode 100644 demo/src/app/scout/data/nav_prev.rgba create mode 100644 demo/src/app/scout/data/opened_icon.rgba create mode 100644 demo/src/app/scout/data/pointer.rgba create mode 100644 demo/src/app/scout/data/redbar.rgba create mode 100644 demo/src/app/scout/data/sizer.rgba create mode 100644 demo/src/app/scout/data/slider.rgba create mode 100644 demo/src/app/scout/data/test.png create mode 100644 demo/src/app/scout/data/titlebar.rgba create mode 100644 demo/src/app/scout/data/uparrow.rgba create mode 100644 demo/src/app/scout/data/vera16.tff create mode 100644 demo/src/app/scout/data/vera18.tff create mode 100644 demo/src/app/scout/data/vera20.tff create mode 100644 demo/src/app/scout/data/vera22.tff create mode 100644 demo/src/app/scout/data/vera24.tff create mode 100644 demo/src/app/scout/data/verabi10.tff create mode 100644 demo/src/app/scout/data/verai16.tff create mode 100644 demo/src/app/scout/data/whitebar.rgba create mode 100644 demo/src/app/scout/genode/launcher.cc create mode 100644 demo/src/app/scout/genode/platform_genode.cc create mode 100644 demo/src/app/scout/genode/startup.cc create mode 100644 demo/src/app/scout/genode/target.mk create mode 100644 demo/src/app/scout/include/browser.h create mode 100644 demo/src/app/scout/include/browser_window.h create mode 100644 demo/src/app/scout/include/canvas.h create mode 100644 demo/src/app/scout/include/canvas_rgb565.h create mode 100644 demo/src/app/scout/include/color.h create mode 100644 demo/src/app/scout/include/config.h create mode 100644 demo/src/app/scout/include/elements.h create mode 100644 demo/src/app/scout/include/event.h create mode 100644 demo/src/app/scout/include/fade_icon.h create mode 100644 demo/src/app/scout/include/fader.h create mode 100644 demo/src/app/scout/include/font.h create mode 100644 demo/src/app/scout/include/genode/alloc.h create mode 100644 demo/src/app/scout/include/genode/launcher_config.h create mode 100644 demo/src/app/scout/include/genode/printf.h create mode 100644 demo/src/app/scout/include/genode/string.h create mode 100644 demo/src/app/scout/include/history.h create mode 100644 demo/src/app/scout/include/miscmath.h create mode 100644 demo/src/app/scout/include/platform.h create mode 100644 demo/src/app/scout/include/redraw_manager.h create mode 100644 demo/src/app/scout/include/refracted_icon.h create mode 100644 demo/src/app/scout/include/scout_types.h create mode 100644 demo/src/app/scout/include/scrollbar.h create mode 100644 demo/src/app/scout/include/sky_texture.h create mode 100644 demo/src/app/scout/include/styles.h create mode 100644 demo/src/app/scout/include/tick.h create mode 100644 demo/src/app/scout/include/titlebar.h create mode 100644 demo/src/app/scout/include/user_state.h create mode 100644 demo/src/app/scout/include/widgets.h create mode 100644 demo/src/app/scout/include/window.h create mode 100644 demo/src/lib/launchpad/launchpad.cc create mode 100644 demo/src/lib/libpng/contrib/png.c create mode 100644 demo/src/lib/libpng/contrib/pngerror.c create mode 100644 demo/src/lib/libpng/contrib/pngget.c create mode 100644 demo/src/lib/libpng/contrib/pngmem.c create mode 100644 demo/src/lib/libpng/contrib/pngpread.c create mode 100644 demo/src/lib/libpng/contrib/pngread.c create mode 100644 demo/src/lib/libpng/contrib/pngrio.c create mode 100644 demo/src/lib/libpng/contrib/pngrtran.c create mode 100644 demo/src/lib/libpng/contrib/pngrutil.c create mode 100644 demo/src/lib/libpng/contrib/pngset.c create mode 100644 demo/src/lib/libpng/contrib/pngtrans.c create mode 100644 demo/src/lib/libpng/contrib/pngwio.c create mode 100644 demo/src/lib/libpng/contrib/pngwrite.c create mode 100644 demo/src/lib/libpng/contrib/pngwtran.c create mode 100644 demo/src/lib/libpng/contrib/pngwutil.c create mode 100644 demo/src/lib/libpng/main.cc create mode 100644 demo/src/lib/libpng/stdio.h create mode 100644 demo/src/lib/libpng/target.mk create mode 100644 demo/src/lib/libz/contrib/adler32.c create mode 100644 demo/src/lib/libz/contrib/compress.c create mode 100644 demo/src/lib/libz/contrib/crc32.c create mode 100644 demo/src/lib/libz/contrib/crc32.h create mode 100644 demo/src/lib/libz/contrib/deflate.c create mode 100644 demo/src/lib/libz/contrib/deflate.h create mode 100644 demo/src/lib/libz/contrib/gzio.c create mode 100644 demo/src/lib/libz/contrib/infback.c create mode 100644 demo/src/lib/libz/contrib/inffast.c create mode 100644 demo/src/lib/libz/contrib/inffast.h create mode 100644 demo/src/lib/libz/contrib/inffixed.h create mode 100644 demo/src/lib/libz/contrib/inflate.c create mode 100644 demo/src/lib/libz/contrib/inflate.h create mode 100644 demo/src/lib/libz/contrib/inftrees.c create mode 100644 demo/src/lib/libz/contrib/inftrees.h create mode 100644 demo/src/lib/libz/contrib/trees.c create mode 100644 demo/src/lib/libz/contrib/trees.h create mode 100644 demo/src/lib/libz/contrib/uncompr.c create mode 100644 demo/src/lib/libz/contrib/zutil.c create mode 100644 demo/src/lib/libz/contrib/zutil.h create mode 100644 demo/src/lib/mini_c/README create mode 100644 demo/src/lib/mini_c/abort.cc create mode 100644 demo/src/lib/mini_c/atol.cc create mode 100644 demo/src/lib/mini_c/malloc_free.cc create mode 100644 demo/src/lib/mini_c/memcmp.cc create mode 100644 demo/src/lib/mini_c/memset.cc create mode 100644 demo/src/lib/mini_c/mini_c.c create mode 100644 demo/src/lib/mini_c/printf.cc create mode 100644 demo/src/lib/mini_c/snprintf.cc create mode 100644 demo/src/lib/mini_c/strlen.cc create mode 100644 demo/src/lib/mini_c/strtod.cc create mode 100644 demo/src/lib/mini_c/strtol.cc create mode 100644 demo/src/lib/mini_c/vsnprintf.cc create mode 100644 demo/src/server/liquid_framebuffer/README create mode 100644 demo/src/server/liquid_framebuffer/framebuffer_window.h create mode 100644 demo/src/server/liquid_framebuffer/main.cc create mode 100644 demo/src/server/liquid_framebuffer/services.cc create mode 100644 demo/src/server/liquid_framebuffer/services.h create mode 100644 demo/src/server/liquid_framebuffer/target.mk create mode 100644 demo/src/server/nitlog/main.cc create mode 100644 demo/src/server/nitlog/mono.tff create mode 100644 demo/src/server/nitlog/target.mk create mode 100644 doc/Makefile create mode 100644 doc/build_system.txt create mode 100644 doc/coding_style.txt create mode 100644 doc/components.txt create mode 100644 doc/conventions.txt create mode 100644 doc/future_optimizations.txt create mode 100644 doc/getting_started.txt create mode 100644 doc/release_notes-08-11.txt create mode 100644 doc/release_notes-09-02.txt create mode 100644 doc/release_notes-09-05.txt create mode 100644 doc/release_notes-09-08.txt create mode 100644 doc/release_notes-09-11.txt create mode 100644 doc/release_notes-10-02.txt create mode 100644 doc/release_notes-10-05.txt create mode 100644 doc/release_notes-10-08.txt create mode 100644 doc/release_notes-10-11.txt create mode 100644 doc/release_notes-11-02.txt create mode 100644 doc/release_notes-11-05.txt create mode 100644 doc/release_notes-11-08.txt create mode 100644 doc/release_notes-11-11.txt create mode 100644 gems/README create mode 100644 gems/include/terminal/character_screen.h create mode 100644 gems/include/terminal/character_screen_tracer.h create mode 100644 gems/include/terminal/decoder.h create mode 100644 gems/include/terminal/types.h create mode 100644 gems/run/tcp_terminal.run create mode 100644 gems/run/terminal_decoder.run create mode 100644 gems/run/terminal_echo.run create mode 100644 gems/src/server/http_block/README create mode 100644 gems/src/server/http_block/http.cc create mode 100644 gems/src/server/http_block/http.h create mode 100644 gems/src/server/http_block/main.cc create mode 100644 gems/src/server/http_block/target.mk create mode 100644 gems/src/server/tcp_terminal/README create mode 100644 gems/src/server/tcp_terminal/main.cc create mode 100644 gems/src/server/tcp_terminal/target.mk create mode 100644 gems/src/server/terminal/main.cc create mode 100644 gems/src/server/terminal/mono.tff create mode 100644 gems/src/server/terminal/target.mk create mode 100644 gems/src/test/terminal_decoder/main.cc create mode 100644 gems/src/test/terminal_decoder/target.mk create mode 100644 gems/src/test/terminal_decoder/vim.vt create mode 100644 hello_tutorial/README create mode 100644 hello_tutorial/config/config create mode 100644 hello_tutorial/doc/hello_tutorial.txt create mode 100644 hello_tutorial/include/hello_session/client.h create mode 100644 hello_tutorial/include/hello_session/connection.h create mode 100644 hello_tutorial/include/hello_session/hello_session.h create mode 100644 hello_tutorial/run/hello.run create mode 100644 hello_tutorial/src/hello/client/main.cc create mode 100644 hello_tutorial/src/hello/client/target.mk create mode 100644 hello_tutorial/src/hello/server/main.cc create mode 100644 hello_tutorial/src/hello/server/target.mk create mode 100644 libports/Makefile create mode 100644 libports/README create mode 100644 libports/doc/libc.txt create mode 100644 libports/include/EGL/eglplatform.h create mode 100644 libports/include/freetype-genode/ftconfig.h create mode 100644 libports/include/freetype-genode/ftmodule.h create mode 100644 libports/include/gcc/README create mode 100644 libports/include/gcc/longlong.h create mode 100644 libports/include/gmp/config.h create mode 100644 libports/include/gmp/x86_32/fac_ui.h create mode 100644 libports/include/gmp/x86_32/fib_table.h create mode 100644 libports/include/gmp/x86_32/gmp.h create mode 100644 libports/include/gmp/x86_32/mp_bases.h create mode 100644 libports/include/gmp/x86_32/perfsqr.h create mode 100644 libports/include/jpeg/jconfig.h create mode 100644 libports/include/libc-genode/mntent.h create mode 100644 libports/include/libc-genode/sys/syscall.h create mode 100644 libports/include/libc-genode/timeconv.h create mode 100644 libports/include/libc-plugin/fd_alloc.h create mode 100644 libports/include/libc-plugin/plugin.h create mode 100644 libports/include/libc-plugin/plugin_registry.h create mode 100644 libports/include/lwip/arch/cc.h create mode 100644 libports/include/lwip/arch/perf.h create mode 100644 libports/include/lwip/arch/sys_arch.h create mode 100644 libports/include/lwip/genode.h create mode 100644 libports/include/lwip/lwipopts.h create mode 100644 libports/include/ncurses/ncurses_cfg.h create mode 100644 libports/include/python/osreldate.h create mode 100644 libports/include/python/pyconfig.h create mode 100644 libports/include/python/x86_32/genode_defs.h create mode 100644 libports/include/python/x86_64/genode_defs.h create mode 100644 libports/include/readline/config.h create mode 100644 libports/lib/import/import-gmp.mk create mode 100644 libports/lib/import/import-jpeg.mk create mode 100644 libports/lib/import/import-libc.mk create mode 100644 libports/lib/import/import-libpng.mk create mode 100644 libports/lib/import/import-lwip.mk create mode 100644 libports/lib/import/import-mpfr.mk create mode 100644 libports/lib/import/import-ncurses.mk create mode 100644 libports/lib/import/import-python.mk create mode 100644 libports/lib/import/import-zlib.mk create mode 100644 libports/lib/mk/arm/libc-gen.mk create mode 100644 libports/lib/mk/arm/libm.mk create mode 100644 libports/lib/mk/ffat.mk create mode 100644 libports/lib/mk/ffat_block.mk create mode 100644 libports/lib/mk/freetype.mk create mode 100644 libports/lib/mk/gallium-aux.mk create mode 100644 libports/lib/mk/gallium-egl.mk create mode 100644 libports/lib/mk/gallium-failover.mk create mode 100644 libports/lib/mk/gallium-i915.mk create mode 100644 libports/lib/mk/gallium-identity.mk create mode 100644 libports/lib/mk/gallium-softpipe.mk create mode 100644 libports/lib/mk/gallium-trace.mk create mode 100644 libports/lib/mk/gallium.inc create mode 100644 libports/lib/mk/gallium.mk create mode 100644 libports/lib/mk/gmp-mpf.mk create mode 100644 libports/lib/mk/gmp-mpq.mk create mode 100644 libports/lib/mk/gmp-mpz.mk create mode 100644 libports/lib/mk/gmp.inc create mode 100644 libports/lib/mk/gmp.mk create mode 100644 libports/lib/mk/history.mk create mode 100644 libports/lib/mk/jpeg.mk create mode 100644 libports/lib/mk/libc-common.inc create mode 100644 libports/lib/mk/libc-gdtoa.mk create mode 100644 libports/lib/mk/libc-gen.inc create mode 100644 libports/lib/mk/libc-inet.mk create mode 100644 libports/lib/mk/libc-locale.mk create mode 100644 libports/lib/mk/libc-stdio.mk create mode 100644 libports/lib/mk/libc-stdlib.mk create mode 100644 libports/lib/mk/libc-stdtime.mk create mode 100644 libports/lib/mk/libc-string.mk create mode 100644 libports/lib/mk/libc.mk create mode 100644 libports/lib/mk/libc_ffat.mk create mode 100644 libports/lib/mk/libc_lock_pipe.mk create mode 100644 libports/lib/mk/libc_log.mk create mode 100644 libports/lib/mk/libc_lwip.mk create mode 100644 libports/lib/mk/libc_lwip_loopback.mk create mode 100644 libports/lib/mk/libc_lwip_nic_dhcp.mk create mode 100644 libports/lib/mk/libc_terminal.mk create mode 100644 libports/lib/mk/libdrm.mk create mode 100644 libports/lib/mk/libm.mk create mode 100644 libports/lib/mk/libpng.mk create mode 100644 libports/lib/mk/lwip.mk create mode 100644 libports/lib/mk/mesa-egl.mk create mode 100644 libports/lib/mk/mesa.inc create mode 100644 libports/lib/mk/mesa.mk create mode 100644 libports/lib/mk/mpfr.mk create mode 100644 libports/lib/mk/ncurses.mk create mode 100644 libports/lib/mk/python.inc create mode 100644 libports/lib/mk/readline.mk create mode 100644 libports/lib/mk/sdl.mk create mode 100644 libports/lib/mk/test-ldso.mk create mode 100644 libports/lib/mk/test-ldso2.mk create mode 100644 libports/lib/mk/x86_32/gmp-mpn.mk create mode 100644 libports/lib/mk/x86_32/libc-gen.mk create mode 100644 libports/lib/mk/x86_32/python.mk create mode 100644 libports/lib/mk/x86_64/libc-gen.mk create mode 100644 libports/lib/mk/x86_64/python.mk create mode 100644 libports/lib/mk/zlib.mk create mode 100644 libports/ports/ffat.mk create mode 100644 libports/ports/freetype.mk create mode 100644 libports/ports/gmp.mk create mode 100644 libports/ports/jpeg.mk create mode 100644 libports/ports/libc.mk create mode 100644 libports/ports/libdrm.mk create mode 100644 libports/ports/libpng.mk create mode 100644 libports/ports/lwip.mk create mode 100644 libports/ports/mesa.mk create mode 100644 libports/ports/mpfr.mk create mode 100644 libports/ports/ncurses.mk create mode 100644 libports/ports/python.mk create mode 100644 libports/ports/readline.mk create mode 100644 libports/ports/sdl.mk create mode 100644 libports/ports/zlib.mk create mode 100644 libports/run/eglgears.run create mode 100644 libports/run/libc_ffat.run create mode 100644 libports/run/lwip.run create mode 100644 libports/run/lwip_lx.run create mode 100644 libports/run/python.run create mode 100644 libports/run/test-libc.run create mode 100644 libports/src/app/eglgears/eglgears.c create mode 100644 libports/src/app/eglgears/target.mk create mode 100644 libports/src/lib/egl/driver.cc create mode 100644 libports/src/lib/egl/select_driver.cc create mode 100644 libports/src/lib/egl/select_driver.h create mode 100644 libports/src/lib/egl/st_opengl.c create mode 100644 libports/src/lib/ffat/config.patch create mode 100644 libports/src/lib/ffat/diskio.c create mode 100644 libports/src/lib/ffat/diskio_block.cc create mode 100644 libports/src/lib/gallium/README create mode 100644 libports/src/lib/gallium/dummy_trace.c create mode 100644 libports/src/lib/gallium/i915/query_device_id.cc create mode 100644 libports/src/lib/gallium/i915/target.mk create mode 100644 libports/src/lib/gallium/main.cc create mode 100644 libports/src/lib/gallium/p_state_config.patch create mode 100644 libports/src/lib/gmp/config.m4 create mode 100644 libports/src/lib/gmp/mpn/x86/add_n.asm create mode 100644 libports/src/lib/gmp/mpn/x86/dummy.c create mode 100644 libports/src/lib/gmp/mpn/x86/fib_table.c create mode 100644 libports/src/lib/gmp/mpn/x86/mp_bases.c create mode 100644 libports/src/lib/libc/Version.def create mode 100644 libports/src/lib/libc/atexit.cc create mode 100644 libports/src/lib/libc/clock_gettime.cc create mode 100644 libports/src/lib/libc/dummies.cc create mode 100644 libports/src/lib/libc/environ.cc create mode 100644 libports/src/lib/libc/errno.cc create mode 100644 libports/src/lib/libc/exit.cc create mode 100644 libports/src/lib/libc/fd_alloc.cc create mode 100644 libports/src/lib/libc/file_operations.cc create mode 100644 libports/src/lib/libc/gai_strerror.cc create mode 100644 libports/src/lib/libc/gettimeofday.cc create mode 100644 libports/src/lib/libc/ioctl.cc create mode 100644 libports/src/lib/libc/issetugid.cc create mode 100644 libports/src/lib/libc/libc_debug.h create mode 100644 libports/src/lib/libc/malloc.cc create mode 100644 libports/src/lib/libc/munmap.cc create mode 100644 libports/src/lib/libc/patches/README create mode 100644 libports/src/lib/libc/patches/malloc_c.patch create mode 100644 libports/src/lib/libc/patches/math_private.patch create mode 100644 libports/src/lib/libc/patches/pthread_cancel.patch create mode 100644 libports/src/lib/libc/patches/vfwprintf_c_warn.patch create mode 100644 libports/src/lib/libc/plugin.cc create mode 100644 libports/src/lib/libc/plugin_registry.cc create mode 100644 libports/src/lib/libc/progname.cc create mode 100644 libports/src/lib/libc/readlink.cc create mode 100644 libports/src/lib/libc/rlimit.cc create mode 100644 libports/src/lib/libc/select.cc create mode 100644 libports/src/lib/libc/sysctl.cc create mode 100644 libports/src/lib/libc_ffat/plugin.cc create mode 100644 libports/src/lib/libc_lock_pipe/plugin.cc create mode 100644 libports/src/lib/libc_log/plugin.cc create mode 100644 libports/src/lib/libc_lwip/init.cc create mode 100644 libports/src/lib/libc_lwip/plugin.cc create mode 100644 libports/src/lib/libc_lwip_loopback/init.cc create mode 100644 libports/src/lib/libc_lwip_nic_dhcp/init.cc create mode 100644 libports/src/lib/libc_terminal/README create mode 100644 libports/src/lib/libc_terminal/plugin.cc create mode 100644 libports/src/lib/libdrm/ioctl.cc create mode 100644 libports/src/lib/libpng/config.h create mode 100644 libports/src/lib/lwip/errno.patch create mode 100644 libports/src/lib/lwip/include/nic.h create mode 100644 libports/src/lib/lwip/include/ring_buffer.h create mode 100644 libports/src/lib/lwip/include/thread.h create mode 100644 libports/src/lib/lwip/include/timer.h create mode 100644 libports/src/lib/lwip/libc_select_notify.patch create mode 100644 libports/src/lib/lwip/platform/nic.cc create mode 100644 libports/src/lib/lwip/platform/printf.cc create mode 100644 libports/src/lib/lwip/platform/sys_arch.cc create mode 100644 libports/src/lib/python/config.c create mode 100644 libports/src/lib/python/dup.c create mode 100644 libports/src/lib/python/libc_plugin.cc create mode 100644 libports/src/lib/python/libc_plugin_init.cc create mode 100644 libports/src/lib/python/posixmodule.patch create mode 100644 libports/src/lib/readline/genode.cc create mode 100644 libports/src/lib/sdl/SDL_config.h create mode 100644 libports/src/lib/sdl/SDL_config_genode.h create mode 100644 libports/src/lib/sdl/SDL_video.patch create mode 100644 libports/src/lib/sdl/video/SDL_genode_fb_events.cc create mode 100644 libports/src/lib/sdl/video/SDL_genode_fb_events.h create mode 100644 libports/src/lib/sdl/video/SDL_genode_fb_video.cc create mode 100644 libports/src/lib/sdl/video/SDL_genode_fb_video.h create mode 100644 libports/src/test/ldso/include/test-ldso.h create mode 100644 libports/src/test/ldso/lib/test-rtld.cc create mode 100644 libports/src/test/ldso/lib/test_lib.cc create mode 100644 libports/src/test/ldso/main.cc create mode 100644 libports/src/test/ldso/target.mk create mode 100644 libports/src/test/libc/main.cc create mode 100644 libports/src/test/libc/target.mk create mode 100644 libports/src/test/libc_ffat/main.cc create mode 100644 libports/src/test/libc_ffat/target.mk create mode 100644 libports/src/test/libports/freetype/target.mk create mode 100644 libports/src/test/libports/gmp/target.mk create mode 100644 libports/src/test/libports/jpeg/target.mk create mode 100644 libports/src/test/libports/libpng/target.mk create mode 100644 libports/src/test/libports/main.cc create mode 100644 libports/src/test/libports/mesa/target.mk create mode 100644 libports/src/test/libports/mpfr/target.mk create mode 100644 libports/src/test/libports/ncurses/target.mk create mode 100644 libports/src/test/libports/readline/target.mk create mode 100644 libports/src/test/libports/zlib/target.mk create mode 100644 libports/src/test/lwip/http_srv/main.cc create mode 100644 libports/src/test/lwip/http_srv/target.mk create mode 100644 libports/src/test/lwip/loopback/main.cc create mode 100644 libports/src/test/lwip/loopback/target.mk create mode 100644 libports/src/test/python/README create mode 100644 libports/src/test/python/hello.py create mode 100644 libports/src/test/python/main.cc create mode 100644 libports/src/test/python/target.mk create mode 100644 libports/src/test/sdl/main.cc create mode 100644 libports/src/test/sdl/target.mk create mode 100644 libports/tool/mesa/Makefile create mode 100644 os/README create mode 100644 os/config/bomb create mode 100644 os/config/demo create mode 100644 os/config/gta01 create mode 100644 os/config/linux_demo create mode 100644 os/config/mixer create mode 100644 os/config/nested_config create mode 100644 os/config/priority create mode 100644 os/doc/init.txt create mode 100644 os/include/audio_out_session/audio_out_session.h create mode 100644 os/include/audio_out_session/capability.h create mode 100644 os/include/audio_out_session/client.h create mode 100644 os/include/audio_out_session/connection.h create mode 100644 os/include/audio_out_session/rpc_object.h create mode 100644 os/include/blit/blit.h create mode 100644 os/include/block/component.h create mode 100644 os/include/block/driver.h create mode 100644 os/include/block_session/block_session.h create mode 100644 os/include/block_session/capability.h create mode 100644 os/include/block_session/client.h create mode 100644 os/include/block_session/connection.h create mode 100644 os/include/block_session/rpc_object.h create mode 100644 os/include/dde_kit/assert.h create mode 100644 os/include/dde_kit/dde_kit.h create mode 100644 os/include/dde_kit/initcall.h create mode 100644 os/include/dde_kit/interrupt.h create mode 100644 os/include/dde_kit/lock.h create mode 100644 os/include/dde_kit/memory.h create mode 100644 os/include/dde_kit/panic.h create mode 100644 os/include/dde_kit/pci.h create mode 100644 os/include/dde_kit/pgtab.h create mode 100644 os/include/dde_kit/printf.h create mode 100644 os/include/dde_kit/resources.h create mode 100644 os/include/dde_kit/semaphore.h create mode 100644 os/include/dde_kit/thread.h create mode 100644 os/include/dde_kit/timer.h create mode 100644 os/include/dde_kit/types.h create mode 100644 os/include/framebuffer_session/capability.h create mode 100644 os/include/framebuffer_session/client.h create mode 100644 os/include/framebuffer_session/connection.h create mode 100644 os/include/framebuffer_session/framebuffer_session.h create mode 100644 os/include/gpu/driver.h create mode 100644 os/include/init/child.h create mode 100644 os/include/init/child_config.h create mode 100644 os/include/init/child_policy.h create mode 100644 os/include/input/component.h create mode 100644 os/include/input/event.h create mode 100644 os/include/input/keycodes.h create mode 100644 os/include/input_session/capability.h create mode 100644 os/include/input_session/client.h create mode 100644 os/include/input_session/connection.h create mode 100644 os/include/input_session/input_session.h create mode 100644 os/include/ldso/arch.h create mode 100644 os/include/loader_session/capability.h create mode 100644 os/include/loader_session/client.h create mode 100644 os/include/loader_session/connection.h create mode 100644 os/include/loader_session/loader_session.h create mode 100644 os/include/net/arp.h create mode 100644 os/include/net/dhcp.h create mode 100644 os/include/net/ethernet.h create mode 100644 os/include/net/ipv4.h create mode 100644 os/include/net/netaddress.h create mode 100644 os/include/net/udp.h create mode 100644 os/include/nic/component.h create mode 100644 os/include/nic/driver.h create mode 100644 os/include/nic_session/capability.h create mode 100644 os/include/nic_session/client.h create mode 100644 os/include/nic_session/connection.h create mode 100644 os/include/nic_session/nic_session.h create mode 100644 os/include/nic_session/rpc_object.h create mode 100644 os/include/nitpicker_gfx/README create mode 100644 os/include/nitpicker_gfx/canvas.h create mode 100644 os/include/nitpicker_gfx/chunky_canvas.h create mode 100644 os/include/nitpicker_gfx/color.h create mode 100644 os/include/nitpicker_gfx/font.h create mode 100644 os/include/nitpicker_gfx/geometry.h create mode 100644 os/include/nitpicker_gfx/miscmath.h create mode 100644 os/include/nitpicker_gfx/nitpicker_types.h create mode 100644 os/include/nitpicker_gfx/pixel_rgb.h create mode 100644 os/include/nitpicker_gfx/pixel_rgb565.h create mode 100644 os/include/nitpicker_session/capability.h create mode 100644 os/include/nitpicker_session/client.h create mode 100644 os/include/nitpicker_session/connection.h create mode 100644 os/include/nitpicker_session/nitpicker_session.h create mode 100644 os/include/nitpicker_view/capability.h create mode 100644 os/include/nitpicker_view/client.h create mode 100644 os/include/nitpicker_view/nitpicker_view.h create mode 100644 os/include/os/alarm.h create mode 100644 os/include/os/attached_io_mem_dataspace.h create mode 100644 os/include/os/attached_ram_dataspace.h create mode 100644 os/include/os/config.h create mode 100644 os/include/os/irq_activation.h create mode 100644 os/include/os/packet_stream.h create mode 100644 os/include/os/ring_buffer.h create mode 100644 os/include/os/session_policy.h create mode 100644 os/include/os/timed_semaphore.h create mode 100644 os/include/packet_stream_rx/client.h create mode 100644 os/include/packet_stream_rx/packet_stream_rx.h create mode 100644 os/include/packet_stream_rx/rpc_object.h create mode 100644 os/include/packet_stream_tx/client.h create mode 100644 os/include/packet_stream_tx/packet_stream_tx.h create mode 100644 os/include/packet_stream_tx/rpc_object.h create mode 100644 os/include/pci_device/capability.h create mode 100644 os/include/pci_device/client.h create mode 100644 os/include/pci_device/pci_device.h create mode 100644 os/include/pci_session/capability.h create mode 100644 os/include/pci_session/client.h create mode 100644 os/include/pci_session/connection.h create mode 100644 os/include/pci_session/pci_session.h create mode 100644 os/include/platform/pbxa9/lan9118_defs.h create mode 100644 os/include/platform/pbxa9/pl011_defs.h create mode 100644 os/include/platform/pbxa9/pl050_defs.h create mode 100644 os/include/platform/pbxa9/pl11x_defs.h create mode 100644 os/include/platform/pbxa9/pl180_defs.h create mode 100644 os/include/platform/pbxa9/sp810_defs.h create mode 100644 os/include/platform/vea9x4/bus.h create mode 100644 os/include/platform/vea9x4/lan9118_defs.h create mode 100644 os/include/platform/vea9x4/pl011_defs.h create mode 100644 os/include/platform/vea9x4/pl050_defs.h create mode 100644 os/include/platform/vea9x4/pl11x_defs.h create mode 100644 os/include/platform/vea9x4/pl180_defs.h create mode 100644 os/include/platform/vea9x4/sp810_defs.h create mode 100644 os/include/platform/vpb926/pl011_defs.h create mode 100644 os/include/platform/vpb926/pl050_defs.h create mode 100644 os/include/platform/vpb926/pl11x_defs.h create mode 100644 os/include/platform/vpb926/sp810_defs.h create mode 100644 os/include/terminal_session/client.h create mode 100644 os/include/terminal_session/connection.h create mode 100644 os/include/terminal_session/terminal_session.h create mode 100644 os/include/timer_session/capability.h create mode 100644 os/include/timer_session/client.h create mode 100644 os/include/timer_session/connection.h create mode 100644 os/include/timer_session/server.h create mode 100644 os/include/timer_session/timer_session.h create mode 100644 os/include/util/endian.h create mode 100644 os/include/util/xml_node.h create mode 100644 os/include/xev_track/xev_track.h create mode 100644 os/lib/mk/alarm.mk create mode 100644 os/lib/mk/arm/ld.mk create mode 100644 os/lib/mk/arm/ldso-startup.mk create mode 100644 os/lib/mk/arm/ldso_crt0.mk create mode 100644 os/lib/mk/blit.mk create mode 100644 os/lib/mk/codezero/ldso-arch.mk create mode 100644 os/lib/mk/dde_kit.mk create mode 100644 os/lib/mk/ldso-arch.mk create mode 100644 os/lib/mk/ldso-startup.mk create mode 100644 os/lib/mk/linux/ldso-arch.mk create mode 100644 os/lib/mk/net.mk create mode 100644 os/lib/mk/pistachio/ldso-arch.mk create mode 100644 os/lib/mk/timed_semaphore.mk create mode 100644 os/lib/mk/x86_32/blit.mk create mode 100644 os/lib/mk/x86_32/ld.mk create mode 100644 os/lib/mk/x86_32/ldso_crt0.mk create mode 100644 os/lib/mk/x86_32/ldso_crt0_lx.mk create mode 100644 os/lib/mk/x86_64/blit.mk create mode 100644 os/lib/mk/x86_64/ld.mk create mode 100644 os/lib/mk/x86_64/ldso_crt0.mk create mode 100644 os/lib/mk/x86_64/ldso_crt0_lx.mk create mode 100644 os/lib/mk/xev_track.mk create mode 100644 os/run/ahci.run create mode 100644 os/run/demo.run create mode 100644 os/run/ldso.run create mode 100644 os/run/part_blk.run create mode 100644 os/run/rom_blk.run create mode 100644 os/run/sd_card.run create mode 100644 os/run/signal.run create mode 100644 os/run/tar_rom.run create mode 100644 os/run/timed_semaphore.run create mode 100644 os/run/uart.run create mode 100644 os/src/app/xvfb/README create mode 100644 os/src/app/xvfb/inject_input.cc create mode 100644 os/src/app/xvfb/inject_input.h create mode 100644 os/src/app/xvfb/main.cc create mode 100644 os/src/app/xvfb/target.mk create mode 100644 os/src/drivers/ahci/README create mode 100644 os/src/drivers/ahci/main.cc create mode 100644 os/src/drivers/ahci/target.mk create mode 100644 os/src/drivers/atapi/README create mode 100644 os/src/drivers/atapi/ata_bus_master.cc create mode 100644 os/src/drivers/atapi/ata_bus_master.h create mode 100644 os/src/drivers/atapi/ata_device.cc create mode 100644 os/src/drivers/atapi/ata_device.h create mode 100644 os/src/drivers/atapi/atapi_device.cc create mode 100644 os/src/drivers/atapi/contrib/mindrvr-guide.txt create mode 100644 os/src/drivers/atapi/contrib/mindrvr.c create mode 100644 os/src/drivers/atapi/contrib/mindrvr.h create mode 100644 os/src/drivers/atapi/endian.h create mode 100644 os/src/drivers/atapi/io.cc create mode 100644 os/src/drivers/atapi/io.h create mode 100644 os/src/drivers/atapi/main.cc create mode 100644 os/src/drivers/atapi/pio.h create mode 100644 os/src/drivers/atapi/target.mk create mode 100644 os/src/drivers/audio_out/linux/alsa.c create mode 100644 os/src/drivers/audio_out/linux/alsa.h create mode 100644 os/src/drivers/audio_out/linux/main.cc create mode 100644 os/src/drivers/audio_out/linux/target.mk create mode 100644 os/src/drivers/framebuffer/fiasco_ux/framebuffer.cc create mode 100644 os/src/drivers/framebuffer/fiasco_ux/framebuffer.h create mode 100644 os/src/drivers/framebuffer/fiasco_ux/main.cc create mode 100644 os/src/drivers/framebuffer/fiasco_ux/target.mk create mode 100644 os/src/drivers/framebuffer/pl11x/main.cc create mode 100644 os/src/drivers/framebuffer/pl11x/pbxa9/target.mk create mode 100644 os/src/drivers/framebuffer/pl11x/target.mk create mode 100644 os/src/drivers/framebuffer/pl11x/vea9x4/target.mk create mode 100644 os/src/drivers/framebuffer/pl11x/vea9x4/video_memory.cc create mode 100644 os/src/drivers/framebuffer/pl11x/video_memory.cc create mode 100644 os/src/drivers/framebuffer/pl11x/video_memory.h create mode 100644 os/src/drivers/framebuffer/pl11x/vpb926/target.mk create mode 100644 os/src/drivers/framebuffer/sdl/fb_sdl.cc create mode 100644 os/src/drivers/framebuffer/sdl/input.cc create mode 100644 os/src/drivers/framebuffer/sdl/target.mk create mode 100644 os/src/drivers/framebuffer/vesa/README create mode 100644 os/src/drivers/framebuffer/vesa/contrib/LICENSE create mode 100644 os/src/drivers/framebuffer/vesa/contrib/Makefile.old create mode 100644 os/src/drivers/framebuffer/vesa/contrib/debug.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/decode.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/fgets.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/fpu.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/ops.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/ops2.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/prim_ops.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/printk.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/sys.c create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/debug.h create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/decode.h create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/fpu.h create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/fpu_regs.h create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/ops.h create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/prim_asm.h create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/prim_ops.h create mode 100644 os/src/drivers/framebuffer/vesa/contrib/x86emu/x86emui.h create mode 100644 os/src/drivers/framebuffer/vesa/framebuffer.cc create mode 100644 os/src/drivers/framebuffer/vesa/hw_emul.cc create mode 100644 os/src/drivers/framebuffer/vesa/ifx86emu.cc create mode 100644 os/src/drivers/framebuffer/vesa/include/framebuffer.h create mode 100644 os/src/drivers/framebuffer/vesa/include/hw_emul.h create mode 100644 os/src/drivers/framebuffer/vesa/include/ifx86emu.h create mode 100644 os/src/drivers/framebuffer/vesa/include/vbe.h create mode 100644 os/src/drivers/framebuffer/vesa/include/vesa.h create mode 100644 os/src/drivers/framebuffer/vesa/include/x86emu/regs.h create mode 100644 os/src/drivers/framebuffer/vesa/include/x86emu/types.h create mode 100644 os/src/drivers/framebuffer/vesa/include/x86emu/x86emu.h create mode 100644 os/src/drivers/framebuffer/vesa/main.cc create mode 100644 os/src/drivers/framebuffer/vesa/target.mk create mode 100644 os/src/drivers/input/dummy/README create mode 100644 os/src/drivers/input/dummy/main.cc create mode 100644 os/src/drivers/input/dummy/target.mk create mode 100644 os/src/drivers/input/fiasco_ux/input.cc create mode 100644 os/src/drivers/input/fiasco_ux/input.h create mode 100644 os/src/drivers/input/fiasco_ux/main.cc create mode 100644 os/src/drivers/input/fiasco_ux/target.mk create mode 100644 os/src/drivers/input/fiasco_ux/test.cc create mode 100644 os/src/drivers/input/ps2/event_queue.h create mode 100644 os/src/drivers/input/ps2/input_driver.h create mode 100644 os/src/drivers/input/ps2/irq_handler.h create mode 100644 os/src/drivers/input/ps2/pl050/irq_handler.h create mode 100644 os/src/drivers/input/ps2/pl050/main.cc create mode 100644 os/src/drivers/input/ps2/pl050/pl050.h create mode 100644 os/src/drivers/input/ps2/pl050/stdio.h create mode 100644 os/src/drivers/input/ps2/pl050/string.h create mode 100644 os/src/drivers/input/ps2/pl050/target.mk create mode 100644 os/src/drivers/input/ps2/ps2_keyboard.h create mode 100644 os/src/drivers/input/ps2/ps2_mouse.h create mode 100644 os/src/drivers/input/ps2/scan_code_set_1.h create mode 100644 os/src/drivers/input/ps2/scan_code_set_2.h create mode 100644 os/src/drivers/input/ps2/serial_interface.h create mode 100644 os/src/drivers/input/ps2/x86/i8042.h create mode 100644 os/src/drivers/input/ps2/x86/main.cc create mode 100644 os/src/drivers/input/ps2/x86/target.mk create mode 100644 os/src/drivers/nic/lan9118/lan9118.h create mode 100644 os/src/drivers/nic/lan9118/main.cc create mode 100644 os/src/drivers/nic/lan9118/target.mk create mode 100644 os/src/drivers/nic/linux/main.cc create mode 100644 os/src/drivers/nic/linux/target.mk create mode 100644 os/src/drivers/pci/main.cc create mode 100644 os/src/drivers/pci/pci_config_access.h create mode 100644 os/src/drivers/pci/pci_device_component.h create mode 100644 os/src/drivers/pci/pci_device_config.h create mode 100644 os/src/drivers/pci/pci_session_component.h create mode 100644 os/src/drivers/pci/x86/target.mk create mode 100644 os/src/drivers/platform/gta01/main.cc create mode 100644 os/src/drivers/platform/gta01/target.mk create mode 100644 os/src/drivers/rtc/x86/main.cc create mode 100644 os/src/drivers/rtc/x86/target.mk create mode 100644 os/src/drivers/sd_card/host_driver.h create mode 100644 os/src/drivers/sd_card/pl180/main.cc create mode 100644 os/src/drivers/sd_card/pl180/pl180.h create mode 100644 os/src/drivers/sd_card/pl180/target.mk create mode 100644 os/src/drivers/sd_card/sd_card.h create mode 100644 os/src/drivers/timer/codezero/platform_timer.cc create mode 100644 os/src/drivers/timer/codezero/target.mk create mode 100644 os/src/drivers/timer/fiasco/platform_timer.cc create mode 100644 os/src/drivers/timer/fiasco/target.mk create mode 100644 os/src/drivers/timer/foc/target.mk create mode 100644 os/src/drivers/timer/foc/timer_root.h create mode 100644 os/src/drivers/timer/foc/timer_session_component.h create mode 100644 os/src/drivers/timer/include/timer_root.h create mode 100644 os/src/drivers/timer/include/timer_session_component.h create mode 100644 os/src/drivers/timer/include_periodic/platform_timer.h create mode 100644 os/src/drivers/timer/include_pit/platform_timer.h create mode 100644 os/src/drivers/timer/linux/platform_timer.cc create mode 100644 os/src/drivers/timer/linux/target.mk create mode 100644 os/src/drivers/timer/main.cc create mode 100644 os/src/drivers/timer/nova/target.mk create mode 100644 os/src/drivers/timer/nova/timer_root.h create mode 100644 os/src/drivers/timer/nova/timer_session_component.h create mode 100644 os/src/drivers/timer/okl4_arm/platform_timer.cc create mode 100644 os/src/drivers/timer/okl4_arm/target.mk create mode 100644 os/src/drivers/timer/okl4_x86/target.mk create mode 100644 os/src/drivers/timer/pistachio/platform_timer.cc create mode 100644 os/src/drivers/timer/pistachio/target.mk create mode 100644 os/src/drivers/uart/README create mode 100644 os/src/drivers/uart/i8250/i8250.h create mode 100644 os/src/drivers/uart/i8250/main.cc create mode 100644 os/src/drivers/uart/i8250/target.mk create mode 100755 os/src/drivers/uart/pl011/calc_brd_values.py create mode 100644 os/src/drivers/uart/pl011/main.cc create mode 100644 os/src/drivers/uart/pl011/pl011.h create mode 100644 os/src/drivers/uart/pl011/target.mk create mode 100644 os/src/drivers/uart/terminal_component.h create mode 100644 os/src/drivers/uart/terminal_driver.h create mode 100644 os/src/init/config.explicit_routing create mode 100644 os/src/init/config.wildcard create mode 100644 os/src/init/main.cc create mode 100644 os/src/init/target.mk create mode 100644 os/src/lib/alarm/alarm.cc create mode 100644 os/src/lib/blit/blit.cc create mode 100644 os/src/lib/blit/x86/blit.cc create mode 100644 os/src/lib/blit/x86/x86_32/mmx.h create mode 100644 os/src/lib/blit/x86/x86_64/mmx.h create mode 100644 os/src/lib/dde_kit/dde_kit.cc create mode 100644 os/src/lib/dde_kit/interrupt.cc create mode 100644 os/src/lib/dde_kit/lock.cc create mode 100644 os/src/lib/dde_kit/memory.cc create mode 100644 os/src/lib/dde_kit/panic.cc create mode 100644 os/src/lib/dde_kit/pci.cc create mode 100644 os/src/lib/dde_kit/pci_tree.cc create mode 100644 os/src/lib/dde_kit/pci_tree.h create mode 100644 os/src/lib/dde_kit/pgtab.cc create mode 100644 os/src/lib/dde_kit/printf.cc create mode 100644 os/src/lib/dde_kit/resources.cc create mode 100644 os/src/lib/dde_kit/semaphore.cc create mode 100644 os/src/lib/dde_kit/thread.cc create mode 100644 os/src/lib/dde_kit/thread.h create mode 100644 os/src/lib/dde_kit/timer.cc create mode 100644 os/src/lib/ldso/README create mode 100644 os/src/lib/ldso/arch/binary_name.cc create mode 100644 os/src/lib/ldso/arch/codezero/dummy.c create mode 100644 os/src/lib/ldso/arch/linux/binary_name.cc create mode 100644 os/src/lib/ldso/arch/linux/parent_cap.cc create mode 100644 os/src/lib/ldso/arch/parent_cap.cc create mode 100644 os/src/lib/ldso/arm/crt0.s create mode 100644 os/src/lib/ldso/arm/platform.c create mode 100644 os/src/lib/ldso/contrib/amd64/reloc.c create mode 100644 os/src/lib/ldso/contrib/amd64/rtld_machdep.h create mode 100644 os/src/lib/ldso/contrib/amd64/rtld_start.S create mode 100644 os/src/lib/ldso/contrib/arm/reloc.c create mode 100644 os/src/lib/ldso/contrib/arm/rtld_machdep.h create mode 100644 os/src/lib/ldso/contrib/arm/rtld_start.S create mode 100644 os/src/lib/ldso/contrib/debug.c create mode 100644 os/src/lib/ldso/contrib/debug.h create mode 100644 os/src/lib/ldso/contrib/i386/reloc.c create mode 100644 os/src/lib/ldso/contrib/i386/rtld_machdep.h create mode 100644 os/src/lib/ldso/contrib/i386/rtld_start.S create mode 100644 os/src/lib/ldso/contrib/libmap.c create mode 100644 os/src/lib/ldso/contrib/libmap.h create mode 100644 os/src/lib/ldso/contrib/malloc.c create mode 100644 os/src/lib/ldso/contrib/map_object.c create mode 100644 os/src/lib/ldso/contrib/rtld.c create mode 100644 os/src/lib/ldso/contrib/rtld.h create mode 100644 os/src/lib/ldso/contrib/rtld_lock.c create mode 100644 os/src/lib/ldso/contrib/rtld_lock.h create mode 100644 os/src/lib/ldso/contrib/rtld_tls.h create mode 100644 os/src/lib/ldso/contrib/xmalloc.c create mode 100644 os/src/lib/ldso/dl_extensions.h create mode 100644 os/src/lib/ldso/environ.cc create mode 100644 os/src/lib/ldso/err.cc create mode 100644 os/src/lib/ldso/file.cc create mode 100644 os/src/lib/ldso/file.h create mode 100644 os/src/lib/ldso/include/arm/call_main.h create mode 100644 os/src/lib/ldso/include/libc/dlfcn.h create mode 100644 os/src/lib/ldso/include/libc/elf-hints.h create mode 100644 os/src/lib/ldso/include/libc/libc-amd64/machine/elf.h create mode 100644 os/src/lib/ldso/include/libc/libc-arm/machine/asm.h create mode 100644 os/src/lib/ldso/include/libc/libc-arm/machine/elf.h create mode 100644 os/src/lib/ldso/include/libc/libc-i386/machine/elf.h create mode 100644 os/src/lib/ldso/include/libc/sys/cdefs.h create mode 100644 os/src/lib/ldso/include/libc/sys/elf.h create mode 100644 os/src/lib/ldso/include/libc/sys/elf32.h create mode 100644 os/src/lib/ldso/include/libc/sys/elf64.h create mode 100644 os/src/lib/ldso/include/libc/sys/elf_common.h create mode 100644 os/src/lib/ldso/include/libc/sys/elf_generic.h create mode 100644 os/src/lib/ldso/include/libc/sys/link_elf.h create mode 100644 os/src/lib/ldso/include/libc/sys/queue.h create mode 100644 os/src/lib/ldso/include/libc_emu/err.h create mode 100644 os/src/lib/ldso/include/libc_emu/errno.h create mode 100644 os/src/lib/ldso/include/libc_emu/fcntl.h create mode 100644 os/src/lib/ldso/include/libc_emu/ldso_types.h create mode 100644 os/src/lib/ldso/include/libc_emu/link.h create mode 100644 os/src/lib/ldso/include/libc_emu/machine/atomic.h create mode 100644 os/src/lib/ldso/include/libc_emu/machine/segments.h create mode 100644 os/src/lib/ldso/include/libc_emu/machine/sysarch.h create mode 100644 os/src/lib/ldso/include/libc_emu/stdio.h create mode 100644 os/src/lib/ldso/include/libc_emu/stdlib.h create mode 100644 os/src/lib/ldso/include/libc_emu/string.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/_types.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/ktrace.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/mman.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/mount.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/param.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/queue.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/stat.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/types.h create mode 100644 os/src/lib/ldso/include/libc_emu/sys/uio.h create mode 100644 os/src/lib/ldso/include/libc_emu/unistd.h create mode 100644 os/src/lib/ldso/include/x86_32/call_main.h create mode 100644 os/src/lib/ldso/include/x86_64/call_main.h create mode 100644 os/src/lib/ldso/ldso.ld create mode 100644 os/src/lib/ldso/ldso_types.c create mode 100644 os/src/lib/ldso/lock.cc create mode 100644 os/src/lib/ldso/main.c create mode 100644 os/src/lib/ldso/platform.c create mode 100644 os/src/lib/ldso/rtld_dummies.c create mode 100644 os/src/lib/ldso/startup/startup.cc create mode 100644 os/src/lib/ldso/startup/unwind_exidx.cc create mode 100644 os/src/lib/ldso/stdio.cc create mode 100644 os/src/lib/ldso/stdlib.cc create mode 100644 os/src/lib/ldso/string.cc create mode 100644 os/src/lib/ldso/symbol.map create mode 100644 os/src/lib/ldso/target.inc create mode 100644 os/src/lib/ldso/test.cc create mode 100644 os/src/lib/ldso/x86_32/crt0.s create mode 100644 os/src/lib/ldso/x86_32/linux/crt0.s create mode 100644 os/src/lib/ldso/x86_64/crt0.s create mode 100644 os/src/lib/ldso/x86_64/linux/crt0.s create mode 100644 os/src/lib/net/ethernet.cc create mode 100644 os/src/lib/net/ipv4.cc create mode 100644 os/src/lib/timed_semaphore/timed_semaphore.cc create mode 100644 os/src/lib/xev_track/xev_track.cc create mode 100644 os/src/platform/genode_dyn.ld create mode 100644 os/src/platform/genode_rel.ld create mode 100644 os/src/server/iso9660/README create mode 100644 os/src/server/iso9660/backing_store.h create mode 100644 os/src/server/iso9660/iso9660.cc create mode 100644 os/src/server/iso9660/iso9660.h create mode 100644 os/src/server/iso9660/main.cc create mode 100644 os/src/server/iso9660/target.mk create mode 100644 os/src/server/loader/README create mode 100644 os/src/server/loader/input_root.h create mode 100644 os/src/server/loader/input_session_component.cc create mode 100644 os/src/server/loader/input_session_component.h create mode 100644 os/src/server/loader/loader_child.h create mode 100644 os/src/server/loader/loader_root.h create mode 100644 os/src/server/loader/loader_session_component.cc create mode 100644 os/src/server/loader/loader_session_component.h create mode 100644 os/src/server/loader/loader_view_component.h create mode 100644 os/src/server/loader/main.cc create mode 100644 os/src/server/loader/nitpicker_root.h create mode 100644 os/src/server/loader/nitpicker_session_component.cc create mode 100644 os/src/server/loader/nitpicker_session_component.h create mode 100644 os/src/server/loader/nitpicker_view_component.h create mode 100644 os/src/server/loader/rom_root.h create mode 100644 os/src/server/loader/rom_session_component.cc create mode 100644 os/src/server/loader/rom_session_component.h create mode 100644 os/src/server/loader/tar_server_child.h create mode 100644 os/src/server/loader/target.mk create mode 100644 os/src/server/mixer/mixer.cc create mode 100644 os/src/server/mixer/target.mk create mode 100644 os/src/server/nic_bridge/address_node.cc create mode 100644 os/src/server/nic_bridge/address_node.h create mode 100644 os/src/server/nic_bridge/avl_safe.h create mode 100644 os/src/server/nic_bridge/component.cc create mode 100644 os/src/server/nic_bridge/component.h create mode 100644 os/src/server/nic_bridge/list_safe.h create mode 100644 os/src/server/nic_bridge/mac.cc create mode 100644 os/src/server/nic_bridge/mac.h create mode 100644 os/src/server/nic_bridge/main.cc create mode 100644 os/src/server/nic_bridge/packet_handler.cc create mode 100644 os/src/server/nic_bridge/packet_handler.h create mode 100644 os/src/server/nic_bridge/target.mk create mode 100644 os/src/server/nic_bridge/vlan.cc create mode 100644 os/src/server/nic_bridge/vlan.h create mode 100644 os/src/server/nic_loopback/main.cc create mode 100644 os/src/server/nic_loopback/target.mk create mode 100644 os/src/server/nit_fb/README create mode 100644 os/src/server/nit_fb/main.cc create mode 100644 os/src/server/nit_fb/target.mk create mode 100644 os/src/server/nitpicker/README create mode 100644 os/src/server/nitpicker/TODO create mode 100644 os/src/server/nitpicker/common/user_state.cc create mode 100644 os/src/server/nitpicker/common/view.cc create mode 100644 os/src/server/nitpicker/common/view_stack.cc create mode 100644 os/src/server/nitpicker/data/big_mouse.h create mode 100755 os/src/server/nitpicker/data/default.tff create mode 100644 os/src/server/nitpicker/genode/main.cc create mode 100644 os/src/server/nitpicker/genode/target.mk create mode 100644 os/src/server/nitpicker/include/background.h create mode 100644 os/src/server/nitpicker/include/chunky_menubar.h create mode 100644 os/src/server/nitpicker/include/clip_guard.h create mode 100644 os/src/server/nitpicker/include/draw_label.h create mode 100644 os/src/server/nitpicker/include/list.h create mode 100644 os/src/server/nitpicker/include/menubar.h create mode 100644 os/src/server/nitpicker/include/mode.h create mode 100644 os/src/server/nitpicker/include/mouse_cursor.h create mode 100644 os/src/server/nitpicker/include/session.h create mode 100644 os/src/server/nitpicker/include/string.h create mode 100644 os/src/server/nitpicker/include/user_state.h create mode 100644 os/src/server/nitpicker/include/view.h create mode 100644 os/src/server/nitpicker/include/view_stack.h create mode 100644 os/src/server/part_blk/README create mode 100644 os/src/server/part_blk/back_end.cc create mode 100644 os/src/server/part_blk/main.cc create mode 100644 os/src/server/part_blk/part_blk.h create mode 100644 os/src/server/part_blk/target.mk create mode 100644 os/src/server/rom_loopdev/README create mode 100644 os/src/server/rom_loopdev/main.cc create mode 100644 os/src/server/rom_loopdev/target.mk create mode 100644 os/src/server/rom_prefetcher/main.cc create mode 100644 os/src/server/rom_prefetcher/target.mk create mode 100644 os/src/server/tar_rom/README create mode 100755 os/src/server/tar_rom/main.cc create mode 100755 os/src/server/tar_rom/target.mk create mode 100644 os/src/test/ahci/main.cc create mode 100644 os/src/test/ahci/target.mk create mode 100644 os/src/test/alarm/main.cc create mode 100644 os/src/test/alarm/target.mk create mode 100644 os/src/test/audio_out/README create mode 100644 os/src/test/audio_out/main.cc create mode 100644 os/src/test/audio_out/target.mk create mode 100644 os/src/test/block/main.cc create mode 100644 os/src/test/block/target.mk create mode 100644 os/src/test/bomb/main.cc create mode 100644 os/src/test/bomb/target.mk create mode 100644 os/src/test/dde_kit/i8042.h create mode 100644 os/src/test/dde_kit/target.mk create mode 100644 os/src/test/dde_kit/test.cc create mode 100644 os/src/test/fb_block_adapter/main.cc create mode 100644 os/src/test/fb_block_adapter/target.mk create mode 100644 os/src/test/input/key_strings.h create mode 100644 os/src/test/input/target.mk create mode 100644 os/src/test/input/test.cc create mode 100644 os/src/test/iso/main.cc create mode 100644 os/src/test/iso/target.mk create mode 100644 os/src/test/iso/test.txt create mode 100644 os/src/test/loader/main.cc create mode 100644 os/src/test/loader/target.mk create mode 100644 os/src/test/nic_loopback/main.cc create mode 100644 os/src/test/nic_loopback/target.mk create mode 100644 os/src/test/nitpicker/target.mk create mode 100644 os/src/test/nitpicker/test.cc create mode 100644 os/src/test/packet_stream/main.cc create mode 100644 os/src/test/packet_stream/target.mk create mode 100644 os/src/test/part_blk/main.cc create mode 100644 os/src/test/part_blk/target.mk create mode 100644 os/src/test/pci/target.mk create mode 100644 os/src/test/pci/test.cc create mode 100644 os/src/test/rom_blk/main.cc create mode 100644 os/src/test/rom_blk/target.mk create mode 100644 os/src/test/signal/main.cc create mode 100644 os/src/test/signal/target.mk create mode 100644 os/src/test/terminal_echo/main.cc create mode 100644 os/src/test/terminal_echo/target.mk create mode 100644 os/src/test/timed_semaphore/main.cc create mode 100644 os/src/test/timed_semaphore/target.mk create mode 100644 os/src/test/timer/main.cc create mode 100644 os/src/test/timer/target.mk create mode 100644 os/src/test/timer_accuracy/main.cc create mode 100644 os/src/test/timer_accuracy/target.mk create mode 100644 os/src/test/uart/main.cc create mode 100644 os/src/test/uart/target.mk create mode 100644 os/src/test/xev_track/main.cc create mode 100644 os/src/test/xev_track/target.mk create mode 100755 os/tool/dde_kit_adapt_sources create mode 100755 os/tool/dde_kit_find_initcalls create mode 100644 ports-foc/Makefile create mode 100644 ports-foc/README create mode 100644 ports-foc/config/android_config.arm create mode 100644 ports-foc/config/android_config.x86_32 create mode 100644 ports-foc/config/linux_config.arm create mode 100644 ports-foc/config/linux_config.x86_32 create mode 100644 ports-foc/include/32-bit/l4/util/l4_macros.h create mode 100644 ports-foc/include/64-bit/l4/util/l4_macros.h create mode 100644 ports-foc/include/genode/block.h create mode 100644 ports-foc/include/genode/framebuffer.h create mode 100644 ports-foc/include/genode/input.h create mode 100644 ports-foc/include/genode/linkage.h create mode 100644 ports-foc/include/genode/net.h create mode 100644 ports-foc/include/genode/terminal.h create mode 100644 ports-foc/include/l4/io/io.h create mode 100644 ports-foc/include/l4/log/log.h create mode 100644 ports-foc/include/l4/re/c/dataspace.h create mode 100644 ports-foc/include/l4/re/c/debug.h create mode 100644 ports-foc/include/l4/re/c/mem_alloc.h create mode 100644 ports-foc/include/l4/re/c/namespace.h create mode 100644 ports-foc/include/l4/re/c/rm.h create mode 100644 ports-foc/include/l4/re/c/util/cap.h create mode 100644 ports-foc/include/l4/re/c/util/cap_alloc.h create mode 100644 ports-foc/include/l4/re/consts.h create mode 100644 ports-foc/include/l4/re/env.h create mode 100644 ports-foc/include/l4/util/atomic.h create mode 100644 ports-foc/include/l4/util/cpu.h create mode 100644 ports-foc/include/l4/util/kip.h create mode 100644 ports-foc/include/l4/util/kprintf.h create mode 100644 ports-foc/include/l4/util/util.h create mode 100644 ports-foc/lib/mk/l4lx.mk create mode 100644 ports-foc/lib/mk/l4sys.mk create mode 100644 ports-foc/mk/l4lx.mk create mode 100644 ports-foc/patches/l4android_genode.patch create mode 100644 ports-foc/patches/l4lx_genode.patch create mode 100644 ports-foc/run/l4android.run create mode 100644 ports-foc/run/l4linux.run create mode 100644 ports-foc/src/drivers/Makefile create mode 100644 ports-foc/src/drivers/genode_block.c create mode 100644 ports-foc/src/drivers/genode_fb.c create mode 100644 ports-foc/src/drivers/genode_net.c create mode 100644 ports-foc/src/drivers/genode_rtc.c create mode 100644 ports-foc/src/drivers/genode_serial.c create mode 100644 ports-foc/src/l4android/arm/target.mk create mode 100644 ports-foc/src/l4android/x86_32/target.mk create mode 100644 ports-foc/src/l4linux/arm/target.mk create mode 100644 ports-foc/src/l4linux/x86_32/target.mk create mode 100644 ports-foc/src/lib/l4lx/dataspace.cc create mode 100644 ports-foc/src/lib/l4lx/env.cc create mode 100644 ports-foc/src/lib/l4lx/genode_block.cc create mode 100644 ports-foc/src/lib/l4lx/genode_framebuffer.cc create mode 100644 ports-foc/src/lib/l4lx/genode_input.cc create mode 100644 ports-foc/src/lib/l4lx/genode_net.cc create mode 100644 ports-foc/src/lib/l4lx/genode_terminal.cc create mode 100644 ports-foc/src/lib/l4lx/include/dataspace.h create mode 100644 ports-foc/src/lib/l4lx/include/env.h create mode 100644 ports-foc/src/lib/l4lx/include/l4lx_irq.h create mode 100644 ports-foc/src/lib/l4lx/include/l4lx_memory.h create mode 100644 ports-foc/src/lib/l4lx/include/l4lx_task.h create mode 100644 ports-foc/src/lib/l4lx/include/l4lx_thread.h create mode 100644 ports-foc/src/lib/l4lx/include/linux.h create mode 100644 ports-foc/src/lib/l4lx/include/rm.h create mode 100644 ports-foc/src/lib/l4lx/include/task.h create mode 100644 ports-foc/src/lib/l4lx/include/vcpu.h create mode 100644 ports-foc/src/lib/l4lx/l4_io.cc create mode 100644 ports-foc/src/lib/l4lx/l4_log.cc create mode 100644 ports-foc/src/lib/l4lx/l4_re_c_dataspace.cc create mode 100644 ports-foc/src/lib/l4lx/l4_re_c_debug.cc create mode 100644 ports-foc/src/lib/l4lx/l4_re_c_mem_alloc.cc create mode 100644 ports-foc/src/lib/l4lx/l4_re_c_namespace.cc create mode 100644 ports-foc/src/lib/l4lx/l4_re_c_rm.cc create mode 100644 ports-foc/src/lib/l4lx/l4_re_c_util_cap.cc create mode 100644 ports-foc/src/lib/l4lx/l4_re_env.cc create mode 100644 ports-foc/src/lib/l4lx/l4_util_cpu.cc create mode 100644 ports-foc/src/lib/l4lx/l4_util_kip.cc create mode 100644 ports-foc/src/lib/l4lx/l4_util_util.cc create mode 100644 ports-foc/src/lib/l4lx/l4lx_irq.cc create mode 100644 ports-foc/src/lib/l4lx/l4lx_memory.cc create mode 100644 ports-foc/src/lib/l4lx/l4lx_task.cc create mode 100644 ports-foc/src/lib/l4lx/l4lx_thread.cc create mode 100644 ports-foc/src/lib/l4lx/rm.cc create mode 100644 ports-foc/src/lib/l4lx/startup.cc create mode 100644 ports-okl4/Makefile create mode 100644 ports-okl4/README create mode 100644 ports-okl4/config/elfweaver_config create mode 100644 ports-okl4/config/init_config create mode 100644 ports-okl4/config/linux_config create mode 100644 ports-okl4/include/oklx_kernel/oklx/ioctl.h create mode 100644 ports-okl4/include/oklx_lib/genode/audio.h create mode 100644 ports-okl4/include/oklx_lib/genode/block.h create mode 100644 ports-okl4/include/oklx_lib/genode/config.h create mode 100644 ports-okl4/include/oklx_lib/genode/exit.h create mode 100644 ports-okl4/include/oklx_lib/genode/framebuffer.h create mode 100644 ports-okl4/include/oklx_lib/genode/input.h create mode 100644 ports-okl4/include/oklx_lib/genode/lock.h create mode 100644 ports-okl4/include/oklx_lib/genode/memory.h create mode 100644 ports-okl4/include/oklx_lib/genode/net.h create mode 100644 ports-okl4/include/oklx_lib/genode/open.h create mode 100644 ports-okl4/include/oklx_lib/genode/printf.h create mode 100644 ports-okl4/include/oklx_lib/genode/sleep.h create mode 100644 ports-okl4/include/oklx_lib/iguana/eas.h create mode 100644 ports-okl4/include/oklx_lib/iguana/hardware.h create mode 100644 ports-okl4/include/oklx_lib/iguana/memsection.h create mode 100644 ports-okl4/include/oklx_lib/iguana/pd.h create mode 100644 ports-okl4/include/oklx_lib/iguana/stdint.h create mode 100644 ports-okl4/include/oklx_lib/iguana/thread.h create mode 100644 ports-okl4/include/oklx_lib/iguana/tls.h create mode 100644 ports-okl4/include/oklx_lib/iguana/types.h create mode 100644 ports-okl4/lib/mk/oklx.mk create mode 100644 ports-okl4/patches/oklx_genode.patch create mode 100644 ports-okl4/patches/unionfs.patch create mode 100644 ports-okl4/run/lx_block.run create mode 100644 ports-okl4/src/app/xev_track/bounding_box.h create mode 100644 ports-okl4/src/app/xev_track/main.cc create mode 100644 ports-okl4/src/app/xev_track/target.mk create mode 100644 ports-okl4/src/lib/oklx/genode/genode_audio.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_block.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_config.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_exit.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_framebuffer.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_input.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_lock.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_memory.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_net.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_open.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_printf.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_sleep.cc create mode 100644 ports-okl4/src/lib/oklx/genode/genode_threads.cc create mode 100644 ports-okl4/src/lib/oklx/iguana/iguana_eas.cc create mode 100644 ports-okl4/src/lib/oklx/iguana/iguana_hardware.cc create mode 100644 ports-okl4/src/lib/oklx/iguana/iguana_memsection.cc create mode 100644 ports-okl4/src/lib/oklx/iguana/iguana_pd.cc create mode 100644 ports-okl4/src/lib/oklx/iguana/iguana_thread.cc create mode 100644 ports-okl4/src/lib/oklx/iguana/iguana_tls.cc create mode 100644 ports-okl4/src/lib/oklx/include/oklx_memory_maps.h create mode 100644 ports-okl4/src/lib/oklx/include/oklx_screens.h create mode 100644 ports-okl4/src/lib/oklx/include/oklx_threads.h create mode 100644 ports-okl4/src/oklinux/main.cc create mode 100644 ports-okl4/src/oklinux/target.mk create mode 100644 ports/Makefile create mode 100644 ports/README create mode 100644 ports/doc/gdb.txt create mode 100644 ports/include/noux_session/capability.h create mode 100644 ports/include/noux_session/client.h create mode 100644 ports/include/noux_session/connection.h create mode 100644 ports/include/noux_session/noux_session.h create mode 100644 ports/include/noux_session/sysio.h create mode 100644 ports/lib/mk/fiasco_x86/gdbserver_platform.mk create mode 100644 ports/lib/mk/foc_arm/gdbserver_platform.mk create mode 100644 ports/lib/mk/foc_x86_32/gdbserver_platform.mk create mode 100644 ports/lib/mk/gdbserver_libc_support.mk create mode 100644 ports/lib/mk/gdbserver_platform.inc create mode 100644 ports/lib/mk/libc_noux.mk create mode 100644 ports/lib/mk/linux_x86_32/gdbserver_platform.mk create mode 100644 ports/lib/mk/okl4_x86/gdbserver_platform.mk create mode 100644 ports/lib/mk/pistachio_x86/gdbserver_platform.mk create mode 100644 ports/lib/mk/x86_32/gdbserver_platform_x86_32.inc create mode 100644 ports/mk/noux.mk create mode 100644 ports/ports/arora.mk create mode 100644 ports/ports/bash.mk create mode 100644 ports/ports/binutils.mk create mode 100644 ports/ports/coreutils.mk create mode 100644 ports/ports/dash.mk create mode 100644 ports/ports/findutils.mk create mode 100644 ports/ports/gcc.mk create mode 100644 ports/ports/gdb.mk create mode 100644 ports/ports/make.mk create mode 100644 ports/ports/vancouver.mk create mode 100644 ports/ports/vim.mk create mode 100644 ports/run/debug_nitpicker.run create mode 100644 ports/run/gdb_monitor.run create mode 100644 ports/run/noux.run create mode 100644 ports/run/noux_vim.run create mode 100644 ports/run/vancouver.run create mode 100644 ports/src/app/arora/arora.pro create mode 100644 ports/src/app/arora/arora_bookmarks.patch create mode 100644 ports/src/app/arora/arora_disable_adblock.patch create mode 100644 ports/src/app/arora/arora_disable_program_exit.patch create mode 100644 ports/src/app/arora/arora_genode.patch create mode 100644 ports/src/app/arora/arora_move_window.patch create mode 100644 ports/src/app/arora/arora_nitpicker_plugin.patch create mode 100644 ports/src/app/arora/arora_startpage.patch create mode 100644 ports/src/app/arora/demo/bg.png create mode 100644 ports/src/app/arora/demo/bg_content.png create mode 100644 ports/src/app/arora/demo/bg_top.png create mode 100644 ports/src/app/arora/demo/busybox.html create mode 100644 ports/src/app/arora/demo/demo.html create mode 100644 ports/src/app/arora/demo/http_block.png create mode 100644 ports/src/app/arora/demo/intro.html create mode 100644 ports/src/app/arora/demo/nitpicker.html create mode 100644 ports/src/app/arora/demo/nitpicker_plugin.png create mode 100644 ports/src/app/arora/demo/tetrix.html create mode 100644 ports/src/app/arora/demo/tinycore.html create mode 100644 ports/src/app/arora/demo/title_bg.png create mode 100644 ports/src/app/arora/demo_html.qrc create mode 100644 ports/src/app/arora/qwebplugins/nitpicker/nitpicker.pri create mode 100644 ports/src/app/arora/qwebplugins/nitpicker/nitpickerplugin.cpp create mode 100644 ports/src/app/arora/qwebplugins/nitpicker/nitpickerplugin.h create mode 100644 ports/src/app/arora/qwebplugins/nitpicker/nitpickerpluginwidget.cpp create mode 100644 ports/src/app/arora/qwebplugins/nitpicker/nitpickerpluginwidget.h create mode 100644 ports/src/app/arora/target.mk create mode 100644 ports/src/app/gdb_monitor/app_child.h create mode 100644 ports/src/app/gdb_monitor/append_list.h create mode 100644 ports/src/app/gdb_monitor/cpu_root.h create mode 100644 ports/src/app/gdb_monitor/cpu_session_component.cc create mode 100644 ports/src/app/gdb_monitor/cpu_session_component.h create mode 100644 ports/src/app/gdb_monitor/dataspace_object.h create mode 100644 ports/src/app/gdb_monitor/gdb_stub_thread.cc create mode 100644 ports/src/app/gdb_monitor/gdb_stub_thread.h create mode 100644 ports/src/app/gdb_monitor/gdbserver/config.h create mode 100644 ports/src/app/gdb_monitor/gdbserver/genode-low.cc create mode 100644 ports/src/app/gdb_monitor/gdbserver/genode-low.h create mode 100644 ports/src/app/gdb_monitor/gdbserver_genode.patch create mode 100644 ports/src/app/gdb_monitor/main.cc create mode 100644 ports/src/app/gdb_monitor/ram_root.h create mode 100644 ports/src/app/gdb_monitor/ram_session_component.cc create mode 100644 ports/src/app/gdb_monitor/ram_session_component.h create mode 100644 ports/src/app/gdb_monitor/rm_root.h create mode 100644 ports/src/app/gdb_monitor/rm_session_component.cc create mode 100644 ports/src/app/gdb_monitor/rm_session_component.h create mode 100644 ports/src/app/gdb_monitor/rom.h create mode 100644 ports/src/app/gdb_monitor/signal_handler_thread.cc create mode 100644 ports/src/app/gdb_monitor/signal_handler_thread.h create mode 100644 ports/src/app/gdb_monitor/target.mk create mode 100644 ports/src/app/gdb_monitor/thread_info.h create mode 100644 ports/src/lib/gdbserver_libc_support/elf/common.h create mode 100644 ports/src/lib/gdbserver_libc_support/gdbserver_libc_support.c create mode 100644 ports/src/lib/gdbserver_libc_support/gdbserver_libc_support.h create mode 100644 ports/src/lib/gdbserver_libc_support/sys/procfs.h create mode 100644 ports/src/lib/gdbserver_libc_support/sys/ptrace.h create mode 100644 ports/src/lib/gdbserver_libc_support/sys/vfs.h create mode 100644 ports/src/lib/gdbserver_platform/fiasco_x86_low.cc create mode 100644 ports/src/lib/gdbserver_platform/foc_arm_low.cc create mode 100644 ports/src/lib/gdbserver_platform/foc_x86_32_low.cc create mode 100644 ports/src/lib/gdbserver_platform/gdbserver_platform_helper.cc create mode 100644 ports/src/lib/gdbserver_platform/gdbserver_platform_helper.h create mode 100644 ports/src/lib/gdbserver_platform/i386.h create mode 100644 ports/src/lib/gdbserver_platform/linux_x86_32_low.cc create mode 100644 ports/src/lib/gdbserver_platform/okl4_x86_low.cc create mode 100644 ports/src/lib/gdbserver_platform/pistachio_x86_low.cc create mode 100644 ports/src/lib/gdbserver_platform/reg-arm.h create mode 100644 ports/src/lib/libc_noux/plugin.cc create mode 100644 ports/src/lib/libc_noux/target.mk create mode 100644 ports/src/noux-pkg/bash/build.patch create mode 100644 ports/src/noux-pkg/bash/target.mk create mode 100644 ports/src/noux-pkg/binutils/build.patch create mode 100644 ports/src/noux-pkg/binutils/target.mk create mode 100644 ports/src/noux-pkg/coreutils/target.mk create mode 100644 ports/src/noux-pkg/dash/build.patch create mode 100644 ports/src/noux-pkg/dash/target.mk create mode 100644 ports/src/noux-pkg/findutils/target.mk create mode 100644 ports/src/noux-pkg/gcc/build.patch create mode 100644 ports/src/noux-pkg/gcc/target.mk create mode 100644 ports/src/noux-pkg/make/target.mk create mode 100644 ports/src/noux-pkg/vim/target.mk create mode 100644 ports/src/noux/args.h create mode 100644 ports/src/noux/child.h create mode 100644 ports/src/noux/directory_service.h create mode 100644 ports/src/noux/dummy_input_io_channel.h create mode 100644 ports/src/noux/environment.h create mode 100644 ports/src/noux/file_descriptor_registry.h create mode 100644 ports/src/noux/file_io_service.h create mode 100644 ports/src/noux/file_system.h create mode 100644 ports/src/noux/io_channel.h create mode 100644 ports/src/noux/main.cc create mode 100644 ports/src/noux/path.h create mode 100644 ports/src/noux/pwd.h create mode 100644 ports/src/noux/range_checked_index.h create mode 100644 ports/src/noux/root_file_system.h create mode 100644 ports/src/noux/shared_pointer.h create mode 100644 ports/src/noux/signal_dispatcher.h create mode 100644 ports/src/noux/tar_file_system.h create mode 100644 ports/src/noux/target.mk create mode 100644 ports/src/noux/terminal_io_channel.h create mode 100644 ports/src/noux/vfs.h create mode 100644 ports/src/noux/vfs_handle.h create mode 100644 ports/src/noux/vfs_io_channel.h create mode 100644 ports/src/noux/wake_up_notifier.h create mode 100644 ports/src/test/gdb_monitor/main.cc create mode 100644 ports/src/test/gdb_monitor/target.mk create mode 100644 ports/src/vancouver/README create mode 100644 ports/src/vancouver/boot_module_provider.h create mode 100644 ports/src/vancouver/device_model_registry.cc create mode 100644 ports/src/vancouver/device_model_registry.h create mode 100644 ports/src/vancouver/main.cc create mode 100644 ports/src/vancouver/nova_user_env.cc create mode 100644 ports/src/vancouver/service/profile.h create mode 100644 ports/src/vancouver/target.mk create mode 100644 qt4/Makefile create mode 100644 qt4/README create mode 100644 qt4/include/genode/thread_qt.h create mode 100644 qt4/include/qnitpickerviewwidget/qnitpickerviewwidget.h create mode 100644 qt4/include/qpluginwidget/qpluginwidget.h create mode 100644 qt4/include/qt4/QtCore/private/qeventdispatcher_genode_p.h create mode 100644 qt4/include/qt4/QtCore/qconfig-genode.h create mode 100644 qt4/include/qt4/QtCore/qconfig.h create mode 100644 qt4/include/qt4/QtGui/private/qwindowsurface_nitpicker_qws_p.h create mode 100644 qt4/include/qt4/QtGui/qinputnitpicker_qws.h create mode 100644 qt4/include/qt4/QtGui/qkbdnitpicker_qws.h create mode 100644 qt4/include/qt4/QtGui/qkbdpc101_qws.h create mode 100644 qt4/include/qt4/QtGui/qmousenitpicker_qws.h create mode 100644 qt4/include/qt4/QtGui/qscreennitpicker_qws.h create mode 100644 qt4/lib/import/import-qt4.mk create mode 100644 qt4/lib/import/import-qt_core.mk create mode 100644 qt4/lib/import/import-qt_gui.mk create mode 100644 qt4/lib/import/import-qt_javascriptcore.mk create mode 100644 qt4/lib/import/import-qt_jscore.mk create mode 100644 qt4/lib/import/import-qt_network.mk create mode 100644 qt4/lib/import/import-qt_script.mk create mode 100644 qt4/lib/import/import-qt_scriptclassic.mk create mode 100644 qt4/lib/import/import-qt_scripttools.mk create mode 100644 qt4/lib/import/import-qt_svg.mk create mode 100644 qt4/lib/import/import-qt_ui_tools.mk create mode 100644 qt4/lib/import/import-qt_webcore.mk create mode 100644 qt4/lib/import/import-qt_xml.mk create mode 100644 qt4/lib/mk/dejavusans.mk create mode 100644 qt4/lib/mk/qgif.mk create mode 100644 qt4/lib/mk/qjpeg.mk create mode 100644 qt4/lib/mk/qnitpickerviewwidget.mk create mode 100644 qt4/lib/mk/qpluginwidget.mk create mode 100644 qt4/lib/mk/qt_core.mk create mode 100644 qt4/lib/mk/qt_gui.mk create mode 100644 qt4/lib/mk/qt_jscore.mk create mode 100644 qt4/lib/mk/qt_network.mk create mode 100644 qt4/lib/mk/qt_script.mk create mode 100644 qt4/lib/mk/qt_script46.mk create mode 100644 qt4/lib/mk/qt_scriptclassic.mk create mode 100644 qt4/lib/mk/qt_scripttools.mk create mode 100644 qt4/lib/mk/qt_svg.mk create mode 100644 qt4/lib/mk/qt_ui_tools.mk create mode 100644 qt4/lib/mk/qt_webcore.mk create mode 100644 qt4/lib/mk/qt_xml.mk create mode 100644 qt4/run/qt4.run create mode 100644 qt4/src/app/examples/calculatorform/calculatorform.cpp create mode 100644 qt4/src/app/examples/calculatorform/calculatorform.h create mode 100644 qt4/src/app/examples/calculatorform/calculatorform.pro create mode 100644 qt4/src/app/examples/calculatorform/calculatorform.ui create mode 100644 qt4/src/app/examples/calculatorform/font.qrc create mode 100644 qt4/src/app/examples/calculatorform/main.cpp create mode 100644 qt4/src/app/examples/calculatorform/target.mk create mode 100644 qt4/src/app/examples/previewer/target.mk create mode 100644 qt4/src/app/examples/tetrix/font.qrc create mode 100644 qt4/src/app/examples/tetrix/main.cpp create mode 100644 qt4/src/app/examples/tetrix/target.mk create mode 100644 qt4/src/app/examples/tetrix/tetrix.pro create mode 100644 qt4/src/app/examples/tetrix/tetrix.qrc create mode 100644 qt4/src/app/examples/tetrix/tetrixboard.cpp create mode 100644 qt4/src/app/examples/tetrix/tetrixboard.h create mode 100644 qt4/src/app/examples/tetrix/tetrixboard.js create mode 100644 qt4/src/app/examples/tetrix/tetrixpiece.js create mode 100644 qt4/src/app/examples/tetrix/tetrixwindow.js create mode 100644 qt4/src/app/examples/tetrix/tetrixwindow.ui create mode 100644 qt4/src/app/examples/textedit/target.mk create mode 100644 qt4/src/app/qt_launchpad/child_entry.cpp create mode 100644 qt4/src/app/qt_launchpad/child_entry.h create mode 100644 qt4/src/app/qt_launchpad/child_entry.ui create mode 100644 qt4/src/app/qt_launchpad/font.qrc create mode 100644 qt4/src/app/qt_launchpad/kbyte_loadbar.cpp create mode 100644 qt4/src/app/qt_launchpad/kbyte_loadbar.h create mode 100644 qt4/src/app/qt_launchpad/launch_entry.cpp create mode 100644 qt4/src/app/qt_launchpad/launch_entry.h create mode 100644 qt4/src/app/qt_launchpad/launch_entry.ui create mode 100644 qt4/src/app/qt_launchpad/main.cpp create mode 100644 qt4/src/app/qt_launchpad/qt_launchpad.cpp create mode 100644 qt4/src/app/qt_launchpad/qt_launchpad.h create mode 100644 qt4/src/app/qt_launchpad/qt_launchpad.pro create mode 100644 qt4/src/app/qt_launchpad/qt_launchpad.ui create mode 100644 qt4/src/app/qt_launchpad/target.mk create mode 100644 qt4/src/app/tmpl/target.mk.example create mode 100644 qt4/src/lib/dejavusans/dejavusans.qrc create mode 100644 qt4/src/lib/qnitpickerviewwidget/qnitpickerviewwidget.cpp create mode 100644 qt4/src/lib/qpluginwidget/qpluginwidget.cpp create mode 100644 qt4/src/lib/qt4/mkspecs/genode-g++/qmake.conf create mode 100644 qt4/src/lib/qt4/mkspecs/genode-g++/qplatformdefs.h create mode 100644 qt4/src/lib/qt4/mkspecs/qws/genode-x86-g++/qmake.conf create mode 100644 qt4/src/lib/qt4/mkspecs/qws/genode-x86-g++/qplatformdefs.h create mode 100644 qt4/src/lib/qt4/previewer_example.patch create mode 100644 qt4/src/lib/qt4/qt4_genode.patch create mode 100644 qt4/src/lib/qt4/qt4_include_time_h.patch create mode 100644 qt4/src/lib/qt4/qt4_lwip_connect_semantics_adaption.patch create mode 100644 qt4/src/lib/qt4/qt4_no_exit_on_window_close.patch create mode 100644 qt4/src/lib/qt4/qt4_no_search_for_resolv_lib.patch create mode 100644 qt4/src/lib/qt4/qt4_no_separate_host_lookup_threads.patch create mode 100644 qt4/src/lib/qt4/qt4_nonblocking_sockets.patch create mode 100644 qt4/src/lib/qt4/qt4_renderwidget.patch create mode 100644 qt4/src/lib/qt4/qt4_virtual_deletelater.patch create mode 100644 qt4/src/lib/qt4/src/corelib/global/qconfig.cpp create mode 100644 qt4/src/lib/qt4/src/corelib/io/qprocess_genode.cpp create mode 100644 qt4/src/lib/qt4/src/corelib/kernel/qeventdispatcher_genode.cpp create mode 100644 qt4/src/lib/qt4/src/corelib/thread/qmutex_genode.cpp create mode 100644 qt4/src/lib/qt4/src/corelib/thread/qthread_genode.cpp create mode 100644 qt4/src/lib/qt4/src/corelib/thread/qwaitcondition_genode.cpp create mode 100644 qt4/src/lib/qt4/src/gui/embedded/qinputnitpicker_qws.cpp create mode 100644 qt4/src/lib/qt4/src/gui/embedded/qkbdnitpicker_qws.cpp create mode 100644 qt4/src/lib/qt4/src/gui/embedded/qkbdpc101_qws.cpp create mode 100644 qt4/src/lib/qt4/src/gui/embedded/qmousenitpicker_qws.cpp create mode 100644 qt4/src/lib/qt4/src/gui/embedded/qscreennitpicker_qws.cpp create mode 100644 qt4/src/lib/qt4/src/gui/painting/qwindowsurface_nitpicker_qws.cpp create mode 100644 qt4/src/lib/qt4/textedit_example.patch create mode 100644 qt4/src/lib/qt_main/qt_main.cc create mode 100644 qt4/tool/Makefile create mode 100644 qt4/tool/qmake/Makefile create mode 100644 tool/README create mode 100755 tool/autopilot create mode 100755 tool/beautify create mode 100644 tool/boot/chain.c32 create mode 100644 tool/boot/isolinux.bin create mode 100644 tool/boot/isolinux.cfg create mode 100644 tool/boot/stage2_eltorito create mode 100644 tool/builddir/build.mk create mode 100644 tool/builddir/etc/README create mode 100644 tool/builddir/etc/build.conf.codezero_vpb926 create mode 100644 tool/builddir/etc/build.conf.drivers_x86 create mode 100644 tool/builddir/etc/build.conf.fiasco_x86 create mode 100644 tool/builddir/etc/build.conf.foc_pbxa9 create mode 100644 tool/builddir/etc/build.conf.foc_vea9x4 create mode 100644 tool/builddir/etc/build.conf.foc_x86_32 create mode 100644 tool/builddir/etc/build.conf.foc_x86_64 create mode 100644 tool/builddir/etc/build.conf.generic create mode 100644 tool/builddir/etc/build.conf.linux_x86 create mode 100644 tool/builddir/etc/build.conf.mb_ml507 create mode 100644 tool/builddir/etc/build.conf.mb_s3a_starter_kit create mode 100644 tool/builddir/etc/build.conf.nova_x86 create mode 100644 tool/builddir/etc/build.conf.okl4_x86 create mode 100644 tool/builddir/etc/build.conf.pistachio_x86 create mode 100644 tool/builddir/etc/build.conf.ports-foc create mode 100644 tool/builddir/etc/build.conf.ports-okl4 create mode 100644 tool/builddir/etc/build.conf.qemu_no_kvm create mode 100755 tool/create_builddir create mode 100755 tool/create_iso create mode 100755 tool/fix_include_ifndef create mode 100644 tool/libgcc_libc_stub.h create mode 100755 tool/parse_cxx create mode 100755 tool/run create mode 100755 tool/tool_chain diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d8cf7d463 --- /dev/null +++ b/LICENSE @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/README b/README index 042bbace8..a0d913f21 100644 --- a/README +++ b/README @@ -1,4 +1,180 @@ -Genode OS Framework -This is just the initial version without any code yet. We're on the -way to migrate the current released version into Git. + ================================= + Genode Operating System Framework + ================================= + + +This is the source tree of the reference implementation of the Genode OS +architecture. For a general overview about the architecture, please refer to +the project's official website: + +:Official project website for the Genode OS Framework: + + [http://genode.org/documentation/general-overview] + +The current implementation can be compiled for 8 different kernels: Linux, +L4ka::Pistachio, L4/Fiasco, OKL4, NOVA, Fiasco.OC, Codezero, and a custom +kernel for the MicroBlaze architecture. Whereas the Linux version serves us as +development vehicle and enables us to rapidly develop the generic parts of the +system, the actual target platforms of the framework are microkernels. There +is no "perfect" microkernel - and neither should there be one. If a microkernel +pretended to be fit for all use cases, it wouldn't be "micro". Hence, all +microkernels differ in terms of their respective features, complexity, and +supported hardware architectures. + +Genode allows the use of each of the kernels listed above with a rich set of +device drivers, protocol stacks, libraries, and applications in a uniform way. +For developers, the framework provides an easy way to target multiple different +kernels instead of tying the development to a particular kernel technology. For +kernel developers, Genode contributes advanced workloads, stress-testing their +kernel, and enabling a variety of application use cases that would not be +possible otherwise. For users and system integrators, it enables the choice of +the kernel that fits best with the requirements at hand for the particular +usage scenario. + + +Directory overview +################## + +The Genode source tree is composed of the following subdirectories: + +:'doc': + + This directory contains general documentation. Please consider the following + document for a quick guide to get started with the framework: + + ! doc/getting_started.txt + + If you are curious about the ready-to-use components that come with the + framework, please review the components overview: + + ! doc/components.txt + +:'base': + + This directory contains the source-code repository of the fundamental + frameworks and interfaces of Genode. Furthermore, it contains the generic + parts of core. + +:'os': + + This directory contains the non-base OS components such as the init process, + device drivers, and basic system services. + +:'demo': + + This directory contains the source-code repository of various services and + applications that we use for demonstration purposes. For example, a graphical + application launcher called Launchpad and the Scout tutorial browser. + +:'base-': + These directories contain platform-specific source-code repositories + complementing the 'base' repository. The following platforms are supported: + + :'linux': + Linux kernel (both x86_32 and x86_64) + + :'pistachio': + L4ka::Pistachio kernel developed at University of Karlsruhe. + See [http://genode.org/community/wiki/GenodeOnL4kaPistachio] + + :'fiasco': + L4/Fiasco kernel developed at University of Technology Dresden. + See [http://genode.org/community/wiki/GenodeOnL4Fiasco] + + :'foc': + Fiasco.OC is a modernized version of the Fiasco microkernel with a + completely revised kernel interface fostering capability-based + security. It is not compatible with L4/Fiasco. + See [http://genode.org/community/wiki/GenodeOnFiascoOC] + + :'okl4': + OKL4 kernel (x86_32 and ARM) developed at Open-Kernel-Labs. + See [http://genode.org/community/wiki/GenodeOnOKL4] + + :'nova': + NOVA hypervisor developed at University of Technology Dresden + See [http://genode.org/community/wiki/GenodeOnNOVA] + + :'codezero': + Codezero microkernel developed by B-Labs + See [http://genode.org/community/wiki/GenodeOnCodezero] + + :'mb': + Support for running Genode natively on the MicroBlaze softcore CPU. + See [http://genode.org/community/wiki/GenodeOnMicroBlaze] + + :'host': + Pseudo platform documenting the interface between the generic and + platform-specific parts of the base framework. This is not a functional + base platform. + +:'tool': + + Source-code management tools and scripts. Please refer to the README file + contained in the directory. + +:'hello_tutorial': + + Tutorial for creating a simple client-server scenario with Genode. This + repository includes documentation and the complete source code. + +:'libports': + + This source-code repository contains ports of popular open-source libraries + to Genode, most importantly the C library. The repository contains no + upstream source code but means to download the code and adapt it to Genode. + For instructions about how to use this mechanism, please consult the README + file at the top level of the repository. + +:'linux_drivers': + + This source-code repository contains the device driver environment for + executing Linux device drivers natively on Genode. + +:'dde_ipxe': + + This source-code repository contains the device driver environment for + executing drivers of the iPXE project. + +:'qt4': + + This source-code repository contains the Genode version of Qt4 framework. + Please find more information about using Qt4 with Genode in the repository's + 'README' file. + +:'ports': + + This source-code repository hosts ports of 3rd-party applications to + Genode. The repository does not contain upstream source code but provides + a mechanism for downloading the official source distributions and adapt + them to the Genode environment. The used mechanism is roughly the same + as used for the 'libports' repository. Please consult 'libports/README' + for further information. + +:'ports-': + + These platform-specific source-code repositories contain software that + capitalizes special features of the respective kernel platform. I.e., + for the OKL4 base platform, a port of OKLinux is provided in 'ports-okl4'. + For the Fiasco.OC platform, 'ports-foc' hosts a port of the L4Linux + kernel. For further information, please refer to the README file at the + top level of the respective repository. + +:'gems': + + This source-code repository contains Genode applications that use + both native Genode interfaces as well as features of other high-level + repositories, in particular shared libraries provided by 'libports'. + + +Contact +####### + +The best way to get in touch with Genode developers and users is the project's +mailing list. Please feel welcome to join in! + +:Genode Mailing Lists: + + [http://genode.org/community/mailing-lists] + diff --git a/base-codezero/Makefile b/base-codezero/Makefile new file mode 100644 index 000000000..f04ea5e7d --- /dev/null +++ b/base-codezero/Makefile @@ -0,0 +1,44 @@ +# +# \brief Download and prepare the Codezero kernel +# \author Norman Feske +# \date 2011-08-05 +# + +VERBOSE ?= @ +ECHO = @echo +GIT_URL = git://git.l4dev.org/codezero.git +GIT_REV = 6fa4884a5a1cf6207372f69ae01e5faa6d5a39c8 +CONTRIB_DIR = contrib +PATCHES = $(shell find patches -name *.patch) + +# +# Print help information by default +# +help:: + +prepare: $(CONTRIB_DIR) + +help:: + $(ECHO) + $(ECHO) "Check out upstream source code of the Codezero kernel" + $(ECHO) + $(ECHO) "The source code will be located at the '$(CONTRIB_DIR)/' directory." + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - checkout upstream source codes" + $(ECHO) "clean - remove upstream source codes" + $(ECHO) + +$(CONTRIB_DIR)/.git: + $(VERBOSE)git clone $(GIT_URL) $(CONTRIB_DIR) + +$(CONTRIB_DIR): $(CONTRIB_DIR)/.git + $(VERBOSE)cd $(CONTRIB_DIR); git reset --hard $(GIT_REV) + $(ECHO) "applying patches to '$(CONTRIB_DIR)/'" + $(VERBOSE)for i in $(PATCHES); do patch -d $@ -p1 < $$i; done + +.PHONY: $(CONTRIB_DIR) + +clean:: + $(VERBOSE)rm -rf $(CONTRIB_DIR) + diff --git a/base-codezero/README b/base-codezero/README new file mode 100644 index 000000000..c2d8da087 --- /dev/null +++ b/base-codezero/README @@ -0,0 +1,3 @@ +This repository contains the port of Genode to the Codezero microkernel +For instructions about using Genode with Codezero, please refer to +'doc/codezero.txt'. diff --git a/base-codezero/config/vpb926.cml b/base-codezero/config/vpb926.cml new file mode 100644 index 000000000..2629c72d0 --- /dev/null +++ b/base-codezero/config/vpb926.cml @@ -0,0 +1,240 @@ +# +# Automatically generated, don't edit +# +# Generated on: furnace +# At: Fri, 05 Aug 2011 21:48:00 +0000 +# Linux version 2.6.32-33-generic (buildd@rothera) (gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) ) #70-Ubuntu SMP Thu Jul 7 21:09:46 UTC 2011 + +# +# Codezero Microkernel Configurator +# + +# +# Main architecture +# +CONFIG_ARCH_ARM=y + + +# +# ARM Architecture Configuration +# + +# +# ARM Platform Type +# +CONFIG_PLATFORM_PB926=y +CONFIG_PLATFORM_PBA9=n +CONFIG_PLATFORM_BEAGLE=n +CONFIG_PLATFORM_EB=n + + +# +# ARM Processor Type +# +CONFIG_CPU_ARM926=y + + + +# +# Generic Processor Properties +# +CONFIG_ICACHE_DISABLE=n +CONFIG_DCACHE_DISABLE=n + + +# +# Generic Kernel Properties +# +CONFIG_PREEMPT_DISABLE=n +CONFIG_DEBUG_ACCOUNTING=n +CONFIG_DEBUG_SPINLOCKS=n +CONFIG_SCHED_TICKS=1000 + + +# +# Toolchain Prefix +# +CONFIG_TOOLCHAIN_USERSPACE="arm-none-linux-gnueabi-" +CONFIG_TOOLCHAIN_KERNEL="arm-none-eabi-" + + +# +# Container Setup +# +CONFIG_CAPABILITIES=n +CONFIG_CONTAINERS=1 + +# +# Container 0 Parameters +# + +# +# Container 0 Type +# +CONFIG_CONT0_TYPE_BAREMETAL=y +CONFIG_CONT0_TYPE_POSIX=n +CONFIG_CONT0_TYPE_LINUX=n + + +# +# Container 0 Options +# +CONFIG_CONT0_OPT_NAME="empty0" + +# +# Baremetal Project Type +# +CONFIG_CONT0_BAREMETAL_PROJ_EMPTY=y +CONFIG_CONT0_BAREMETAL_PROJ_HELLO_WORLD=n +CONFIG_CONT0_BAREMETAL_PROJ_THREADS_DEMO=n +CONFIG_CONT0_BAREMETAL_PROJ_TEST_SUITE=n +CONFIG_CONT0_BAREMETAL_PROJ_UART_SERVICE=n +CONFIG_CONT0_BAREMETAL_PROJ_TIMER_SERVICE=n +CONFIG_CONT0_BAREMETAL_PROJ_KMI_SERVICE=n +CONFIG_CONT0_BAREMETAL_PROJ_MUTEX_DEMO=n +CONFIG_CONT0_BAREMETAL_PROJ_IPC_DEMO=n + + +# +# Container 0 Pager Linker Parameters +# +CONFIG_CONT0_PAGER_LMA=0x40000 +CONFIG_CONT0_PAGER_VMA=0x100000 + + +# +# Container 0 Pager Physical Memory Regions (Capabilities) +# +CONFIG_CONT0_PAGER_PHYSMEM_REGIONS=1 +CONFIG_CONT0_PAGER_PHYS0_START=0x40000 +CONFIG_CONT0_PAGER_PHYS0_END=0x4000000 + + +# +# Container 0 Pager Virtual Memory Regions (Capabilities) +# +CONFIG_CONT0_PAGER_VIRTMEM_REGIONS=1 +CONFIG_CONT0_PAGER_VIRT0_START=0x0 +CONFIG_CONT0_PAGER_VIRT0_END=0x50000000 + + +# +# Container 0 Pager Capabilities +# + +# +# Container 0 Thread Pool Capability +# +CONFIG_CONT0_PAGER_CAP_THREADPOOL_USE=y +CONFIG_CONT0_PAGER_CAP_THREADPOOL_SIZE=64 + + +# +# Container 0 Space Pool Capability +# +CONFIG_CONT0_PAGER_CAP_SPACEPOOL_USE=y +CONFIG_CONT0_PAGER_CAP_SPACEPOOL_SIZE=64 + + +# +# Container 0 Mutex Pool Capability +# +CONFIG_CONT0_PAGER_CAP_MUTEXPOOL_USE=y +CONFIG_CONT0_PAGER_CAP_MUTEXPOOL_SIZE=100 + + +# +# Container 0 Map Pool Capability +# +CONFIG_CONT0_PAGER_CAP_MAPPOOL_USE=y +CONFIG_CONT0_PAGER_CAP_MAPPOOL_SIZE=800 + + +# +# Container 0 IPC Capability +# +CONFIG_CONT0_PAGER_CAP_IPC_USE=y +CONFIG_CONT0_PAGER_CAP_IPC_TARGET_CURRENT_CONTAINER=y +CONFIG_CONT0_PAGER_CAP_IPC_TARGET_CURRENT_PAGER_SPACE=n +CONFIG_CONT0_PAGER_CAP_IPC_TARGET_OTHER_CONTAINER=n +CONFIG_CONT0_PAGER_CAP_IPC_TARGET_OTHER_PAGER=n + + +# +# Container 0 IRQ Control Capability +# +CONFIG_CONT0_PAGER_CAP_IRQCTRL_USE=y + + +# +# Container 0 Custom Capability 0 Parameters +# +CONFIG_CONT0_PAGER_CAP_CUSTOM0_USE=n + + +# +# Container 0 Custom Capability 1 Parameters +# +CONFIG_CONT0_PAGER_CAP_CUSTOM1_USE=n + + +# +# Container 0 Custom Capability 2 Parameters +# +CONFIG_CONT0_PAGER_CAP_CUSTOM2_USE=n + + +# +# Container 0 Custom Capability 3 Parameters +# +CONFIG_CONT0_PAGER_CAP_CUSTOM3_USE=n + + + +# +# Container 0 Global Capabilities +# + +# +# Container 0 IPC Capability +# +CONFIG_CONT0_CAP_IPC_USE=y +CONFIG_CONT0_CAP_IPC_TARGET_CURRENT_CONTAINER=y +CONFIG_CONT0_CAP_IPC_TARGET_CURRENT_PAGER_SPACE=n +CONFIG_CONT0_CAP_IPC_TARGET_OTHER_CONTAINER=n +CONFIG_CONT0_CAP_IPC_TARGET_OTHER_PAGER=n + + +# +# Container 0 Mutex Pool Capability +# +CONFIG_CONT0_CAP_MUTEXPOOL_USE=y +CONFIG_CONT0_CAP_MUTEXPOOL_SIZE=100 + + + + + + +# +# Derived symbols +# +CONFIG_CONT3_START_PC_ADDR=0xd0000000 +CONFIG_DEBUG_PERFMON_KERNEL=n +CONFIG_CONT1_PAGER_LOAD_ADDR=0x1100000 +CONFIG_CONT2_START_PC_ADDR=0xc0000000 +CONFIG_CONT2_PAGER_VIRT_ADDR=0xc0000000 +CONFIG_RAM_BASE_PLAT=0 +CONFIG_CONT2_PAGER_LOAD_ADDR=0x2100000 +CONFIG_CONT1_PAGER_VIRT_ADDR=0xb0000000 +CONFIG_CONT3_PAGER_LOAD_ADDR=0x3100000 +CONFIG_SUBARCH_V5=y +CONFIG_SUBARCH_V7=n +CONFIG_SUBARCH_V6=n +CONFIG_CONT0_PAGER_LOAD_ADDR=0x40000 +CONFIG_CONT0_PAGER_VIRT_ADDR=0x100000 +CONFIG_CONT3_PAGER_VIRT_ADDR=0xd0000000 +CONFIG_CONT0_START_PC_ADDR=0x100000 +CONFIG_CONT1_START_PC_ADDR=0xb0000000 +# +# That's all, folks! diff --git a/base-codezero/doc/codezero.txt b/base-codezero/doc/codezero.txt new file mode 100644 index 000000000..30bd482a6 --- /dev/null +++ b/base-codezero/doc/codezero.txt @@ -0,0 +1,274 @@ + + ================================== + Genode on the Codezero microkernel + ================================== + + + Norman Feske + + +Codezero is a microkernel primarily targeted at ARM-based embedded systems. +It is developed by the British company B-Labs. + +:B-Labs website: + + [http://b-labs.co.uk] + +The Codezero kernel was first made publicly available in summer 2009. The +latest version, documentation, and community resources are available at the +project website: + +:Codezero project website: + + [http://l4dev.org] + +As highlighted by the name of the project website, the design of the kernel is +closely related to the family of L4 microkernels. In short, the kernel provides +a minimalistic set of functionality for managing address spaces, threads, and +communication between threads, but leaves complicated policy and device access +to user-level components. + + +Using Genode with Codezero +########################## + +For using Codezero, please ensure to have Git, SCons, and Python installed as +these tools are required for downloading and building the kernel. Furthermore, +you will need to install the tool chain for ARM. For instructions on how to +download and install the tool chain, please refer to: + +:[http://genode.org/download/tool-chain]: + Genode tool-chain + +To download the Codezero kernel and integrate it with Genode, issue + +! make prepare + +from the 'base-codezero/' directory. The Codezero kernel is fully supported by +Genode's run mechanism. Therefore, you can run Genode scenarios using Qemu +directly from the build directory. For a quick test, let's create a build +directory for Codezero on the VersatilePB926 platform using Genode's +'create_builddir' tool: + +! /tool/create_builddir codezero_vpb926 BUILD_DIR= + +To execute the graphical Genode demo, change to the new created build directory +and issue: + +! make run/demo + + +Characteristics of the kernel +############################# + +To put Codezero in relation to other L4 kernels, here is a quick summary on the +most important design aspects as implemented with the version 0.3, and on how +our port of Genode relates to them: + +* In the line of the original L4 interface, the kernel uses global name spaces + for kernel objects such as threads and address spaces. + +* For the interaction between a user thread and the kernel, the concept of + user-level thread-control blocks (UTCB) is used. A UTCB is a small + thread-specific region in the thread's virtual address space, which is + always mapped. Hence the access to the UTCB can never raise a page fault, + which makes it perfect for the kernel to access system-call arguments, + in particular IPC payload copied from/to user threads. In contrast to other + L4 kernels, the location of UTCBs within the virtual address space is managed + by the user land. + + On Genode, core keeps track of the UTCB locations for all user threads. + This way, the physical backing store for the UTCB can be properly accounted + to the corresponding protection domain. + +* The kernel provides three kinds of synchronous inter-process communication + (IPC): Short IPC carries payload in CPU registers only. Full IPC copies + message payload via the UTCBs of the communicating parties. Extended IPC + transfers a variable-sized message from/to arbitrary locations of the + sender/receiver address spaces. During an extended IPC, page fault may + occur. + + Genode solely relies on extended IPC, leaving the other IPC mechanisms to + future optimizations. + +* The scheduling of threads is based on hard priorities. Threads with the + same priority are executed in a round-robin fashion. The kernel supports + time-slice-based preemption. + + Genode does not support Codezero priorities yet. + +* The original L4 interface leaves open the question on how to manage + and account kernel resources such as the memory used for page tables. + Codezero makes the accounting of such resources explicit, enables the + user-land to manage them in a responsible way, and prevent kernel-resource + denial-of-service problems. + +* In contrast to the original L4.v2 and L4.x0 interfaces, the kernel provides + no time source in the form of IPC timeouts to the user land. A time source + must be provided by a user-space timer driver. Genode employs such a timer + services on all platforms so that it is not effected by this limitation. + +In several ways, Codezero goes beyond the known L4 interfaces. The most +noticeable addition is the support for so-called containers. A container is +similar to a virtual machine. It is an execution environment that holds a set +of physical resources such as RAM and devices. The number of containers and the +physical resources assigned to them is static and is to be defined at build +time. The code executed inside a container can be roughly classified into two +cases. First, there are static programs that require strong isolation from the +rest of the system but no classical operating-system infrastructure, for +example special-purpose telecommunication stacks or cryptographic functionality +of an embedded device. Second, there a kernel-like workload, which use the L4 +interface to substructure the container into address spaces, for example a +paravirtualized Linux kernel that uses Codezero address spaces to protect Linux +processes. Genode runs inside a container and facilitates Codezero's L4 +interface to implement its multi-server architecture. + + +Behind the scenes +################# + +The 'make prepare' mechanism checks out the kernel source code from the +upstream Git repository to 'base-codezero/contrib'. When building the kernel +from within a Genode build directory via 'make kernel', this directory won't be +touched by the Genode build system. Instead, a snapshot of the 'contrib' +directory is taken to '/kernel/codezero'. This is the place where +the Codezero configuration and build processes are executed. By working with a +build-directory-local snapshot, we ensure that the source tree remains +untouched at all times. After having taken the snapshot, the Codezero kernel is +configured using a configuration template specific for the hardware platform. +The configuration comes in the form of a CML file located at +'base-codezero/config/'. There is one CML file per supported platform named +'.cml'. The configured Codezero build directory will reside at +'/kernel/codezero/build/'. Finally, the Codezero build system is +invoked to build the kernel. + +The two stages of building Codezero +=================================== + +The Codezero build system always performs the compilation of the kernel and the +so-called containers as well as the integration of all these components into a +final ELF image as one operation. When building just the kernel via 'make +kernel', the final image will contain the default container0 that comes with +the Codezero distribution. For integrating Genode into the final image, the +content of the container0 must be replaced by the Genode binaries followed by +another execution of 'kernel/codezero/build.py'. Now, the single-image will be +re-created, including the Genode binaries. When using Genode's run mechanism, +these steps are automated for you. For reference, please review the Codezero +run environment at 'base-codezero/run/env'. + +By first building the kernel with Codezero's default container ('make kernel') +and later replacing the container's content with Genode binaries, we +optimize the work flow for building Genode components. The kernel is compiled +only once, but the (quick) re-linking of the final image is done every time a +run script is executed. + +In the run environment, you will see that we forcefully remove a file called +'cinfo.c' from the build-directory-local snapshot of the Codezero source tree. +This file is generated automatically by the Codezero build system and linked +against the kernel. It contains the parameters of the containers executed on +the kernel. Because we change the content of container0 each time when +executing a run script, those parameter change. So we have to enforce to +re-generation of the 'cinfo.c' file. + +How Genode ROM modules are passed into the final image +====================================================== + +The Codezero build system picks up any ELF files residing the container's +directory wheres the file called 'main.elf' is considered to be the roottask +(in Codezero speak called pager) of the container. For Genode, 'main.elf' +corresponds to the core executable. All other boot modules are merged into an +ELF file, which we merely use as a container for these binary data. This ELF +file is linked such that it gets loaded directly after the core image (this is +how core finds the boot modules). The process of archiving all boot modules +into the single ELF file is automated via the 'base-codezero/tool/gen_romfs' +tool. In the container's directory, the merged file is called 'modules.elf'. + +Adapting the source code of the kernel +====================================== + +For debugging and development you might desire to change the kernel code +at times. You can safely do so within the 'base-codezero/contrib/' directory. +When issuing the next 'make kernel' from the Genode build directory, your +changes will be picked up. However, when working with run scripts, the kernel +is not revisited each time. The kernel gets built only once if the +'/kernel' directory does not exist, yet. If you work on the kernel +source tree and wish to conveniently test the kernel with a run script, use + +! make kernel run/ + +This way, you make sure to rebuild the kernel prior executing the steps +described in the run script. + +Tweaking the kernel configuration +================================= + +The kernel configuration can be tweaked within '/kernel/codezero'. +Just change to this directory and issue './build.py -C'. The next time you +build the kernel via 'make kernel' your configuration will be applied. +If you want to conserve your custom configuration, just copy the file +'/kernel/codezero/build/config.cml'. + +Parameters of 'vpb926.cml' explained +==================================== + +The default configuration for the VersatilePB926 platform as found at +'base-codzero/config/vpb926.cml' is paramaterized as follows: + +:Default pager parameters: +! 0x40000 Pager LMA +! 0x100000 Pager VMA +These values are important because they are currently hard-wired in the +linker script used by Genode. If you need to adopt these values, make +sure to also update the Genode linker script located at +'base-codezero/src/platform/genode.ld'. + +:Physical Memory Regions: +! 1 Number of Physical Regions +! 0x40000 Physical Region 0 Start Address +! 0x4000000 Physical Region 0 End Address +We only use 64MB of memory. The physical memory between 0 and 0x40000 is +used by the kernel. + +:Virtual Memory Regions: +! 1 Number of Virtual Regions +! 0x0 Virtual Region 0 Start Address +! 0x50000000 Virtual Region 0 End Address +It is important to choose the end address such that the virtual memory +covers the thread context area. The context area is defined at +'base/include/base/thread.h'. + + +Limitations +########### + +At the current stage, the Genode version for Codezero is primarily geared +towards the developers of Codezero as a workload to stress their kernel. It +still has a number of limitations that would affect the real-world use: + +* Because the only platform supported out of the box by the official Codezero + source tree is the ARM-based Versatilebp board, Genode is currently tied to + this hardware platform. + +* The current timer driver at 'os/src/drivers/timer/codezero/' is a dummy + driver that just yields the CPU time instead of blocking. Is is not + suitable as time source. + +* The PL110 framebuffer driver at 'os/src/drivers/framebuffer/pl110/' + does only support the LCD display as provided by Qemu but it is not tested on + real hardware. + +* Even though Codezero provides priority-based scheduling, Genode does not + allow assigning priorities to Codezero processes, yet. + +As always, these limitations will be addressed as needed. + + +Thanks +###### + +We want to thank the main developer of Codezero Bahadir Balban for his great +responsiveness to our feature requests and questions. Without his help, the +porting effort would have taken much more effort. We hope that our framework +will be of value to the Codezero community. + + diff --git a/base-codezero/etc/specs.conf b/base-codezero/etc/specs.conf new file mode 100644 index 000000000..500de1149 --- /dev/null +++ b/base-codezero/etc/specs.conf @@ -0,0 +1 @@ +SPECS = genode diff --git a/base-codezero/include/arm/cpu/atomic.h b/base-codezero/include/arm/cpu/atomic.h new file mode 100644 index 000000000..8a73cb8a9 --- /dev/null +++ b/base-codezero/include/arm/cpu/atomic.h @@ -0,0 +1,33 @@ +/* + * \brief Atomic operations for ARM on codezero + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ARM__CPU__ATOMIC_H_ +#define _INCLUDE__ARM__CPU__ATOMIC_H_ + +namespace Genode { + + /** + * Atomic compare and exchange + * + * This function compares the value at dest with cmp_val. + * If both values are equal, dest is set to new_val. If + * both values are different, the value at dest remains + * unchanged. + * + * \return 1 if the value was successfully changed to new_val, + * 0 if cmp_val and the value at dest differ. + */ + int cmpxchg(volatile int *dest, int cmp_val, int new_val); +} + +#endif /* _INCLUDE__ARM__CPU__ATOMIC_H_ */ diff --git a/base-codezero/include/base/ipc_msgbuf.h b/base-codezero/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..cb5beae44 --- /dev/null +++ b/base-codezero/include/base/ipc_msgbuf.h @@ -0,0 +1,63 @@ +/* + * \brief IPC message buffer + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +namespace Genode { + + /** + * IPC message buffer layout + */ + class Msgbuf_base + { + protected: + + size_t _size; + char _msg_start[]; /* symbol marks start of message */ + + public: + + /* + * Begin of actual message buffer + */ + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; }; + + /** + * Return address of message buffer + */ + inline void *addr() { return &_msg_start[0]; }; + + } __attribute__((aligned(4))); + + /** + * Instance of IPC message buffer with specified buffer size + */ + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + + } __attribute__((aligned(4))); +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-codezero/include/base/ipc_pager.h b/base-codezero/include/base/ipc_pager.h new file mode 100644 index 000000000..d1fcbf603 --- /dev/null +++ b/base-codezero/include/base/ipc_pager.h @@ -0,0 +1,169 @@ +/* + * \brief Dummy pager support for Genode + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +#include +#include +#include + +namespace Genode { + + class Mapping + { + private: + + addr_t _from_phys_addr; + addr_t _to_virt_addr; + size_t _num_pages; + bool _writeable; + + enum { PAGE_SIZE_LOG2 = 12 }; + + public: + + /** + * Constructor + */ + Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, unsigned l2size = PAGE_SIZE_LOG2, + bool rw = true) + : + _from_phys_addr(src_addr), + _to_virt_addr(dst_addr), + _num_pages(1 << (l2size - PAGE_SIZE_LOG2)), + _writeable(rw) + { } + + /** + * Construct invalid mapping + */ + Mapping() : _num_pages(0) { } + + /** + * Prepare map operation + * + * No preparations are needed on Codezero because all mapping + * originate from the physical address space. + */ + void prepare_map_operation() { } + + addr_t from_phys() const { return _from_phys_addr; } + addr_t to_virt() const { return _to_virt_addr; } + size_t num_pages() const { return _num_pages; } + bool writeable() const { return _writeable; } + }; + + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + private: + + Native_thread_id _last; /* faulted thread */ + addr_t _pf_addr; /* page-fault address */ + addr_t _pf_ip; /* instruction pointer of faulter */ + bool _pf_write; /* true on write fault */ + + Mapping _reply_mapping; + +// protected: +// +// /** +// * Wait for pagefault +// */ +// void _wait(); +// +// /** +// * Send page-fault reply and wait for next page fault +// */ +// void _reply_and_wait(); + + public: + + /** + * Constructor + */ + Ipc_pager(); + + /** + * Wait for a new page fault received as short message IPC + */ + void wait_for_fault(); + + /** + * Reply current page-fault and wait for a new one + */ + void reply_and_wait_for_fault(); + + /** + * Request instruction pointer of current page fault + */ + addr_t fault_ip() { return _pf_ip; } + + /** + * Request fault address of current page fault + */ + addr_t fault_addr() { return _pf_addr; } + + /** + * Set parameters for next reply + */ + void set_reply_mapping(Mapping m) { _reply_mapping = m; } + + /** + * Set destination for next reply + */ + void set_reply_dst(Native_capability pager_object) { + _last = pager_object.local_name(); } + + /** + * Answer call without sending a mapping + * + * This function is used to acknowledge local calls from one of + * core's region-manager sessions. + */ + void acknowledge_wakeup(); + + /** + * Return thread ID of last faulter + */ + Native_thread_id last() const { return _last; } + + /** + * Return badge for faulting thread + */ + unsigned long badge() const { return _last.tid; } + + /** + * Return true if page fault was a write fault + */ + bool is_write_fault() const { return _pf_write; } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + /* + * Reflection of exceptions is not supported on this platform. + */ + return false; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-codezero/include/base/native_types.h b/base-codezero/include/base/native_types.h new file mode 100644 index 000000000..abe7190b5 --- /dev/null +++ b/base-codezero/include/base/native_types.h @@ -0,0 +1,143 @@ +/* + * \brief Dummy definitions for native types used for compiling unit tests + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +namespace Codezero { + + struct l4_mutex; + + enum { NILTHREAD = -1 }; +} + +namespace Genode { + + class Platform_thread; + + struct Native_thread_id + { + int tid; + + /** + * Pointer to thread's running lock + * + * Once initialized (see 'lock_helper.h'), it will point to the + * '_running_lock' field of the thread's 'Native_thread' structure, + * which is part of the thread context. This member variable is + * used by the lock implementation only. + */ + struct Codezero::l4_mutex *running_lock; + + Native_thread_id() { } + + /** + * Constructor (used as implicit constructor) + */ + Native_thread_id(int l4id) : tid(l4id), running_lock(0) { } + + Native_thread_id(int l4id, Codezero::l4_mutex *rl) : tid(l4id), running_lock(rl) { } + }; + + struct Native_thread + { + Native_thread_id l4id; + + /** + * Only used in core + * + * For 'Thread' objects created within core, 'pt' points to the + * physical thread object, which is going to be destroyed on + * destruction of the 'Thread'. + */ + Platform_thread *pt; + }; + + /** + * Empty UTCB type expected by the thread library + * + * On this kernel, UTCBs are not placed within the the context area. Each + * thread can request its own UTCB pointer using the kernel interface. + * However, we use the 'Native_utcb' member of the thread context to + * hold thread-specific data, i.e. the running lock used by the lock + * implementation. + */ + struct Native_utcb + { + private: + + /** + * Prevent construction + * + * A UTCB is never constructed, it is backed by zero-initialized memory. + */ + Native_utcb(); + + /** + * Backing store for per-thread running lock + * + * The size of this member must equal 'sizeof(Codezero::l4_mutex)'. + * Unfortunately, we cannot include the Codezero headers here. + */ + int _running_lock; + + public: + + Codezero::l4_mutex *running_lock() { + return (Codezero::l4_mutex *)&_running_lock; } + }; + + inline bool operator == (Native_thread_id t1, Native_thread_id t2) { return t1.tid == t2.tid; } + inline bool operator != (Native_thread_id t1, Native_thread_id t2) { return t1.tid != t2.tid; } + + /* + * Because Codezero does not support local names for capabilities, a Genode + * capability consists of the global thread ID and a global object ID, not + * protected by the kernel when transmitted as IPC payloads. + */ + class Native_capability + { + private: + + Native_thread_id _tid; /* global thread ID */ + int _local_name; /* global unique object ID */ + + public: + + /** + * Default constructor creates invalid capability + */ + Native_capability() + : _local_name(0) { _tid.tid = Codezero::NILTHREAD; } + + /** + * Constructor for hand-crafting capabilities + * + * This constructor is only used internally be the framework. + */ + Native_capability(Native_thread_id tid, int local_name) + : _tid(tid), _local_name(local_name) { } + + bool valid() const { return _tid.tid != Codezero::NILTHREAD; } + + int local_name() const { return _local_name; } + int dst() const { return _tid.tid; } + + Native_thread_id tid() const { return _tid; } + }; + + typedef int Native_connection_state; +} + + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-codezero/include/codezero/dummies/stdio.h b/base-codezero/include/codezero/dummies/stdio.h new file mode 100644 index 000000000..e69de29bb diff --git a/base-codezero/include/codezero/dummies/string.h b/base-codezero/include/codezero/dummies/string.h new file mode 100644 index 000000000..e69de29bb diff --git a/base-codezero/include/codezero/syscalls.h b/base-codezero/include/codezero/syscalls.h new file mode 100644 index 000000000..e289825df --- /dev/null +++ b/base-codezero/include/codezero/syscalls.h @@ -0,0 +1,76 @@ +/* + * \brief Aggregate Codezero syscall bindings + * \author Norman Feske + * \date 2010-02-16 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CODEZERO__SYSCALLS_H_ +#define _INCLUDE__CODEZERO__SYSCALLS_H_ + +/* + * Codezero headers happen to include the compiler's 'stdarg.h'. If this + * happened within the 'Codezero' namespace below, we would not be able to + * include 'stdarg.h' later on into the root namespace (stdarg's include guards + * would prevent this. Therefore, we make sure to include the file into the + * root namespace prior processing any Codezero headers. + */ +#include + +namespace Codezero { extern "C" { + +/* make Codezero includes happy */ +extern char *strncpy(char *dest, const char *src, __SIZE_TYPE__); +extern void *memcpy(void *dest, const void *src, __SIZE_TYPE__); + +/* + * Work around the problem of C++ keywords being used as + * argument names in the Codezero API headers. + */ +#define new _new_ +#define virtual _virtual_ +#define printf(A, ...) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* needed for capability.h */ +#include +#include +#include +#include + +#undef new +#undef virtual +#ifdef max +#undef max +#endif +#undef printf +} } + +namespace Codezero { + + /** + * Return thread ID of the calling thread + */ + inline int thread_myself() + { + struct task_ids ids = { 0, 0, 0 }; + l4_getid(&ids); + return ids.tid; + } +} + +#endif /* _INCLUDE__CODEZERO__SYSCALLS_H_ */ diff --git a/base-codezero/lib/mk/arm/startup.mk b/base-codezero/lib/mk/arm/startup.mk new file mode 100644 index 000000000..66f13e018 --- /dev/null +++ b/base-codezero/lib/mk/arm/startup.mk @@ -0,0 +1,9 @@ +LIBS = cxx lock l4 +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(REP_DIR)/src/platform +INC_DIR += $(BASE_DIR)/src/platform +INC_DIR += $(REP_DIR)/include/codezero/dummies + +vpath crt0.s $(BASE_DIR)/src/platform/arm +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-codezero/lib/mk/arm_v5/l4.mk b/base-codezero/lib/mk/arm_v5/l4.mk new file mode 100644 index 000000000..8779d0b68 --- /dev/null +++ b/base-codezero/lib/mk/arm_v5/l4.mk @@ -0,0 +1,3 @@ +LIBS += l4_arm_v5 + +include $(REP_DIR)/lib/mk/l4.inc diff --git a/base-codezero/lib/mk/arm_v5/l4_arm_v5.mk b/base-codezero/lib/mk/arm_v5/l4_arm_v5.mk new file mode 100644 index 000000000..4ba824957 --- /dev/null +++ b/base-codezero/lib/mk/arm_v5/l4_arm_v5.mk @@ -0,0 +1,9 @@ +LIBL4_DIR = $(CODEZERO_DIR)/conts/userlibs/libl4 + +INC_DIR += $(CODEZERO_DIR)/conts/userlibs/libc/include + +SRC_C += $(notdir $(wildcard $(LIBL4_DIR)/src/arch/arm/v5/*.c)) +SRC_S += $(notdir $(wildcard $(LIBL4_DIR)/src/arch/arm/v5/*.S)) + +vpath %.c $(LIBL4_DIR)/src/arch/arm/v5 +vpath %.S $(LIBL4_DIR)/src/arch/arm/v5 diff --git a/base-codezero/lib/mk/codezero_cml.inc b/base-codezero/lib/mk/codezero_cml.inc new file mode 100644 index 000000000..93cb2386f --- /dev/null +++ b/base-codezero/lib/mk/codezero_cml.inc @@ -0,0 +1,3 @@ +ifeq ($(filter-out $(SPECS),platform_vpb926),) +CODEZERO_CML = $(REP_DIR)/config/vpb926.cml +endif diff --git a/base-codezero/lib/mk/cxx.mk b/base-codezero/lib/mk/cxx.mk new file mode 100644 index 000000000..b77ed0d06 --- /dev/null +++ b/base-codezero/lib/mk/cxx.mk @@ -0,0 +1,13 @@ +# +# Additional symbols we need to keep when using the arm-none-linux-gnueabi +# tool chain +# +KEEP_SYMBOLS += __aeabi_ldivmod __aeabi_uldivmod __dynamic_cast +KEEP_SYMBOLS += _ZN10__cxxabiv121__vmi_class_type_infoD0Ev + +# +# Override sources of the base repository with our changed version +# +vpath exception.cc $(REP_DIR)/src/base/cxx + +include $(BASE_DIR)/lib/mk/cxx.mk diff --git a/base-codezero/lib/mk/ipc.mk b/base-codezero/lib/mk/ipc.mk new file mode 100644 index 000000000..9e662a419 --- /dev/null +++ b/base-codezero/lib/mk/ipc.mk @@ -0,0 +1,4 @@ +SRC_CC = ipc.cc pager.cc +INC_DIR += $(REP_DIR)/include/codezero/dummies + +vpath %.cc $(REP_DIR)/src/base/ipc diff --git a/base-codezero/lib/mk/l4.inc b/base-codezero/lib/mk/l4.inc new file mode 100644 index 000000000..521519fe5 --- /dev/null +++ b/base-codezero/lib/mk/l4.inc @@ -0,0 +1,43 @@ +LIBL4_DIR = $(CODEZERO_DIR)/conts/userlibs/libl4 + +SRC_C += init.c irq.c mutex.c +SRC_C += arch/arm/exregs.c +SRC_S += $(addprefix arch/arm/,syscalls.S new_thread.S) +SRC_C += $(addprefix lib/,addr.c bit.c idpool.c) +SRC_C += $(addprefix lib/thread/,init.c thread.c) +SRC_C += $(addprefix lib/cap/,cap.c read.c) + +INC_DIR += $(CODEZERO_DIR)/conts/userlibs/libc/include +INC_DIR += $(CODEZERO_DIR)/conts/userlibs/libmem/include +INC_DIR += $(CODEZERO_DIR)/conts/userlibs/libmem + +vpath % $(LIBL4_DIR)/src + +# +# The libl4 source files uses macros defined in macros.h but do not +# explicitly include the 'macros.h' header file. +# +CC_OPT += -include $(LIBL4_DIR)/include/l4lib/macros.h + +# +# Resolve conflicts with built-in functions +# +CC_OPT += -fno-builtin-pow + +# +# During the compilation of the libl4 file 'thread.c', the 'l4id_t' type +# is used without prior inclusion of 'types.h'. Furthermore, 'types.h' +# has a wrong include guard, so we take care of this problem using a +# wrapper. +# +CC_OPT_lib_thread_thread += -include fix_include_types.h +CC_OPT_arch_arm_exregs += -include fix_include_types.h + +lib/thread/thread.o arch/arm/exregs.o: fix_include_types.h + +fix_include_types.h: + @echo "#include " > $@ + @echo "#define __L4LIB_ARM_TYPES_H___" >> $@ + +CC_OPT += -std=gnu99 + diff --git a/base-codezero/lib/mk/lock.mk b/base-codezero/lib/mk/lock.mk new file mode 100644 index 000000000..be1457625 --- /dev/null +++ b/base-codezero/lib/mk/lock.mk @@ -0,0 +1,7 @@ +SRC_CC = lock.cc cmpxchg.cc + +INC_DIR += $(REP_DIR)/include/codezero/dummies +INC_DIR += $(REP_DIR)/src/base/lock + +vpath lock.cc $(BASE_DIR)/src/base/lock +vpath cmpxchg.cc $(REP_DIR)/src/base/lock diff --git a/base-codezero/lib/mk/pager.mk b/base-codezero/lib/mk/pager.mk new file mode 100644 index 000000000..24f25bcf8 --- /dev/null +++ b/base-codezero/lib/mk/pager.mk @@ -0,0 +1,4 @@ +SRC_CC = pager.cc +INC_DIR += $(REP_DIR)/include/codezero/dummies + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-codezero/lib/mk/pl011/core_printf.mk b/base-codezero/lib/mk/pl011/core_printf.mk new file mode 100644 index 000000000..7b10977e1 --- /dev/null +++ b/base-codezero/lib/mk/pl011/core_printf.mk @@ -0,0 +1,6 @@ +SRC_CC = core_printf.cc +LIBS = cxx console +INC_DIR += $(REP_DIR)/src/base/console/pl011 +INC_DIR += $(REP_DIR)/include/codezero/dummies + +vpath core_printf.cc $(BASE_DIR)/src/base/console diff --git a/base-codezero/lib/mk/platform.mk b/base-codezero/lib/mk/platform.mk new file mode 100644 index 000000000..a68192448 --- /dev/null +++ b/base-codezero/lib/mk/platform.mk @@ -0,0 +1,33 @@ +# +# Create prerequisites for building Genode for Codezero +# + +# +# Execute the rules in this file only at the second build stage when we know +# about the complete build settings, e.g., the 'CROSS_DEV_PREFIX'. +# +ifeq ($(called_from_lib_mk),yes) + +include $(REP_DIR)/lib/mk/codezero_cml.inc + +all: $(BUILD_BASE_DIR)/include/l4/config.h + +$(BUILD_BASE_DIR)/include/l4/config.h: $(CODEZERO_CML) + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)$(CODEZERO_DIR)/tools/cml2header.py -i $^ -o $@ + +# +# Codezero's 'macros.h' includes the file "config.h", expected to be located in +# the same directory (using #include "config.h"). However, 'config.h' is +# generated into the source tree by the Codezero configuration system. Since we +# do not want to pollute the source tree, we create a shadow copy of 'macros.h' +# in the same directory as our generated 'config.h'. +# +all: $(BUILD_BASE_DIR)/include/l4/macros.h + +$(BUILD_BASE_DIR)/include/l4/macros.h: $(CODEZERO_DIR)/include/l4/macros.h + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)ln -s $^ $@ + +endif + diff --git a/base-codezero/lib/mk/thread.mk b/base-codezero/lib/mk/thread.mk new file mode 100644 index 000000000..a0741ed3b --- /dev/null +++ b/base-codezero/lib/mk/thread.mk @@ -0,0 +1,5 @@ +SRC_CC = thread.cc thread_start.cc thread_bootstrap.cc +INC_DIR += $(REP_DIR)/include/codezero/dummies + +vpath thread_start.cc $(REP_DIR)/src/base/thread +vpath %.cc $(BASE_DIR)/src/base/thread diff --git a/base-codezero/mk/spec-codezero.mk b/base-codezero/mk/spec-codezero.mk new file mode 100644 index 000000000..3ad6ea616 --- /dev/null +++ b/base-codezero/mk/spec-codezero.mk @@ -0,0 +1,56 @@ +# +# Specifics for the Codezero kernel API +# + +# +# Read default and builddir-specific config files +# +# In these config files, we expect to find the definition of CODEZERO_DIR +# +-include $(call select_from_repositories,etc/codezero.conf) +-include $(BUILD_BASE_DIR)/etc/codezero.conf + +ifeq ($(CODEZERO_DIR),) +$(error Could not find the definition of CODEZERO_DIR in etc/codezero.conf) +endif + +# +# Convert path to absolute directory +# +absdir = $(shell readlink -f $(1)) + +# +# Headers generated within the build directory +# (see 'lib/mk/platform.mk') +# +INC_DIR += $(BUILD_BASE_DIR)/include + +# +# Codezero headers +# +CODEZERO_ABS_DIR = $(call absdir,$(CODEZERO_DIR)) + +INC_DIR += $(CODEZERO_ABS_DIR)/include +INC_DIR += $(CODEZERO_ABS_DIR)/conts/userlibs/libl4/include +INC_DIR += $(CODEZERO_ABS_DIR)/conts/userlibs/libdev/uart/include + +# +# Codezero-specific startup code +# +PRG_LIBS += startup + +# +# Allow programs to test for the Codezero kernel +# +# This is needed by the 'pl050/irq_handler.h' to handle the interrupt semantics +# of Codezero. +# +CC_OPT += -D__CODEZERO__ + +# +# Clean rules for removing the side effects of building the platform +# +clean_includes: + $(VERBOSE)rm -rf $(BUILD_BASE_DIR)/include + +cleanall: clean_includes diff --git a/base-codezero/mk/spec-codezero_arm.mk b/base-codezero/mk/spec-codezero_arm.mk new file mode 100644 index 000000000..adca798ff --- /dev/null +++ b/base-codezero/mk/spec-codezero_arm.mk @@ -0,0 +1,13 @@ +# +# Specifics for Codezero on ARM +# +SPECS += codezero + +# +# Linker options specific for ARM +# +LD_TEXT_ADDR ?= 0x02000000 + +CC_OPT += -D__ARCH__=arm + +include $(call select_from_repositories,mk/spec-codezero.mk) diff --git a/base-codezero/mk/spec-codezero_arm_v5.mk b/base-codezero/mk/spec-codezero_arm_v5.mk new file mode 100644 index 000000000..7fd40ca72 --- /dev/null +++ b/base-codezero/mk/spec-codezero_arm_v5.mk @@ -0,0 +1,9 @@ +# +# Specifics for Codezero on ARMv5 +# + +SPECS += codezero_arm + +CC_OPT += -D__SUBARCH__=v5 + +include $(call select_from_repositories,mk/spec-codezero_arm.mk) diff --git a/base-codezero/mk/spec-codezero_platform_vpb926.mk b/base-codezero/mk/spec-codezero_platform_vpb926.mk new file mode 100644 index 000000000..5145a5743 --- /dev/null +++ b/base-codezero/mk/spec-codezero_platform_vpb926.mk @@ -0,0 +1,6 @@ +SPECS += codezero_arm_v5 platform_vpb926 + +CC_OPT += -D__PLATFORM__=pb926 + +include $(call select_from_repositories,mk/spec-codezero_arm_v5.mk) +include $(call select_from_repositories,mk/spec-platform_vpb926.mk) diff --git a/base-codezero/patches/README b/base-codezero/patches/README new file mode 100644 index 000000000..4dc411106 --- /dev/null +++ b/base-codezero/patches/README @@ -0,0 +1,67 @@ +This directory contains patches of the Codezero kernel that are needed for the +integration with Genode. Furthermore, some patches address issues with recent +tool chains not yet supported by the official Codezero verison. + +:binutils-2.21.patch: + + The GNU assembler of binutils-2.21 complains with an error that was ignored + by previous binutils versions: + + "Error: .size expression for ... does not evaluate to a constant" + + This error seems to occur if the argument of 'BEGIN_PROC' does not match + the argument of 'END_PROC'. The patch fixes such inconsistencies in the + code. + +:gcc_shared_enabled.patch: + + Codezero expect the tool chain to be used for the kernel to not support + shared libraries. This is the case for Codesourcery's arm-non-eabi + tool chain. Such tool chains use to incorporate both libgcc and libgcc_eh + into the single libgcc.a library. In contrast, for tool chains built with + '--enable-shared', libgcc does not contain the functions of libgcc_eh. Hence, + one symbol called '__aeabi_unwind_cpp_pr0' referenced by libgcc and normally + provided by libgcc_eh remains unresolved. There are two possible solutions + for this problem: We could link libgcc_eh to the 'final.elf' image as + expected by libgcc. However, this way, we will need to implement the + the environment expected by libgcc_eh. For Codezero, this is pointless + because no C++ is used. The second option is to provide a dummy symbol + for '__aeabi_unwind_cpp_pr0' just to make the linker happy. This patch + adds such a dummy symbol to 'loader/main.c'. + +:libc_search_dir.patch: + + The userlibs are build with w/o '-nostdinc'. Consequently, the standard + search paths of the tool chain are used. Because the user land is + normally build with the Codesourcery tool chain 'arm-none-linux-gnueabi', + the complete glibc headers (that come with the tool chain) end up in + the default search path. Coincidentally, the userlibs SConstruct file + misses to supply the Codezero libc headers, which goes undetected because + headers such as 'stdio.h' are silently taken from the tool chain's libc. + This patch supplies Codezero's libc include-search path for building + the userlibs. This enables the userlibs to be built with tool chains + that do not come with a complete libc. + +:scons-2.0.1.patch: + + SCons 2.0.1 complains about the 'build_dir' argument being renamed to + 'variant_dir'. This patch renames the argument where needed for building + the kernel and the default container. + +:set_fixed_pager.patch: + + At some point, Codezero abandoned the facility to define the pager for a + given thread via the exregs system call. Instead, the kernel hard-wires the + creator of the thread as the thread's pager. This is conflicting with + Genode's way of creating and paging threads. On the current version of Genode + for Codezero, all threads are paged by one thread (thread 3 happens to be the + global pager) within core. As a work-around to Codezero's current limitation, + we define thread 3 to be the pager of all threads. + +:gcc_4_6_1_fixes.patch: + + Version 4.6.1 of GCC is more picky about dead code than previous versions and + warns about unused variables. Because Codezero is build with the '-Werror' + flag, these warnings cause the kernel build to fail. The patch fixes those + warnings by removing the variables in question. + diff --git a/base-codezero/patches/binutils-2.21.patch b/base-codezero/patches/binutils-2.21.patch new file mode 100644 index 000000000..dd34a1037 --- /dev/null +++ b/base-codezero/patches/binutils-2.21.patch @@ -0,0 +1,33 @@ +diff --git a/src/arch/arm/vectors.S b/src/arch/arm/vectors.S +index 0475389..62f3c38 100644 +--- a/src/arch/arm/vectors.S ++++ b/src/arch/arm/vectors.S +@@ -503,7 +503,7 @@ BEGIN_PROC(arm_irq_exception_basic) + mov lr, pc + ldr pc, =do_irq + ldmfd sp!, {r0-r3, pc}^ +-END_PROC(arm_irq_exception) ++END_PROC(arm_irq_exception_basic) + + /* Minimal IRQ state saved on irq stack right after irq vector enters: */ + #define IRQ_R0 0 +diff --git a/conts/userlibs/libc/src/arch-arm/memcpy.S b/conts/userlibs/libc/src/arch-arm/memcpy.S +index 383f5d2..b4df27f 100644 +--- a/conts/userlibs/libc/src/arch-arm/memcpy.S ++++ b/conts/userlibs/libc/src/arch-arm/memcpy.S +@@ -57,4 +57,4 @@ BEGIN_PROC(memcpy) + bne last + 1: + pop {r0, r4 - r11, pc} +-END_PROC(_memcpy) ++END_PROC(memcpy) +diff --git a/conts/userlibs/libc/src/arch-arm/memset.S b/conts/userlibs/libc/src/arch-arm/memset.S +index ce9b06c..3746955 100644 +--- a/conts/userlibs/libc/src/arch-arm/memset.S ++++ b/conts/userlibs/libc/src/arch-arm/memset.S +@@ -65,4 +65,4 @@ BEGIN_PROC(memset) + bne end + + ldmfd sp!, {r4 - r11, pc} +-END_PROC(_memset) ++END_PROC(memset) diff --git a/base-codezero/patches/gcc_4_6_1_fixes.patch b/base-codezero/patches/gcc_4_6_1_fixes.patch new file mode 100644 index 000000000..9404db70f --- /dev/null +++ b/base-codezero/patches/gcc_4_6_1_fixes.patch @@ -0,0 +1,182 @@ +diff --git a/src/api/map.c b/src/api/map.c +index 1d15086..6139b4c 100644 +--- a/src/api/map.c ++++ b/src/api/map.c +@@ -78,6 +78,6 @@ int sys_unmap(unsigned long virtual, unsigned long npages, unsigned int tid) + retval = ret; + } + +- return ret; ++ return retval; + } + +diff --git a/src/api/thread.c b/src/api/thread.c +index 985c425..579e4fb 100644 +--- a/src/api/thread.c ++++ b/src/api/thread.c +@@ -497,7 +497,7 @@ out_err: + */ + int sys_thread_control(unsigned int flags, struct task_ids *ids) + { +- struct ktcb *task = 0, *pager = 0; ++ struct ktcb *task = 0; + int err, ret = 0; + + if ((err = check_access((unsigned long)ids, sizeof(*ids), +@@ -508,8 +508,6 @@ int sys_thread_control(unsigned int flags, struct task_ids *ids) + if (!(task = tcb_find(ids->tid))) + return -ESRCH; + +- pager = task->pager; +- + /* + * Caller may operate on a thread if it shares + * the same address space with that thread's pager +diff --git a/src/arch/arm/mapping-common.c b/src/arch/arm/mapping-common.c +index 385f7c2..55b4bea 100644 +--- a/src/arch/arm/mapping-common.c ++++ b/src/arch/arm/mapping-common.c +@@ -313,12 +313,11 @@ int check_mapping(unsigned long vaddr, unsigned long size, + int remove_mapping_space(struct address_space *space, unsigned long vaddr) + { + pmd_table_t *pmd_table; +- int pgd_i, pmd_i; ++ int pmd_i; + pmd_t *pmd; + unsigned int pmd_type, pte_type; + + vaddr = page_align(vaddr); +- pgd_i = PGD_INDEX(vaddr); + pmd_i = PMD_INDEX(vaddr); + + /* +diff --git a/src/glue/arm/init.c b/src/glue/arm/init.c +index 2373c66..43c6fda 100644 +--- a/src/glue/arm/init.c ++++ b/src/glue/arm/init.c +@@ -68,8 +68,6 @@ void print_sections(void) + /* The kip is non-standard, using 0xBB to indicate mine for now ;-) */ + void kip_init() + { +- struct utcb **utcb_ref; +- + /* + * TODO: Adding utcb size might be useful + */ +@@ -86,9 +84,6 @@ void kip_init() + + kip_init_syscalls(); + +- /* KIP + 0xFF0 is pointer to UTCB segment start address */ +- utcb_ref = (struct utcb **)((unsigned long)&kip + UTCB_KIP_OFFSET); +- + add_boot_mapping(virt_to_phys(&kip), USER_KIP_PAGE, PAGE_SIZE, + MAP_USR_RO); + printk("%s: Kernel built on %s, %s\n", __KERNELNAME__, +diff --git a/loader/libs/elf/src/elf.c b/loader/libs/elf/src/elf.c +index 4a1b5e0..f97273b 100644 +--- a/loader/libs/elf/src/elf.c ++++ b/loader/libs/elf/src/elf.c +@@ -339,16 +339,12 @@ elf_loadFile(void *elfFile, bool phys) + { + int i; + int num_pheaders; +- int pheader_offset; +- int pheader_type; + if (elf_checkFile(elfFile) != 0) { + return false; + } + + num_pheaders = elf_getNumProgramHeaders(elfFile); +- pheader_offset = elf_getProgramHeaderOffset(elfFile, 0); + //printf("Number of program headers: %d\n", num_pheaders); +- //printf("Program header offset of first header from file beginning: 0x%p\n",pheader_offset); + + /* + * FIXME: +@@ -373,8 +369,6 @@ elf_loadFile(void *elfFile, bool phys) + // printf("This section's size in file: %p\n", len); + src = (uint64_t) (uintptr_t) elfFile + elf_getProgramHeaderOffset(elfFile, i); + // printf("Elf program header offset: %p\n", src); +- pheader_type = elf_getProgramHeaderType(elfFile, i); +- // printf("Elf program header type: %p\n", pheader_type); + // Comment + printf("Copying to range from 0x%x to 0x%x of size: 0x%x\n", (unsigned int)dest, (unsigned int)dest + (unsigned int)len, (unsigned int)len); + memcpy((void*) (uintptr_t) dest, (void*) (uintptr_t) src, len); +diff --git a/loader/libs/elf/src/elf32.c b/loader/libs/elf/src/elf32.c +index 2d13798..78bbf33 100644 +--- a/loader/libs/elf/src/elf32.c ++++ b/loader/libs/elf/src/elf32.c +@@ -248,7 +248,6 @@ elf32_fprintf(FILE *f, struct Elf32_Header *file, int size, const char *name, in + struct Elf32_Shdr *sections; + unsigned numSections; + int i, r; +- char *str_table; + + fprintf(f, "Found an elf32 file called \"%s\" located " + "at address 0x%p\n", name, file); +@@ -307,7 +306,6 @@ elf32_fprintf(FILE *f, struct Elf32_Header *file, int size, const char *name, in + } + } + if (flags & ELF_PRINT_SECTIONS) { +- str_table = elf32_getSegmentStringTable(file); + + printf("Section Headers:\n"); + printf(" [Nr] Name Type Addr Off\n"); +diff --git a/src/generic/capability.c b/src/generic/capability.c +index 0860ea5..ef44445 100644 +--- a/src/generic/capability.c ++++ b/src/generic/capability.c +@@ -403,7 +403,7 @@ struct capability *cap_match_mem(struct capability *cap, + { + struct sys_map_args *args = args_ptr; + struct ktcb *target = args->task; +- unsigned long long start, end, pfn_point; ++ unsigned long long start, pfn_point; + unsigned long pfn; + unsigned int perms; + +@@ -415,7 +415,6 @@ struct capability *cap_match_mem(struct capability *cap, + + /* Long long range check to avoid overflow */ + start = cap->start; +- end = cap->end; + pfn_point = pfn; + if (start > pfn_point || cap->end < pfn_point + args->npages) + return 0; +diff --git a/loader/main.c b/loader/main.c +index 7d21a4c..8d7d6db 100644 +--- a/loader/main.c ++++ b/loader/main.c +@@ -26,7 +26,6 @@ int load_elf_image(unsigned long **entry, void *filebuf); + int load_container_image(void *cont_section) + { + struct Elf32_Header *elf_header = (struct Elf32_Header *)cont_section; +- struct Elf32_Shdr *sect_header; + int nsect; + int nimgs = 0; + unsigned long *image_entry; +@@ -36,7 +35,6 @@ int load_container_image(void *cont_section) + return -1; + } + +- sect_header = elf32_getSectionTable(elf_header); + nsect = elf32_getNumSections(elf_header); + + for (int i = 0; i < nsect; i++) { +@@ -59,7 +57,6 @@ int load_container_image(void *cont_section) + int load_container_images(unsigned long start, unsigned long end) + { + struct Elf32_Header *elf_header = (struct Elf32_Header *)start; +- struct Elf32_Shdr *sect_header; + int nsect = 0; + int nconts = 0; + +@@ -68,7 +65,6 @@ int load_container_images(unsigned long start, unsigned long end) + return -1; + } + +- sect_header = elf32_getSectionTable(elf_header); + nsect = elf32_getNumSections(elf_header); + + for (int i = 0; i < nsect; i++) { diff --git a/base-codezero/patches/gcc_shared_enabled.patch b/base-codezero/patches/gcc_shared_enabled.patch new file mode 100644 index 000000000..a5a8be439 --- /dev/null +++ b/base-codezero/patches/gcc_shared_enabled.patch @@ -0,0 +1,10 @@ +diff --git a/loader/main.c b/loader/main.c +index 7d21a4c..ee03918 100644 +--- a/loader/main.c ++++ b/loader/main.c +@@ -135,3 +135,5 @@ int main(void) + return -1; + } + ++ ++asm(".global __aeabi_unwind_cpp_pr0; __aeabi_unwind_cpp_pr0:"); diff --git a/base-codezero/patches/libc_search_dir.patch b/base-codezero/patches/libc_search_dir.patch new file mode 100644 index 000000000..90931cc19 --- /dev/null +++ b/base-codezero/patches/libc_search_dir.patch @@ -0,0 +1,21 @@ +diff --git a/conts/userlibs/SConstruct b/conts/userlibs/SConstruct +index 41c7913..421b563 100644 +--- a/conts/userlibs/SConstruct ++++ b/conts/userlibs/SConstruct +@@ -11,6 +11,7 @@ PROJRELROOT = '../..' + sys.path.append(PROJRELROOT) + + from scripts.config.config_invoke import * ++from scripts.config.projpaths import * + + config = configuration_retrieve() + gcc_arch_flag = config.gcc_arch_flag +@@ -28,7 +29,7 @@ env = Environment(CC = config.toolchain_userspace + 'gcc', + ASFLAGS = ['-D__ASSEMBLY__', '-march=' + gcc_arch_flag], + ENV = {'PATH' : os.environ['PATH']}, + LIBS = 'gcc', # libgcc.a - Required for division routines. +- CPPPATH = KERNEL_HEADERS, ++ CPPPATH = [KERNEL_HEADERS, LIBC_INCLUDE], + CPPFLAGS = '-include l4/config.h -include l4/macros.h -include l4/types.h') + + # Set the build directory for this source tree diff --git a/base-codezero/patches/scons-2.0.1.patch b/base-codezero/patches/scons-2.0.1.patch new file mode 100644 index 000000000..cbab04464 --- /dev/null +++ b/base-codezero/patches/scons-2.0.1.patch @@ -0,0 +1,97 @@ +diff --git a/src/drivers/SConscript b/src/drivers/SConscript +index eedb59f..8f5cd5d 100644 +--- a/src/drivers/SConscript ++++ b/src/drivers/SConscript +@@ -8,24 +8,24 @@ src_local = [] + objs = [] + + objs += SConscript("uart/pl011/SConscript", exports = { 'env' : env }, +- duplicate=0, build_dir = join(bdir, 'pl011')) ++ duplicate=0, variant_dir = join(bdir, 'pl011')) + + objs += SConscript("timer/sp804/SConscript", exports = { 'env' : env }, +- duplicate=0, build_dir = join(bdir, 'timer')) ++ duplicate=0, variant_dir = join(bdir, 'timer')) + + objs += SConscript("irq/pl190/SConscript", exports = { 'env' : env }, +- duplicate=0, build_dir = join(bdir, 'vic')) ++ duplicate=0, variant_dir = join(bdir, 'vic')) + + objs += SConscript("irq/gic/SConscript", exports = { 'env' : env }, +- duplicate=0, build_dir = join(bdir, 'gic')) ++ duplicate=0, variant_dir = join(bdir, 'gic')) + + objs += SConscript("irq/omap3/SConscript", exports = { 'env' : env }, +- duplicate=0, build_dir = join(bdir, 'omap/intc')) ++ duplicate=0, variant_dir = join(bdir, 'omap/intc')) + + objs += SConscript("uart/omap/SConscript", exports = { 'env' : env }, +- duplicate=0, build_dir = join(bdir, 'omap/uart')) ++ duplicate=0, variant_dir = join(bdir, 'omap/uart')) + + objs += SConscript("timer/omap/SConscript", exports = { 'env' : env }, +- duplicate=0, build_dir = join(bdir, 'omap/timer')) ++ duplicate=0, variant_dir = join(bdir, 'omap/timer')) + + Return('objs') +diff --git a/conts/baremetal/empty/SConstruct b/conts/baremetal/empty/SConstruct +index b70d69a..4889d8e 100644 +--- a/conts/baremetal/empty/SConstruct ++++ b/conts/baremetal/empty/SConstruct +@@ -48,7 +48,7 @@ env = Environment(CC = config.toolchain_userspace + 'gcc', + CPPFLAGS = '-include l4/config.h -include l4/macros.h -include l4/types.h') + + objs = SConscript('SConscript', exports = { 'env' : env }, +- duplicate=0, build_dir = builddir) ++ duplicate=0, variant_dir = builddir) + + Depends(objs, join(PROJROOT, CONFIG_H)) + prog = env.Program(join(builddir, 'main.elf'), objs) +diff --git a/SConstruct b/SConstruct +index 2abc190..58c983d 100644 +--- a/SConstruct ++++ b/SConstruct +@@ -71,35 +71,35 @@ env = Environment(CC = config.toolchain_kernel + 'gcc', + objects = [] + objects += SConscript('src/generic/SConscript', + exports = { 'env' : env }, duplicate = 0, +- build_dir = join(builddir, 'generic')) ++ variant_dir = join(builddir, 'generic')) + + objects += SConscript(join(join('src/glue', arch), 'SConscript'), + exports = { 'env' : env }, duplicate = 0, +- build_dir = join(builddir, join('glue',arch))) ++ variant_dir = join(builddir, join('glue',arch))) + + objects += SConscript(join(join('src/arch', arch), 'SConscript'), + exports = { 'env' : env }, duplicate = 0, +- build_dir = join(builddir, join('arch', arch))) ++ variant_dir = join(builddir, join('arch', arch))) + + objects += SConscript(join(join('src/arch', arch), join(subarch, 'SConscript')), + exports = { 'env' : env }, duplicate = 0, +- build_dir = join(builddir, join(join('arch',arch), subarch))) ++ variant_dir = join(builddir, join(join('arch',arch), subarch))) + + objects += SConscript('src/lib/SConscript', + exports = { 'env' : env }, duplicate = 0, +- build_dir = join(builddir, 'lib')) ++ variant_dir = join(builddir, 'lib')) + + objects += SConscript('src/api/SConscript', + exports = { 'env' : env }, duplicate = 0, +- build_dir = join(builddir, 'api')) ++ variant_dir = join(builddir, 'api')) + + objects += SConscript('src/drivers/SConscript', + exports = { 'env' : env, 'bdir' : 'driver/'}, duplicate = 0, +- build_dir = join(builddir, 'driver')) ++ variant_dir = join(builddir, 'driver')) + + objects += SConscript(join(join('src/platform', platform), 'SConscript'), + exports = { 'env' : env }, duplicate = 0, +- build_dir = join(builddir, join('platform', platform))) ++ variant_dir = join(builddir, join('platform', platform))) + + + # Add builders for generating kernel linker scripts diff --git a/base-codezero/patches/set_fixed_pager.patch b/base-codezero/patches/set_fixed_pager.patch new file mode 100644 index 000000000..dc0818584 --- /dev/null +++ b/base-codezero/patches/set_fixed_pager.patch @@ -0,0 +1,13 @@ +diff --git a/include/l4/generic/tcb.h b/include/l4/generic/tcb.h +index 7b315b8..ace38d8 100644 +--- a/include/l4/generic/tcb.h ++++ b/include/l4/generic/tcb.h +@@ -70,7 +70,7 @@ struct task_ids { + + struct container; + +-#define tcb_pagerid(tcb) ((tcb)->pager->tid) ++#define tcb_pagerid(tcb) 3 + + #define space_is_pager(tcb) \ + ((tcb)->space->spid == (tcb)->pager->space->spid) diff --git a/base-codezero/run/env b/base-codezero/run/env new file mode 100644 index 000000000..bfdaec3ab --- /dev/null +++ b/base-codezero/run/env @@ -0,0 +1,88 @@ +# +# \brief Codezero-specific test-environment supplements +# \author Norman Feske +# \date 2011-08-05 +# +# This file is meant to be used as '--include' argument for 'tool/run'. +# + + +## +# Return location of prebuilt mirror of codezero source tree +# +proc kernel_dir { } { return [pwd]/kernel/codezero } + + +## +# Return container directory where the Genode binaries should be copied to +# +proc container_dir { } { return [kernel_dir]/build/cont0/empty0 } + + +## +# Return location of 'gen_romfs' tool +# +proc gen_romfs { } { return "[genode_dir]/base-codezero/tool/gen_romfs" } + + +## +# Print and execute shell command +# +proc exec_sh { command } { + puts "$command" + exec sh -c $command +} + + +################################## +## Test framework API functions ## +################################## + +proc create_boot_directory { } { + + # create only intermediate directries hosting the run directory + exec mkdir -p [run_dir] + exec rm -rf [run_dir] + + exec mkdir -p [run_dir]/genode +} + + +proc build_boot_image {binaries} { + + if {![file exists kernel]} { build kernel } + + copy_and_strip_genode_binaries_to_run_dir $binaries + + # the codezero build system expects that the pager binary is named 'main.elf' + exec cp [run_dir]/genode/core [container_dir]/main.elf + + # obtain list of modules + set modules [glob [run_dir]/genode/*] + + # remove core from list of modules + set core_idx [lsearch -exact $modules [run_dir]/genode/core] + set modules [lreplace $modules $core_idx $core_idx] + + # generate elf image containing the boot modules + exec_sh "[gen_romfs] -p [cross_dev_prefix] -c [run_dir]/genode/core -o [container_dir]/modules.elf $modules" + + set tool_chain_dir [file dirname [cross_dev_prefix]] + set prepend_path "" + if {[file isdirectory $tool_chain_dir]} { + set prepend_path $tool_chain_dir } + + # force re-generation of 'cinfo.c', which depends on the container content + exec_sh "rm -f [kernel_dir]/src/generic/cinfo.c" + + # rebuild codezero, linking the new container content + exec_sh "cd [kernel_dir]; PATH=$prepend_path:\$PATH ./build.py" + + # copy result to [run_dir]/image.elf (to be picked up by spawn_qemu) + exec_sh "cp [kernel_dir]/build/final.elf [run_dir]/image.elf" +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + spawn_qemu $wait_for_re $timeout_value } + diff --git a/base-codezero/src/base/console/pl011/core_console.h b/base-codezero/src/base/console/pl011/core_console.h new file mode 100644 index 000000000..d0e6f4ff7 --- /dev/null +++ b/base-codezero/src/base/console/pl011/core_console.h @@ -0,0 +1,78 @@ +/* + * \brief Console backend for PL011 UART on Codezero + * \author Norman Feske + * \date 2009-10-03 + * + * This code assumes a PL011 UART as provided by 'qemu -M versatilepb'. Prior + * executing this code, the kernel already initialized the UART to print some + * startup message. So we can skip the UART initialization here. The kernel + * maps the UART registers to the magic address PL011_BASE when starting mm0. + * So we can just start using the device without any precautions. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* codezero includes */ +#include + +typedef unsigned char uint8_t; + +/** + * Base address of default-mapped UART device + * + * defined in 'l4/arch/arm/io.h' + */ +enum { PL011_BASE = USERSPACE_CONSOLE_VBASE }; + +/** + * UART registers + */ +enum { PL011_REG_UARTDR = PL011_BASE + 0x00 }; +enum { PL011_REG_UARTFR = PL011_BASE + 0x18 }; + + +/** + * Returns true if UART is ready to transmit a character + */ +static bool pl011_tx_ready() +{ + enum { PL011_TX_FIFO_FULL = 1 << 5 }; + return !(*((volatile unsigned *)PL011_REG_UARTFR) & PL011_TX_FIFO_FULL); +} + + +/** + * Output character to serial port + */ +static void pl011_out_char(uint8_t c) +{ + /* wait until serial port is ready */ + while (!pl011_tx_ready()); + + /* output character */ + *((volatile unsigned int *)PL011_REG_UARTDR) = c; +} + + +namespace Genode +{ + class Core_console : public Console + { + protected: + + void _out_char(char c) { + if(c == '\n') + pl011_out_char('\r'); + pl011_out_char(c); + } + }; +} + diff --git a/base-codezero/src/base/cxx/exception.cc b/base-codezero/src/base/cxx/exception.cc new file mode 100644 index 000000000..612ddd24f --- /dev/null +++ b/base-codezero/src/base/cxx/exception.cc @@ -0,0 +1,53 @@ +/* + * \brief Support for exceptions libsupc++ + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2006-07-21 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" char __eh_frame_start__[]; /* from linker script */ +extern "C" void __register_frame (const void *begin); /* from libgcc_eh */ + +/* + * This symbol is set by Genode's dynamic linker (ldso) during binary setup. + * After setup, the symbol will point to the actual implementation of + * 'dl_iterate_phdr', which is located within the linker. 'dl_iterate_phdr' + * iterates through all (linker loaded) binaries and shared libraries. This + * function has to be implemented in order to support C++ exceptions within + * shared libraries. + * Return values of dl_iterate_phdr (gcc 4.2.4): + * < 0 = error + * 0 = continue program header iteration + * > 0 = stop iteration (no errors occured) + * + * See also: man dl_iterate_phdr + */ +int (*genode__dl_iterate_phdr) (int (*callback) (void *info, unsigned long size, void *data), void *data) = 0; + +extern "C" int dl_iterate_phdr(int (*callback) (void *info, unsigned long size, void *data), void *data) __attribute__((weak)); +extern "C" int dl_iterate_phdr(int (*callback) (void *info, unsigned long size, void *data), void *data) +{ + if (!genode__dl_iterate_phdr) + return -1; + + return genode__dl_iterate_phdr(callback, data); +} + +extern "C" void raise() +{ + PDBG("raise called - not implemented\n"); +} + +void init_exception_handling() +{ +// __register_frame(__eh_frame_start__); +} diff --git a/base-codezero/src/base/cxx/memcmp.cc b/base-codezero/src/base/cxx/memcmp.cc new file mode 100644 index 000000000..330b63165 --- /dev/null +++ b/base-codezero/src/base/cxx/memcmp.cc @@ -0,0 +1,24 @@ +/* + * \brief Functions required for using the arm-none-linux-gnueabi tool chain + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + + +extern "C" int raise(int sig) +{ + PWRN("raise - not yet implemented"); + return 0; +} diff --git a/base-codezero/src/base/ipc/ipc.cc b/base-codezero/src/base/ipc/ipc.cc new file mode 100644 index 000000000..48a242972 --- /dev/null +++ b/base-codezero/src/base/ipc/ipc.cc @@ -0,0 +1,175 @@ +/* + * \brief Codezero implementation of the IPC API + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Codezero includes */ +#include + +/* Genode includes */ +#include +#include +#include +#include + +using namespace Genode; +using namespace Codezero; + +enum { verbose_ipc = false }; + + +/***************** + ** Ipc_ostream ** + *****************/ + +void Ipc_ostream::_send() +{ + if (verbose_ipc) + PDBG("thread %d sends IPC to %d, write_offset=%d", + thread_myself(), _dst.tid().tid, _write_offset); + + umword_t snd_size = min(_write_offset, (unsigned)L4_IPC_EXTENDED_MAX_SIZE); + + *(umword_t *)_snd_msg->addr() = _dst.local_name(); + + int ret = l4_send_extended(_dst.tid().tid, L4_IPC_TAG_SYNC_EXTENDED, + snd_size, _snd_msg->addr()); + if (ret < 0) + PERR("l4_send_extended (to thread %d) returned ret=%d", + _dst.tid().tid, ret); + + _write_offset = sizeof(umword_t); +} + + + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) +: + Ipc_marshaller((char *)snd_msg->addr(), snd_msg->size()), + _snd_msg(snd_msg), _dst(dst) +{ + _write_offset = sizeof(umword_t); +} + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() +{ + umword_t *rcv_buf = (umword_t *)_rcv_msg->addr(); + umword_t rcv_size = min(_rcv_msg->size(), (unsigned)L4_IPC_EXTENDED_MAX_SIZE); + + if (verbose_ipc) + PDBG("thread %d waits for IPC from %d, rcv_buf at %p, rcv_size=%d", + tid().tid, _rcv_cs, rcv_buf, (int)rcv_size); + + int ret = l4_receive_extended(_rcv_cs, rcv_size, rcv_buf); + if (ret < 0) + PERR("l4_receive_extended (from any) returned ret=%d", ret); + + if (verbose_ipc) + PDBG("thread %d received IPC from %d", + tid().tid, l4_get_sender()); + + _read_offset = sizeof(umword_t); +} + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) +: + Ipc_unmarshaller((char *)rcv_msg->addr(), rcv_msg->size()), + Native_capability(thread_myself(), 0), + _rcv_msg(rcv_msg) +{ + _rcv_cs = L4_ANYTHREAD; + _read_offset = sizeof(umword_t); +} + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() +{ +#warning l4_sendrecv_extended is not yet implemented in l4lib/arch/syslib.h + _send(); + _rcv_cs = _dst.tid().tid; + _wait(); + _rcv_cs = L4_ANYTHREAD; + + _write_offset = _read_offset = sizeof(umword_t); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, + Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) +{ } + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_prepare_next_reply_wait() +{ + /* now we have a request to reply */ + _reply_needed = true; + + /* leave space for return value at the beginning of the msgbuf */ + _write_offset = 2*sizeof(umword_t); + + /* receive buffer offset */ + _read_offset = sizeof(umword_t); +} + + +void Ipc_server::_wait() +{ + /* wait for new server request */ + try { Ipc_istream::_wait(); } catch (Blocking_canceled) { } + + /* define destination of next reply */ + _dst = Native_capability(l4_get_sender(), badge()); + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() +{ + try { _send(); } catch (Ipc_error) { } + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply_wait() +{ + if (_reply_needed) + _reply(); + + _wait(); +} + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg) +: + Ipc_istream(rcv_msg), Ipc_ostream(Native_capability(), snd_msg), + _reply_needed(false) +{ } diff --git a/base-codezero/src/base/ipc/pager.cc b/base-codezero/src/base/ipc/pager.cc new file mode 100644 index 000000000..a3e8c3470 --- /dev/null +++ b/base-codezero/src/base/ipc/pager.cc @@ -0,0 +1,175 @@ +/* + * \brief Pager support for Codezero + * \author Norman Feske + * \date 2010-02-16 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* Codezero includes */ +#include + + +using namespace Genode; +using namespace Codezero; + +enum { verbose_page_faults = false }; + + +/************************ + ** Page-fault utility ** + ************************/ + +class Fault +{ + public: + + enum Type { READ, WRITE, EXEC, UNKNOWN }; + + private: + + /** + * Translate Codezero page-fault information to generic fault type + * + * \param sr status + * \param pte page-table entry + */ + static Type _fault_type(umword_t sr, umword_t pte) + { + if (is_prefetch_abort(sr)) + return EXEC; + + if ((pte & PTE_PROT_MASK) == (__MAP_USR_RO & PTE_PROT_MASK)) + return WRITE; + + return READ; + } + + Type _type; + umword_t _addr; + umword_t _ip; + + public: + + /** + * Constructor + * + * \param kdata Codezero-specific page-fault information + */ + Fault(struct fault_kdata const &kdata) + : + _type(_fault_type(kdata.fsr, kdata.pte)), + _addr(_type == EXEC ? kdata.faulty_pc : kdata.far), + _ip(kdata.faulty_pc) + { } + + Type type() const { return _type; } + umword_t addr() const { return _addr; } + umword_t ip() const { return _ip; } +}; + + +/** + * Print page-fault information in a human-readable form + */ +inline void print_page_fault(Fault &fault, int from) +{ + printf("page (%s%s%s) fault from %d at pf_addr=%lx, pf_ip=%lx\n", + fault.type() == Fault::READ ? "r" : "-", + fault.type() == Fault::WRITE ? "w" : "-", + fault.type() == Fault::EXEC ? "x" : "-", + from, fault.addr(), fault.ip()); +} + + +/*************** + ** IPC pager ** + ***************/ + +void Ipc_pager::wait_for_fault() +{ + for (;;) { + int ret = l4_receive(L4_ANYTHREAD); + + if (ret < 0) { + PERR("pager: l4_received returned ret=%d", ret); + continue; + } + + umword_t tag = l4_get_tag(); + int faulter_tid = l4_get_sender(); + + if (tag != L4_IPC_TAG_PFAULT) { + PWRN("got an unexpected IPC from %d", faulter_tid); + continue; + } + + /* copy fault information from message registers */ + struct fault_kdata fault_kdata; + for (unsigned i = 0; i < sizeof(fault_kdata_t)/sizeof(umword_t); i++) + ((umword_t *)&fault_kdata)[i] = read_mr(MR_UNUSED_START + i); + + Fault fault(fault_kdata); + + if (verbose_page_faults) + print_page_fault(fault, faulter_tid); + + /* determine corresponding page in our own address space */ + _pf_addr = fault.addr(); + _pf_write = fault.type() == Fault::WRITE; + _pf_ip = fault.ip(); + _last = faulter_tid; + + return; + } +} + + +void Ipc_pager::reply_and_wait_for_fault() +{ + /* install mapping */ + umword_t flags = _reply_mapping.writeable() ? MAP_USR_RW + : MAP_USR_RO; + + /* + * XXX: remove heuristics for mapping device registers. + */ + if (_reply_mapping.from_phys() == 0x10120000 /* LCD */ + || _reply_mapping.from_phys() == 0x10006000 /* keyboard */ + || _reply_mapping.from_phys() == 0x10007000) /* mouse */ + flags = MAP_USR_IO; + + int ret = l4_map((void *)_reply_mapping.from_phys(), + (void *)_reply_mapping.to_virt(), + _reply_mapping.num_pages(), flags, _last.tid); + + /* wake up faulter if mapping succeeded */ + if (ret < 0) + PERR("l4_map returned %d, putting thread %d to sleep", ret, _last.tid); + else + acknowledge_wakeup(); + + /* wait for next page fault */ + wait_for_fault(); +} + + +void Ipc_pager::acknowledge_wakeup() +{ + enum { SUCCESS = 0 }; + l4_set_sender(_last.tid); + l4_ipc_return(SUCCESS); +} + + +Ipc_pager::Ipc_pager() : Native_capability(thread_myself(), 0) { } + diff --git a/base-codezero/src/base/lock/cmpxchg.cc b/base-codezero/src/base/lock/cmpxchg.cc new file mode 100644 index 000000000..6bda6e2ce --- /dev/null +++ b/base-codezero/src/base/lock/cmpxchg.cc @@ -0,0 +1,48 @@ +/* + * \brief Codezero-specific implementation of cmpxchg + * \author Norman Feske + * \date 2009-10-12 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* Codezero includes */ +#include + + +static bool mutex_initialized; +static Codezero::l4_mutex mutex; + +int Genode::cmpxchg(volatile int *dest, int cmp_val, int new_val) +{ + if (!mutex_initialized) { + Codezero::l4_mutex_init(&mutex); + mutex_initialized = true; + } + + int ret = Codezero::l4_mutex_lock(&mutex); + if (ret < 0) + mutex_initialized = false; + + bool result = false; + if (*dest == cmp_val) { + *dest = new_val; + result = true; + } + + ret = Codezero::l4_mutex_unlock(&mutex); + if (ret < 0) + mutex_initialized = false; + + return result; +} diff --git a/base-codezero/src/base/lock/lock.cc b/base-codezero/src/base/lock/lock.cc new file mode 100644 index 000000000..8ebe5b7d4 --- /dev/null +++ b/base-codezero/src/base/lock/lock.cc @@ -0,0 +1,63 @@ +/* + * \brief Lock implementation + * \author Norman Feske + * \date 2007-10-15 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* Codezero includes */ +#include + +using namespace Genode; + + +Cancelable_lock::Cancelable_lock(Cancelable_lock::State initial) +: + _native_lock(UNLOCKED) +{ + if (initial == LOCKED) + lock(); +} + + +void Cancelable_lock::lock() +{ + while (!cmpxchg(&_native_lock, UNLOCKED, LOCKED)) + Codezero::l4_thread_switch(-1); +} + + +void Cancelable_lock::unlock() +{ + _native_lock = UNLOCKED; +} + + +/* + * Printf implementation to make Codezero's syscall bindings happy. + * + * We need a better place for this function - actually, the best would be not + * to need this function at all. As of now, 'printf' is referenced by + * Codezero's libl4, in particular by the mutex implementation. + */ +extern "C" void printf(const char *format, ...) __attribute__((weak)); +extern "C" void printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + vprintf(format, list); + + va_end(list); +} diff --git a/base-codezero/src/base/lock/lock_helper.h b/base-codezero/src/base/lock/lock_helper.h new file mode 100644 index 000000000..fb8dbc6a6 --- /dev/null +++ b/base-codezero/src/base/lock/lock_helper.h @@ -0,0 +1,112 @@ +/* + * \brief Helper functions for the Lock implementation + * \author Norman Feske + * \date 2010-04-20 + * + * For documentation about the interface, please revisit the 'base-pistachio' + * implementation. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* Codezero includes */ +#include + + +/** + * Resolve 'Thread_base::myself' when not linking the thread library + * + * This weak symbol is primarily used by test cases. Most other Genode programs + * use the thread library. If the thread library is not used, 'myself' can only + * be called by the main thread, for which 'myself' is defined as zero. + */ +Genode::Thread_base * __attribute__((weak)) Genode::Thread_base::myself() { return 0; } + + +Genode::Native_utcb *Genode::Thread_base::utcb() +{ + /* + * If 'utcb' is called on the object returned by 'myself', + * the 'this' pointer may be NULL (if the calling thread is + * the main thread). Therefore we handle this special case + * here. + */ + if (this == 0) return 0; + + return &_context->utcb; +} + + +static Codezero::l4_mutex main_running_lock = { -1 }; + + +static inline void thread_yield() +{ + Codezero::l4_thread_switch(-1); +} + + +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return tid.tid != Codezero::NILTHREAD; +} + + +static bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + if (!thread_id_valid(tid)) + return false; + + Codezero::l4_mutex_unlock(tid.running_lock); + return true; +} + + +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + using namespace Genode; + + Codezero::l4_mutex *running_lock = 0; + + /* obtain pointer to running lock of calling thread */ + if (Thread_base::myself()) + running_lock = Thread_base::myself()->utcb()->running_lock(); + else { + running_lock = &main_running_lock; + if (running_lock->lock == -1) { + Codezero::l4_mutex_init(running_lock); + Codezero::l4_mutex_lock(running_lock); /* block on first mutex lock */ + } + } + + return Genode::Native_thread_id(Codezero::thread_myself(), running_lock); +} + + +static inline Genode::Native_thread_id thread_invalid_id() +{ + return Genode::Native_thread_id(Codezero::NILTHREAD, 0); +} + + +static inline void thread_switch_to(Genode::Native_thread_id tid) +{ + if (thread_id_valid(tid)) + Codezero::l4_thread_switch(tid.tid); +} + + +static inline void thread_stop_myself() +{ + Genode::Native_thread_id myself = thread_get_my_native_id(); + Codezero::l4_mutex_lock(myself.running_lock); +} diff --git a/base-codezero/src/base/pager/pager.cc b/base-codezero/src/base/pager/pager.cc new file mode 100644 index 000000000..e0c9d2be3 --- /dev/null +++ b/base-codezero/src/base/pager/pager.cc @@ -0,0 +1,101 @@ +/* + * \brief Dummy pager framework + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +using namespace Genode; + + +/********************** + ** Pager activation ** + **********************/ + +void Pager_activation_base::entry() +{ + Ipc_pager pager; + _cap = pager; + _cap_valid.unlock(); + + pager.wait_for_fault(); + while (1) { + + /* lookup referenced object */ + Pager_object *obj = _ep ? _ep->obj_by_id(pager.badge()) : 0; + + /* handle request */ + if (obj) { + if (obj->pager(pager)) + /* something strange occured - leave thread in pagefault */ + pager.wait_for_fault(); + else + pager.reply_and_wait_for_fault(); + } else { + + /* + * We got a request from one of cores region-manager sessions + * to answer the pending page fault of a resolved region-manager + * client. Hence, we have to send the page-fault reply to the + * specified thread and answer the call of the region-manager + * session. + * + * When called from a region-manager session, we receive the + * core-local address of the targeted pager object via the + * first message word, which corresponds to the 'fault_ip' + * argument of normal page-fault messages. + */ + obj = reinterpret_cast(pager.fault_ip()); + + /* send reply to the calling region-manager session */ + pager.acknowledge_wakeup(); + + /* answer page fault of resolved pager object */ + pager.set_reply_dst(obj->cap()); + pager.acknowledge_wakeup(); + pager.wait_for_fault(); + } + } +} + + +/********************** + ** Pager entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *, Pager_activation_base *a) +: _activation(a) +{ _activation->ep(this); } + + +void Pager_entrypoint::dissolve(Pager_object *obj) +{ + remove(obj); +} + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + /* return invalid capability if no activation is present */ + if (!_activation) return Pager_capability(); + + _activation->cap(); + + Untyped_capability cap = Native_capability(_activation->cap().tid(), obj->badge()); + + /* add server object to object pool */ + obj->cap(cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return reinterpret_cap_cast(cap); +} diff --git a/base-codezero/src/base/thread/thread_start.cc b/base-codezero/src/base/thread/thread_start.cc new file mode 100644 index 000000000..2204d85d0 --- /dev/null +++ b/base-codezero/src/base/thread/thread_start.cc @@ -0,0 +1,79 @@ +/* + * \brief NOVA-specific implementation of the Thread API + * \author Norman Feske + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* Codezero includes */ +#include + +using namespace Genode; + + +/** + * Entry point entered by new threads + */ +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + Genode::sleep_forever(); +} + + +/***************** + ** Thread base ** + *****************/ + +void Thread_base::_init_platform_thread() +{ + Codezero::l4_mutex_init(utcb()->running_lock()); + Codezero::l4_mutex_lock(utcb()->running_lock()); /* block on first mutex lock */ +} + + +void Thread_base::_deinit_platform_thread() +{ + env()->cpu_session()->kill_thread(_thread_cap); +} + + +void Thread_base::start() +{ + /* create thread at core */ + char buf[48]; + name(buf, sizeof(buf)); + _thread_cap = env()->cpu_session()->create_thread(buf); + + /* assign thread to protection domain */ + env()->pd_session()->bind_thread(_thread_cap); + + /* create new pager object and assign it to the new thread */ + Pager_capability pager_cap = env()->rm_session()->add_client(_thread_cap); + env()->cpu_session()->set_pager(_thread_cap, pager_cap); + + /* register initial IP and SP at core */ + addr_t thread_sp = (addr_t)&_context->stack[-4]; + thread_sp &= ~0xf; /* align initial stack to 16 byte boundary */ + env()->cpu_session()->start(_thread_cap, (addr_t)_thread_start, thread_sp); +} + + +void Thread_base::cancel_blocking() +{ + Codezero::l4_mutex_unlock(utcb()->running_lock()); + env()->cpu_session()->cancel_blocking(_thread_cap); +} diff --git a/base-codezero/src/core/core_rm_session.cc b/base-codezero/src/core/core_rm_session.cc new file mode 100644 index 000000000..7458fc354 --- /dev/null +++ b/base-codezero/src/core/core_rm_session.cc @@ -0,0 +1,67 @@ +/* + * \brief Core-local RM session + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include + +using namespace Genode; + + +Rm_session::Local_addr +Core_rm_session::attach(Dataspace_capability ds_cap, size_t size, + off_t offset, bool use_local_addr, + Rm_session::Local_addr local_addr) +{ + using namespace Codezero; + + Dataspace_component *ds = static_cast(_ds_ep->obj_by_cap(ds_cap)); + if (!ds) + throw Invalid_dataspace(); + + if (size == 0) + size = ds->size(); + + size_t page_rounded_size = (size + get_page_size() - 1) & get_page_mask(); + size_t num_pages = page_rounded_size >> get_page_size_log2(); + + if (use_local_addr) { + PERR("Parameter 'use_local_addr' not supported within core"); + return 0; + } + + if (offset) { + PERR("Parameter 'offset' not supported within core"); + return 0; + } + + /* allocate range in core's virtual address space */ + void *virt_addr; + if (!platform()->region_alloc()->alloc(page_rounded_size, &virt_addr)) { + PERR("Could not allocate virtual address range in core of size %zd\n", + page_rounded_size); + return false; + } + + if (!map_local(ds->phys_addr(), (addr_t)virt_addr, num_pages)) { + PERR("core-local memory mapping failed virt=%lx, phys=%lx\n", + (addr_t)virt_addr, ds->phys_addr()); + return 0; + } + + return virt_addr; +} diff --git a/base-codezero/src/core/include/core_rm_session.h b/base-codezero/src/core/include/core_rm_session.h new file mode 100644 index 000000000..064115d70 --- /dev/null +++ b/base-codezero/src/core/include/core_rm_session.h @@ -0,0 +1,52 @@ +/* + * \brief Core-local region manager session + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_RM_SESSION_H_ +#define _CORE__INCLUDE__CORE_RM_SESSION_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +namespace Genode { + + class Core_rm_session : public Rm_session + { + private: + + Rpc_entrypoint *_ds_ep; + + public: + + Core_rm_session(Rpc_entrypoint *ds_ep) : _ds_ep(ds_ep) { } + + Local_addr attach(Dataspace_capability ds_cap, size_t size = 0, + off_t offset = 0, bool use_local_addr = false, + Local_addr local_addr = 0); + + void detach(Local_addr) { } + + Pager_capability add_client(Thread_capability) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_RM_SESSION_H_ */ diff --git a/base-codezero/src/core/include/irq_session_component.h b/base-codezero/src/core/include/irq_session_component.h new file mode 100644 index 000000000..7d51690bc --- /dev/null +++ b/base-codezero/src/core/include/irq_session_component.h @@ -0,0 +1,71 @@ +/* + * \brief IRQ session interface for NOVA + * \author Norman Feske + * \date 2010-01-30 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ + +#include +#include + +#include + +namespace Genode { + + class Irq_session_component : public Rpc_object, + public List::Element + { + private: + + enum { STACK_SIZE = 4096 }; + + unsigned _irq_number; + Range_allocator *_irq_alloc; + Rpc_entrypoint _entrypoint; + Irq_session_capability _cap; + bool _attached; + + public: + + /** + * Constructor + * + * \param cap_session capability session to use + * \param irq_alloc platform-dependent IRQ allocator + * \param args session construction arguments + */ + Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args); + + /** + * Destructor + */ + ~Irq_session_component(); + + /** + * Return capability to this session + * + * If an initialization error occurs, returned capability is invalid. + */ + Irq_session_capability cap() const { return _cap; } + + + /*************************** + ** Irq session interface ** + ***************************/ + + void wait_for_irq(); + }; +} + +#endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */ diff --git a/base-codezero/src/core/include/map_local.h b/base-codezero/src/core/include/map_local.h new file mode 100644 index 000000000..a9822721e --- /dev/null +++ b/base-codezero/src/core/include/map_local.h @@ -0,0 +1,66 @@ +/* + * \brief Core-local mapping + * \author Norman Feske + * \date 2010-02-15 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__MAP_LOCAL_H_ +#define _CORE__INCLUDE__MAP_LOCAL_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + + +namespace Genode { + + /** + * Map physical pages to core-local virtual address range + * + * On Codezero, mappings originate from the physical address space. + * + * \param from_phys physical source address + * \param to_virt core-local destination address + * \param num_pages number of pages to map + * + * \return true on success + */ + inline bool map_local(addr_t from_phys, addr_t to_virt, size_t num_pages) + { + using namespace Codezero; + + int res = l4_map((void *)from_phys, (void *)to_virt, + num_pages, MAP_USR_RW, thread_myself()); + if (res < 0) { + PERR("l4_map phys 0x%lx -> 0x%lx returned %d", from_phys, to_virt, res); + return false; + } + + return true; + } + + + inline bool unmap_local(addr_t virt_addr, size_t num_pages) + { + using namespace Codezero; + + int res = l4_unmap((void *)virt_addr, num_pages, thread_myself()); + if (res < 0) { + PERR("l4_unmap returned %d", res); + return false; + } + + return true; + } +} + +#endif /* _CORE__INCLUDE__MAP_LOCAL_H_ */ diff --git a/base-codezero/src/core/include/platform.h b/base-codezero/src/core/include/platform.h new file mode 100644 index 000000000..dcb456e01 --- /dev/null +++ b/base-codezero/src/core/include/platform.h @@ -0,0 +1,72 @@ +/* + * \brief Platform interface + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_H_ +#define _CORE__INCLUDE__PLATFORM_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include +#include + +namespace Genode { + + class Platform : public Platform_generic + { + private: + + typedef Core_mem_allocator::Phys_allocator Phys_allocator; + + Core_mem_allocator _core_mem_alloc; /* core-accessible memory */ + Phys_allocator _io_mem_alloc; /* MMIO allocator */ + Phys_allocator _io_port_alloc; /* I/O port allocator */ + Phys_allocator _irq_alloc; /* IRQ allocator */ + Rom_fs _rom_fs; /* ROM file system */ + + /** + * Virtual address range usable by non-core processes + */ + addr_t _vm_base; + size_t _vm_size; + + int _init_rom_fs(); + + public: + + /** + * Constructor + */ + Platform(); + + + /******************************** + ** Generic platform interface ** + ********************************/ + + Range_allocator *ram_alloc() { return _core_mem_alloc.phys_alloc(); } + Range_allocator *io_mem_alloc() { return &_io_mem_alloc; } + Range_allocator *io_port_alloc() { return &_io_port_alloc; } + Range_allocator *irq_alloc() { return &_irq_alloc; } + Range_allocator *region_alloc() { return _core_mem_alloc.virt_alloc(); } + Allocator *core_mem_alloc() { return &_core_mem_alloc; } + addr_t vm_start() const { return _vm_base; } + size_t vm_size() const { return _vm_size; } + Rom_fs *rom_fs() { return &_rom_fs; } + + void wait_for_exit(); + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-codezero/src/core/include/platform_pd.h b/base-codezero/src/core/include/platform_pd.h new file mode 100644 index 000000000..eb6a7e42d --- /dev/null +++ b/base-codezero/src/core/include/platform_pd.h @@ -0,0 +1,74 @@ +/* + * \brief Protection-domain facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_PD_H_ +#define _CORE__INCLUDE__PLATFORM_PD_H_ + +/* core includes */ +#include + +/* Codezero includes */ +#include + +namespace Genode { + + class Platform_thread; + class Platform_pd + { + private: + + enum { MAX_THREADS_PER_PD = 32 }; + enum { UTCB_VIRT_BASE = 0x30000000 }; + enum { UTCB_AREA_SIZE = MAX_THREADS_PER_PD*sizeof(struct Codezero::utcb) }; + + int _space_id; + + bool utcb_in_use[MAX_THREADS_PER_PD]; + + public: + + + /** + * Constructors + */ + Platform_pd(bool core); + Platform_pd(signed pd_id = -1, bool create = true); + + /** + * Destructor + */ + ~Platform_pd(); + + /** + * Bind thread to protection domain + * + * \return 0 on success or + * -1 if thread ID allocation failed. + */ + int bind_thread(Platform_thread *thread); + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread *thread); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent) { return 0; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ diff --git a/base-codezero/src/core/include/platform_thread.h b/base-codezero/src/core/include/platform_thread.h new file mode 100644 index 000000000..1895715e4 --- /dev/null +++ b/base-codezero/src/core/include/platform_thread.h @@ -0,0 +1,135 @@ +/* + * \brief Thread facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__PLATFORM_THREAD_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Genode { + + class Platform_pd; + class Platform_thread + { + private: + + friend class Platform_pd; + + enum { PD_NAME_MAX_LEN = 64 }; + + int _tid; /* global codezero thread ID */ + int _space_id; + addr_t _utcb; + char _name[PD_NAME_MAX_LEN]; + Pager_object *_pager; + + /** + * Assign physical thread ID and UTCB address to thread + * + * This function is called from 'Platform_pd::bind_thread'. + */ + void _assign_physical_thread(int tid, int space_id, addr_t utcb) { + _tid = tid; _space_id = space_id; _utcb = utcb; } + + public: + + enum { THREAD_INVALID = -1 }; /* invalid thread number */ + + /** + * Constructor + */ + Platform_thread(const char *name = 0, unsigned priority = 0, + int thread_id = THREAD_INVALID); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * \param cpu_no target cpu + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp, unsigned int cpu_no = 0); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Set pager capability + */ + Pager_object *pager(Pager_object *pager) const { return _pager; } + void pager(Pager_object *pager) { _pager = pager; } + Pager_object *pager() { return _pager; } + + /** + * Return identification of thread when faulting + */ + unsigned long pager_object_badge() const { return _tid; } + + /** + * Set the executing CPU for this thread. + */ + void set_cpu(unsigned int cpu_no); + + /** + * Get thread name + */ + const char *name() const { return "noname"; } + + + /*********************** + ** Codezero specific ** + ***********************/ + + addr_t utcb() const { return _utcb; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-codezero/src/core/include/util.h b/base-codezero/src/core/include/util.h new file mode 100644 index 000000000..3524d02c2 --- /dev/null +++ b/base-codezero/src/core/include/util.h @@ -0,0 +1,46 @@ +/* + * \brief Core-internal utilities + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__UTIL_H_ +#define _CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include + +/* Codezero includes */ +#include + +namespace Genode { + + inline size_t get_page_size_log2() { return 12; } + inline size_t get_page_size() { return 1 << get_page_size_log2(); } + inline addr_t get_page_mask() { return ~(get_page_size() - 1); } + inline addr_t trunc_page(addr_t addr) { return addr & get_page_mask(); } + inline addr_t round_page(addr_t addr) { return trunc_page(addr + get_page_size() - 1); } + + inline addr_t map_src_addr(addr_t core_local, addr_t phys) { return phys; } + inline size_t constrain_map_size_log2(size_t size_log2) { return get_page_size_log2(); } + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long faulter_badge) + { + printf("%s (%s pf_addr=%p pf_ip=%p from %02lx)\n", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, + faulter_badge); + } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-codezero/src/core/io_mem_session_support.cc b/base-codezero/src/core/io_mem_session_support.cc new file mode 100644 index 000000000..6b1f5715e --- /dev/null +++ b/base-codezero/src/core/io_mem_session_support.cc @@ -0,0 +1,27 @@ +/* + * \brief Implementation of the IO_MEM session interface + * \author Norman Feske + * \date 2009-03-29 + * + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include + + +using namespace Genode; + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ } + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ return 0; } diff --git a/base-codezero/src/core/io_port_session_component.cc b/base-codezero/src/core/io_port_session_component.cc new file mode 100644 index 000000000..c90a87b7e --- /dev/null +++ b/base-codezero/src/core/io_port_session_component.cc @@ -0,0 +1,58 @@ +/* + * \brief Implementation of the IO_PORT session interface + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "io_port_session_component.h" + +using namespace Genode; + + +/************** + ** Port API ** + **************/ + +unsigned char Io_port_session_component::inb(unsigned short address) { + return 0; } + + +unsigned short Io_port_session_component::inw(unsigned short address) { + return 0; } + + +unsigned Io_port_session_component::inl(unsigned short address) { + return 0; } + + +void Io_port_session_component::outb(unsigned short address, unsigned char value) +{ } + + +void Io_port_session_component::outw(unsigned short address, unsigned short value) +{ } + + +void Io_port_session_component::outl(unsigned short address, unsigned value) +{ } + + +/****************************** + ** Constructor / destructor ** + ******************************/ + +Io_port_session_component::Io_port_session_component(Range_allocator *io_port_alloc, + const char *args) +: _io_port_alloc(io_port_alloc) +{ } + + +Io_port_session_component::~Io_port_session_component() +{ } diff --git a/base-codezero/src/core/irq_session_component.cc b/base-codezero/src/core/irq_session_component.cc new file mode 100644 index 000000000..4975c1a3c --- /dev/null +++ b/base-codezero/src/core/irq_session_component.cc @@ -0,0 +1,72 @@ +/* + * \brief Implementation of IRQ session component + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +/* Codezero includes */ +#include + + +using namespace Genode; + + +void Irq_session_component::wait_for_irq() +{ + using namespace Codezero; + + /* attach thread to IRQ when first called */ + if (!_attached) { + int ret = l4_irq_control(IRQ_CONTROL_REGISTER, 0, _irq_number); + if (ret < 0) { + PERR("l4_irq_control(IRQ_CONTROL_REGISTER) returned %d", ret); + sleep_forever(); + } + _attached = true; + } + + /* block for IRQ */ + int ret = l4_irq_control(IRQ_CONTROL_WAIT, 0, _irq_number); + if (ret < 0) + PWRN("l4_irq_control(IRQ_CONTROL_WAIT) returned %d", ret); +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _entrypoint(cap_session, STACK_SIZE, "irq"), + _attached(false) +{ + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + if (!irq_alloc || (irq_number == -1)|| + irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK) { + PERR("unavailable IRQ %lx requested", irq_number); + return; + } + _irq_number = irq_number; + _cap = Irq_session_capability(_entrypoint.manage(this)); +} + + +Irq_session_component::~Irq_session_component() +{ + PERR("not yet implemented"); +} + diff --git a/base-codezero/src/core/platform.cc b/base-codezero/src/core/platform.cc new file mode 100644 index 000000000..b7c2551a9 --- /dev/null +++ b/base-codezero/src/core/platform.cc @@ -0,0 +1,293 @@ +/* + * \brief Platform interface implementation + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include +#include + +/* Codezero includes */ +#include + +using namespace Genode; + +enum { verbose_boot_info = true }; + +/* + * Memory-layout information provided by the linker script + */ + +/* virtual address range consumed by core's program image */ +extern unsigned _prog_img_beg, _prog_img_end; + +/* physical address range occupied by core */ +extern addr_t _vma_start, _lma_start; + + +/************************** + ** Boot-module handling ** + **************************/ + +/** + * Scan ROM module image for boot modules + * + * By convention, the boot modules start at the page after core's BSS segment. + */ +int Platform::_init_rom_fs() +{ + /** + * Format of module meta-data as found in the ROM module image + */ + struct Module + { + long name; /* physical address of null-terminated string */ + long base; /* physical address of module data */ + long size; /* size of module data in bytes */ + }; + + /* find base address of ROM module image */ + addr_t phys_base = round_page((addr_t)&_prog_img_end); + + /* map the first page of the image containing the module meta data */ + class Out_of_virtual_memory_during_rom_fs_init { }; + void *virt_base = 0; + if (!_core_mem_alloc.virt_alloc()->alloc(get_page_size(), &virt_base)) + throw Out_of_virtual_memory_during_rom_fs_init(); + + if (!map_local(phys_base, (addr_t)virt_base, 1)) { + PERR("map_local failed"); + return -1; + } + + /* remove page containing module infos from physical memory allocator */ + _core_mem_alloc.phys_alloc()->remove_range(phys_base, get_page_size()); + + /* validate the presence of a ROM image by checking the magic cookie */ + const char cookie[4] = {'G', 'R', 'O', 'M'}; + for (size_t i = 0; i < sizeof(cookie); i++) + if (cookie[i] != ((char *)virt_base)[i]) { + PERR("could not detect ROM modules"); + return -2; + } + + printf("detected ROM module image at 0x%p\n", (void *)phys_base); + + /* detect overly large meta data, we only support 4K */ + addr_t end_of_header = ((long *)virt_base)[1]; + size_t header_size = end_of_header - (long)phys_base; + if (header_size > get_page_size()) { + PERR("ROM fs module header exceeds %d bytes", get_page_size()); + return -3; + } + + /* start of module list */ + Module *module = (Module *)((addr_t)virt_base + 2*sizeof(long)); + + /* + * Interate over module list and populate core's ROM file system with + * 'Rom_module' objects. + */ + for (; module->name; module++) { + + /* convert physical address of module name to core-local address */ + char *name = (char *)(module->name - phys_base + (addr_t)virt_base); + + printf("ROM module \"%s\" at physical address 0x%p, size=%zd\n", + name, (void *)module->base, (size_t)module->size); + + Rom_module *rom_module = new (core_mem_alloc()) + Rom_module(module->base, module->size, name); + + _rom_fs.insert(rom_module); + + /* remove module from physical memory allocator */ + _core_mem_alloc.phys_alloc()->remove_range(module->base, round_page(module->size)); + } + return 0; +} + + +/**************************************** + ** Support for core memory management ** + ****************************************/ + +bool Core_mem_allocator::Mapped_mem_allocator::_map_local(addr_t virt_addr, addr_t phys_addr, unsigned size_log2) +{ + return map_local(phys_addr, virt_addr, 1 << (size_log2 - get_page_size_log2())); +} + + +/************************ + ** Platform interface ** + ************************/ + +Platform::Platform() : + _io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()), + _irq_alloc(core_mem_alloc()), _vm_base(0), _vm_size(0) +{ + using namespace Codezero; + + /* init core UTCB */ + static char main_utcb[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); + static struct exregs_data exregs; + exregs_set_utcb(&exregs, (unsigned long)&main_utcb[0]); + l4_exchange_registers(&exregs, thread_myself()); + + /* error handling is futile at this point */ + + /* read number of capabilities */ + int num_caps; + int ret; + if ((ret = l4_capability_control(CAP_CONTROL_NCAPS, + 0, &num_caps)) < 0) { + PERR("l4_capability_control(CAP_CONTROL_NCAPS) returned %d", ret); + class Could_not_obtain_num_of_capabilities { }; + throw Could_not_obtain_num_of_capabilities(); + } + + struct capability cap_array[num_caps]; + + if (verbose_boot_info) + printf("allocated cap array[%d] of size %d on stack\n", + num_caps, sizeof(cap_array)); + + /* read all capabilities */ + if ((ret = l4_capability_control(CAP_CONTROL_READ, + 0, cap_array)) < 0) { + PERR("l4_capability_control(CAP_CONTROL_READ) returned %d", ret); + class Read_caps_failed { }; + throw Read_caps_failed(); + } + + /* initialize core allocators */ + bool phys_mem_defined = false; + addr_t dev_mem_base = 0; + for (int i = 0; i < num_caps; i++) { + struct capability *cap = &cap_array[i]; + + addr_t base = cap->start << get_page_size_log2(), + size = cap->size << get_page_size_log2(); + + if (verbose_boot_info) + printf("cap type=%x, rtype=%x, base=%lx, size=%lx\n", + cap_type(cap), cap_rtype(cap), base, size); + + switch (cap_type(cap)) { + + case CAP_TYPE_MAP_VIRTMEM: + + /* + * Use first non-UTCB virtual address range as default + * virtual memory range usable for all processes. + */ + if (_vm_size == 0) { + + /* exclude page at virtual address 0 */ + if (base == 0 && size >= get_page_size()) { + base += get_page_size(); + size -= get_page_size(); + } + + _vm_base = base; + _vm_size = size; + + /* add range as free range to core's virtual address allocator */ + _core_mem_alloc.virt_alloc()->add_range(base, size); + break; + } + + PWRN("ignoring additional virtual address range [%lx,%lx)", + base, base + size); + break; + + case CAP_TYPE_MAP_PHYSMEM: + + /* + * We interpret the first physical memory resource that is bigger + * than typical device resources as RAM. + */ + enum { RAM_SIZE_MIN = 16*1024*1024 }; + if (!phys_mem_defined && size > RAM_SIZE_MIN) { + _core_mem_alloc.phys_alloc()->add_range(base, size); + phys_mem_defined = true; + dev_mem_base = base + size; + } + break; + + case CAP_TYPE_IPC: + case CAP_TYPE_UMUTEX: + case CAP_TYPE_IRQCTRL: + case CAP_TYPE_QUANTITY: + break; + } + } + + addr_t core_virt_beg = trunc_page((addr_t)&_prog_img_beg), + core_virt_end = round_page((addr_t)&_prog_img_end); + size_t core_size = core_virt_end - core_virt_beg; + + printf("core image:\n"); + printf(" virtual address range [%08lx,%08lx) size=0x%zx\n", + core_virt_beg, core_virt_end, core_size); + printf(" physically located at 0x%08lx\n", _lma_start); + + /* remove core image from core's virtual address allocator */ + _core_mem_alloc.virt_alloc()->remove_range(core_virt_beg, core_size); + + /* preserve context area in core's virtual address space */ + _core_mem_alloc.virt_alloc()->raw()->remove_range(Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + Thread_base::CONTEXT_AREA_VIRTUAL_SIZE); + + /* remove used core memory from physical memory allocator */ + _core_mem_alloc.phys_alloc()->remove_range(_lma_start, core_size); + + /* remove magically mapped UART from core virtual memory */ + _core_mem_alloc.virt_alloc()->remove_range(USERSPACE_CONSOLE_VBASE, get_page_size()); + + /* add boot modules to ROM fs */ + if (_init_rom_fs() < 0) { + PERR("initialization of romfs failed - halt."); + while(1); + } + + /* initialize interrupt allocator */ + _irq_alloc.add_range(0, 255); + + /* regard physical addresses higher than memory area as MMIO */ + _io_mem_alloc.add_range(dev_mem_base, 0x80000000 - dev_mem_base); + + /* + * Print statistics about allocator initialization + */ + printf("VM area at [%08lx,%08lx)\n", _vm_base, _vm_base + _vm_size); + + if (verbose_boot_info) { + printf(":phys_alloc: "); _core_mem_alloc.phys_alloc()->raw()->dump_addr_tree(); + printf(":virt_alloc: "); _core_mem_alloc.virt_alloc()->raw()->dump_addr_tree(); + printf(":io_mem_alloc: "); _io_mem_alloc.raw()->dump_addr_tree(); + } +} + + +void Platform::wait_for_exit() +{ + sleep_forever(); +} + + +void Core_parent::exit(int exit_value) { } diff --git a/base-codezero/src/core/platform_pd.cc b/base-codezero/src/core/platform_pd.cc new file mode 100644 index 000000000..2dbd758a3 --- /dev/null +++ b/base-codezero/src/core/platform_pd.cc @@ -0,0 +1,124 @@ +/* + * \brief Protection-domain facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include + +using namespace Genode; +using namespace Codezero; + + +/*************************** + ** Public object members ** + ***************************/ + +int Platform_pd::bind_thread(Platform_thread *thread) +{ + /* allocate new thread at the kernel */ + struct task_ids ids = { 1, _space_id, TASK_ID_INVALID }; + int ret = l4_thread_control(THREAD_CREATE | TC_SHARE_SPACE, &ids); + if (ret < 0) { + PERR("l4_thread_control returned %d, tid=%d\n", ret, ids.tid); + return -1; + } + + /* allocate UTCB for new thread */ + int utcb_idx; + for (utcb_idx = 0; utcb_idx < MAX_THREADS_PER_PD; utcb_idx++) + if (!utcb_in_use[utcb_idx]) break; + + if (utcb_idx == MAX_THREADS_PER_PD) { + PERR("UTCB allocation failed"); + return -2; + } + + /* mark UTCB as being in use */ + utcb_in_use[utcb_idx] = true; + + /* map UTCB area for the first thread of a new PD */ + if (utcb_idx == 0) { + void *utcb_phys = 0; + if (!platform()->ram_alloc()->alloc(UTCB_AREA_SIZE, &utcb_phys)) { + PERR("could not allocate physical pages for UTCB"); + return -3; + } + + ret = l4_map(utcb_phys, (void *)UTCB_VIRT_BASE, + UTCB_AREA_SIZE/get_page_size(), MAP_USR_RW, ids.tid); + if (ret < 0) { + PERR("UTCB mapping into new PD failed, ret=%d", ret); + return -4; + } + } + + addr_t utcb_addr = UTCB_VIRT_BASE + utcb_idx*sizeof(struct utcb); + thread->_assign_physical_thread(ids.tid, _space_id, utcb_addr); + return 0; +} + + +void Platform_pd::unbind_thread(Platform_thread *thread) +{ + /* find UTCB index of thread */ + unsigned utcb_idx; + for (utcb_idx = 0; utcb_idx < MAX_THREADS_PER_PD; utcb_idx++) + if (thread->utcb() == UTCB_VIRT_BASE + utcb_idx*sizeof(struct utcb)) + break; + + if (utcb_idx == MAX_THREADS_PER_PD) { + PWRN("could not find UTCB index of thread"); + return; + } + + utcb_in_use[utcb_idx] = false; + + PWRN("not fully implemented"); +} + + +Platform_pd::Platform_pd(bool core) +{ + PWRN("not yet implemented"); +} + + +Platform_pd::Platform_pd(signed pd_id, bool create) : _space_id(-1) +{ + _space_id = -1; + + /* mark all UTCBs of the new PD as free */ + for (int i = 0; i < MAX_THREADS_PER_PD; i++) + utcb_in_use[i] = false; + + struct task_ids ids = { -1, -1, -1 }; + + int ret = l4_thread_control(THREAD_CREATE | TC_NEW_SPACE, &ids); + if (ret < 0) { + PERR("l4_thread_control(THREAD_CREATE | TC_NEW_SPACE) returned %d", ret); + return; + } + + /* set space ID to valid value to indicate success */ + _space_id = ids.spid; +} + + +Platform_pd::~Platform_pd() +{ + PWRN("not yet implemented"); +} diff --git a/base-codezero/src/core/platform_thread.cc b/base-codezero/src/core/platform_thread.cc new file mode 100644 index 000000000..bead355a1 --- /dev/null +++ b/base-codezero/src/core/platform_thread.cc @@ -0,0 +1,104 @@ +/* + * \brief Thread facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +/* Codezero includes */ +#include + +enum { verbose_thread_start = true }; + +using namespace Genode; +using namespace Codezero; + + +void Platform_thread::set_cpu(unsigned int cpu_no) +{ + PDBG("not yet implemented"); +} + + +int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no) +{ + Native_thread_id pager = _pager ? _pager->cap().tid() : -1; + + /* setup thread context */ + struct exregs_data exregs; + exregs.flags = 0; + exregs_set_stack(&exregs, (unsigned long)sp); + exregs_set_pc (&exregs, (unsigned long)ip); + exregs_set_pager(&exregs, pager.tid); + exregs_set_utcb (&exregs, _utcb); + + int ret = l4_exchange_registers(&exregs, _tid); + if (ret < 0) { + printf("l4_exchange_registers returned ret=%d\n", ret); + return -2; + } + + /* start execution */ + struct task_ids ids = { _tid, _space_id, _tid }; + ret = l4_thread_control(THREAD_RUN, &ids); + if (ret < 0) { + printf("Error: l4_thread_control(THREAD_RUN) returned %d\n", ret); + return -3; + } + + if (verbose_thread_start) + printf("core started thread \"%s\" with ID %d inside space ID %d\n", + _name, _tid, _space_id); + return 0; +} + + +void Platform_thread::pause() +{ + PDBG("not implemented"); +} + + +void Platform_thread::resume() +{ + PDBG("not implemented"); +} + + +int Platform_thread::state(Thread_state *state_dst) +{ + PDBG("not implemented"); + return -1; +} + + +void Platform_thread::cancel_blocking() +{ + PDBG("not implemented"); +} + + +Platform_thread::Platform_thread(const char *name, unsigned, int thread_id) +: _tid(-1) +{ + strncpy(_name, name, sizeof(_name)); +} + + +Platform_thread::~Platform_thread() +{ + PDBG("not implemented"); +} diff --git a/base-codezero/src/core/ram_session_support.cc b/base-codezero/src/core/ram_session_support.cc new file mode 100644 index 000000000..6e57322f1 --- /dev/null +++ b/base-codezero/src/core/ram_session_support.cc @@ -0,0 +1,65 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include +#include + + +using namespace Genode; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { } + +void Ram_session_component::_clear_ds (Dataspace_component *ds) +{ + using namespace Codezero; + + /* + * Map dataspace core-locally, memset, unmap dataspace + */ + + size_t page_rounded_size = (ds->size() + get_page_size() - 1) & get_page_mask(); + size_t num_pages = page_rounded_size >> get_page_size_log2(); + + /* allocate range in core's virtual address space */ + void *virt_addr; + if (!platform()->region_alloc()->alloc(page_rounded_size, &virt_addr)) { + PERR("Could not allocate virtual address range in core of size %zd\n", + page_rounded_size); + return; + } + + /* map the dataspace's physical pages to corresponding virtual addresses */ + if (!map_local(ds->phys_addr(), (addr_t)virt_addr, num_pages)) { + PERR("core-local memory mapping failed\n"); + return; + } + + memset(virt_addr, 0, ds->size()); + + /* unmap dataspace from core */ + if (!unmap_local((addr_t)virt_addr, num_pages)) { + PERR("could not unmap %zd pages from virtual address range at %p", + num_pages, virt_addr); + return; + } + + /* free core's virtual address space */ + platform()->region_alloc()->free(virt_addr, page_rounded_size); +} diff --git a/base-codezero/src/core/rm_session_support.cc b/base-codezero/src/core/rm_session_support.cc new file mode 100644 index 000000000..cae0eed70 --- /dev/null +++ b/base-codezero/src/core/rm_session_support.cc @@ -0,0 +1,28 @@ +/* + * \brief RM-session implementation + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include + +/* Codezero includes */ +#include + +using namespace Genode; +using namespace Codezero; + + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + l4_unmap((void *)virt_base, size >> get_page_size_log2(), badge()); +} diff --git a/base-codezero/src/core/target.inc b/base-codezero/src/core/target.inc new file mode 100644 index 000000000..3557657a2 --- /dev/null +++ b/base-codezero/src/core/target.inc @@ -0,0 +1,55 @@ +TARGET = core +LIBS = cxx ipc heap core_printf process pager lock \ + raw_signal raw_server + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = \ + main.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_start.cc \ + thread_bootstrap.cc \ + platform_thread.cc \ + platform_pd.cc \ + platform.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + core_rm_session.cc \ + core_mem_alloc.cc \ + dump_alloc.cc \ + context_area.cc + +INC_DIR = $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include \ + $(REP_DIR)/include/codezero/dummies + +vpath main.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath signal_source_component.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath core_mem_alloc.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath context_area.cc $(GEN_CORE_DIR) +vpath %.cc $(REP_DIR)/src/core +vpath thread_bootstrap.cc $(BASE_DIR)/src/base/thread +vpath thread.cc $(BASE_DIR)/src/base/thread + diff --git a/base-codezero/src/core/target.mk b/base-codezero/src/core/target.mk new file mode 100644 index 000000000..b1aacf1a0 --- /dev/null +++ b/base-codezero/src/core/target.mk @@ -0,0 +1,4 @@ +include $(PRG_DIR)/target.inc + +LD_TEXT_ADDR = 0x100000 + diff --git a/base-codezero/src/core/thread_start.cc b/base-codezero/src/core/thread_start.cc new file mode 100644 index 000000000..b642a276c --- /dev/null +++ b/base-codezero/src/core/thread_start.cc @@ -0,0 +1,121 @@ +/* + * \brief Implementation of Thread API interface for core + * \author Norman Feske + * \date 2006-05-03 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Codezero includes */ +#include + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include + +enum { verbose_thread_start = true }; + +using namespace Genode; + + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() { } + + +/** + * Create and start new thread + * + * \param space_no space ID in which the new thread will be executed + * \param sp initial stack pointer + * \param ip initial instruction pointer + * \return new thread ID, or + * negative error code + */ +inline int create_thread(int space_no, + void *sp, void *ip, + int pager_tid = 1) +{ + using namespace Codezero; + + struct task_ids ids = { 1, space_no, TASK_ID_INVALID }; + + /* allocate new thread at the kernel */ + unsigned long flags = THREAD_CREATE | TC_SHARE_SPACE | TC_SHARE_GROUP; + int ret = l4_thread_control(flags, &ids); + if (ret < 0) { + PERR("l4_thread_control returned %d, spid=%d\n", + ret, ids.spid); + return -1; + } + + unsigned long utcb_base_addr = (unsigned long)l4_get_utcb(); + + /* calculate utcb address of new thread */ + unsigned long new_utcb = utcb_base_addr + ids.tid*sizeof(struct utcb); + + /* setup thread context */ + struct exregs_data exregs; + exregs_set_stack(&exregs, (unsigned long)sp); + exregs_set_pc (&exregs, (unsigned long)ip); + exregs_set_pager(&exregs, pager_tid); + exregs_set_utcb (&exregs, new_utcb); + + ret = l4_exchange_registers(&exregs, ids.tid); + if (ret < 0) { + printf("l4_exchange_registers returned ret=%d\n", ret); + return -2; + } + + /* start execution */ + ret = l4_thread_control(THREAD_RUN, &ids); + if (ret < 0) { + printf("Error: l4_thread_control(THREAD_RUN) returned %d\n", ret); + return -3; + } + + /* return new thread ID allocated by the kernel */ + return ids.tid; +} + + +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + sleep_forever(); +} + + +void Thread_base::start() +{ + /* create and start platform thread */ + _tid.pt = new(platform()->core_mem_alloc()) Platform_thread(_context->name); + + _tid.l4id = create_thread(1, &_context->stack[-4], (void *)&_thread_start); + if (_tid.l4id.tid < 0) + PERR("create_thread returned %d", _tid.l4id.tid); + + if (verbose_thread_start) + printf("core started local thread \"%s\" with ID %d\n", + _context->name, _tid.l4id.tid); +} + + +void Thread_base::cancel_blocking() +{ + PWRN("not implemented"); +} + + diff --git a/base-codezero/src/kernel/target.mk b/base-codezero/src/kernel/target.mk new file mode 100644 index 000000000..a96dbcbef --- /dev/null +++ b/base-codezero/src/kernel/target.mk @@ -0,0 +1,74 @@ +TARGET = codezero + +-include $(BUILD_BASE_DIR)/etc/codezero.conf +ifeq ($(wildcard $(CODEZERO_DIR)),) +$(error No valid kernel configured in 'etc/codezero.conf') +endif + +include $(REP_DIR)/lib/mk/codezero_cml.inc + +TOOL_CHAIN_DIR = $(dir $(CROSS_DEV_PREFIX)) +CODEZERO_DST_DIR = $(BUILD_BASE_DIR)/kernel/codezero +CODEZERO_BUILD_DIR = $(CODEZERO_DST_DIR)/build + +.PHONY: $(TARGET) + +MIRROR_COPY := conts/baremetal/empty conts/userlibs \ + build.py include SConstruct src loader + +MIRROR_SYMLINK := scripts tools + +update_copy = $(VERBOSE)tar c -C $(CODEZERO_DIR) $(MIRROR_COPY) | tar x -C $(CODEZERO_DST_DIR) + +ifneq ($(VERBOSE),) +CODEZERO_STDOUT := > /dev/null +endif + +# +# Environment variables passed to the Codezero build system +# +BUILD_ENV = PATH=$(dir $(CROSS_DEV_PREFIX)):$$PATH + +# +# Local copy of the CML file used for supplying the configuration +# to the Codezero build system. +# +LOCAL_CONFIG_CML := $(shell pwd)/config.cml + +$(TARGET): $(CODEZERO_BUILD_DIR) + $(MSG_BUILD)kernel + $(update_copy) + $(VERBOSE)cd $(CODEZERO_DST_DIR); $(BUILD_ENV) ./build.py $(CODEZERO_STDOUT) + +# +# Mirror the parts of the Codezero source tree that are relevant for building +# the kernel +# +$(CODEZERO_DST_DIR): $(CODEZERO_DIR) + $(VERBOSE)test -d $@ || mkdir -p $@ + $(VERBOSE)for d in $(MIRROR_SYMLINK); do ln -sf $(realpath $^)/$$d $@/$$d; done + +$(CODEZERO_BUILD_DIR): $(CODEZERO_DST_DIR) $(CODEZERO_CML) + $(update_copy) + $(VERBOSE)cp $(CODEZERO_CML) $(LOCAL_CONFIG_CML) + @# + @# Create copy of the CML config in the local build directory to update + @# the tool chain parameters according to the CROSS_DEV_PREFIX configured + @# for Genode. + @# + $(VERBOSE)sed -i "/TOOLCHAIN_USERSPACE/s/\".*\"/\"$(notdir $(CROSS_DEV_PREFIX))\"/" $(LOCAL_CONFIG_CML) + $(VERBOSE)sed -i "/TOOLCHAIN_KERNEL/s/\".*\"/\"$(notdir $(CROSS_DEV_PREFIX))\"/" $(LOCAL_CONFIG_CML) + $(VERBOSE)cd $(CODEZERO_DST_DIR); $(BUILD_ENV) ./build.py -C -b -f $(LOCAL_CONFIG_CML) $(CODEZERO_STDOUT) + +clean cleanall: clean_codezero + +# +# Make sure to execute the 'clean_codezero' rule prior the generic clean +# rule in 'prg.mk' because the generic rule will attempt to remove $(TARGET) +# file, which is a directory in our case. +# +clean_prg_objects: clean_codezero + +clean_codezero: + $(VERBOSE)rm -f $(LOCAL_CONFIG_CML) + $(VERBOSE)rm -rf $(CODEZERO_DST_DIR) diff --git a/base-codezero/src/platform/_main_helper.h b/base-codezero/src/platform/_main_helper.h new file mode 100644 index 000000000..80a04f02f --- /dev/null +++ b/base-codezero/src/platform/_main_helper.h @@ -0,0 +1,67 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM___MAIN_HELPER_H_ +#define _PLATFORM___MAIN_HELPER_H_ + +#include + +/* make Codezero includes happy */ +extern "C" char *strncpy(char *dest, const char *src, Genode::size_t n); +extern "C" void *memcpy(void *dest, const void *src, Genode::size_t n); + +/* Codezero includes */ +#include + + +/**************************** + ** Codezero libl4 support ** + ****************************/ + +/* + * Unfortunately, the function 'exregs_print_registers' in 'exregs.c' refers to + * 'memset'. Because we do not want to link core against a C library, we have to + * resolve this function here. + */ +extern "C" void *memset(void *s, int c, Genode::size_t n) __attribute__((weak)); +extern "C" void *memset(void *s, int c, Genode::size_t n) +{ + return Genode::memset(s, c, n); +} + + +/* + * Same problem as for 'memset'. The 'printf' symbol is referenced from + * 'mutex.c' and 'exregs.c' of Codezero's libl4. + */ +extern "C" int printf(const char *format, ...) __attribute__((weak)); +extern "C" int printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + Genode::vprintf(format, list); + va_end(list); + return 0; +} + + +/************************** + ** Startup-code helpers ** + **************************/ + +static void main_thread_bootstrap() +{ + Codezero::__l4_init(); +} + +#endif /* _PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-codezero/src/platform/genode.ld b/base-codezero/src/platform/genode.ld new file mode 100644 index 000000000..064ccf6c0 --- /dev/null +++ b/base-codezero/src/platform/genode.ld @@ -0,0 +1,131 @@ +/* + * \brief Linker script for Genode programs + * \author Christian Helmuth + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* values taken from Codezero's mm0 linker script */ +/*physical_base = 0x00208000;*/ +/*virtual_base = 0xe0000000;*/ +/*offset = virtual_base - physical_base;*/ + +/* + * Addresses correspond to the linker script generated by + * the Codezero build system. + */ +vma_start = 0x100000; +lma_start = 0x40000; +offset = vma_start - lma_start; + + +ENTRY(_start) + +PHDRS +{ + ro PT_LOAD; + rw PT_LOAD; +} + +SECTIONS +{ + . = vma_start; + + .text : AT (ADDR(.text) - offset) { + /* begin of program image (link address) */ + _prog_img_beg = .; + + *(.text.crt0) + *(.init) + *(.text .text.* .gnu.linkonce.t.*) + *(.fini) + *(.rodata .rodata.* .gnu.linkonce.r.*) + + . = ALIGN(0x08); + + _ctors_start = .; + KEEP (*(.ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.init_array)) /* list of constructors specific for ARM eabi */ + _ctors_end = .; + _dtors_start = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + _dtors_end = .; + } : ro = 0x90909090 + + /* Linux: exception section for uaccess mechanism */ + __ex_table : { *(__ex_table) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } + + . = ALIGN(0x1000); + + _prog_img_data = .; + + .data : AT (ADDR(.data) - offset) { + /* + * Leave space for parent capability parameters at start of data + * section. The protection domain creator is reponsible for storing + * sane values here. + */ + _parent_cap = .; + LONG(0xffffffff); + LONG(0xffffffff); + _vma_start = .; + LONG(vma_start); + _lma_start = .; + LONG(lma_start); + + *(.data .data.* .gnu.linkonce.d.*) + } : rw + + /* exception frames for C++ */ + .eh_frame : { + __eh_frame_start__ = .; + KEEP (*(.eh_frame)) + LONG(0) + } : rw + + .init_array : { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .gcc_except_table : { KEEP(*(.gcc_except_table)) } + .dynamic : { *(.dynamic) } + + /* .ARM.exidx is sorted, so has to go in its own output section */ + __exidx_start = .; + .ARM.exidx : { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } + __exidx_end = .; + + .ARM.extab : { + *(.ARM.extab*) + } : rw + + . = ALIGN(4); + + .bss : AT (ADDR(.bss) - offset) { + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } + + /* end of program image -- must be after last section */ + _prog_img_end = .; + + /DISCARD/ : { + *(.note) + *(.note.ABI-tag) + *(.comment) + } +} diff --git a/base-codezero/tool/gen_romfs b/base-codezero/tool/gen_romfs new file mode 100755 index 000000000..eca46b04d --- /dev/null +++ b/base-codezero/tool/gen_romfs @@ -0,0 +1,202 @@ +#!/usr/bin/python + +import os, re, getopt, sys +from stat import ST_SIZE +from subprocess import PIPE, Popen + +verbose = 0 + + +# return address of 4K page following the spefified address +def round_page(addr): + page_size = 0x1000 + return (addr + page_size) & ~(page_size - 1) + + +def first_free_addr_after_program(elf, cross_prefix = ""): + try: + objdump = cross_prefix + "objdump" + objdump_output = Popen([objdump, "-p", elf], + stdout=PIPE).communicate()[0] + except OSError: + print "Error: execution of " + objdump + " failed, invalid cross-tool prefix?" + exit(3) + + # + # The output of 'objdump -p' contains the list of program segments. Each + # segment has two lines of text, the first containing the 'vaddr' value and + # the latter containing the 'memsz' value. For each line, we match for both + # 'vaddr' and 'memsz' fields. When observing a line with a 'memsz' field, + # we know that the previous line contained the corresponding 'vaddr' and + # that the end address of the segment is the sum of the current 'vaddr' + # and 'memsz' values. + # + max_end_addr = 0 + for line in objdump_output.splitlines(): + match_vaddr = re.compile(".*vaddr (0x[0-9a-f]*).*").match(line) + match_memsz = re.compile(".*memsz (0x[0-9a-f]*).*").match(line) + if (match_vaddr): + vaddr = int(match_vaddr.group(1), 0) + if (match_memsz): + memsz = int(match_memsz.group(1), 0) + max_end_addr = max(max_end_addr, vaddr + memsz) + + # align the first free address at the next page boundary + return round_page(max_end_addr) + + +def generate_modules_asm(modules): + """ + Generate assembly code aggregating boot-module data from specified files. + The generated assembly code looks as follows: + + /* + * The ELF image consists only of a data section. At file offset 0, there + * is a magic cookie that core validates when accessing the ROM fs. It is + * followed by the end address of the meta data. + */ + .section .data + .string "GROM" /* magic cookie used by core to identify a ROM fs image*/ + .long header_end /* end of ROM fs meta data */ + + /* + * Each module is represented by a struct of 3 long values. The first + * value is pointer to the module name. A null-pointer marks the end of + * the module list. + */ + .long mod1_name /* pointer to the null-terminated module name */ + .long mod1_start /* pointer to the module data */ + .long mod1_end - mod1_start /* size of the module data */ + + .long 0 + + /* + * For each module, there exists a null-terminated string labeled with + * 'mod_name' referenced by the module list above. + */ + mod1_name: + .string "name of data module" + .byte 0 + + header_end: + + /* + * The data of each module must be aligned at a page boundary to enable + * the mapping of individual modules to different address spaces. + */ + .align 4096 + mod1_start: .incbin "data" + mod1_end: + """ + + asm_src = "" + + # header + asm_src += ".section .data\nmodule_list:\n" + asm_src += ".ascii \"GROM\"\n" + asm_src += ".long header_end\n" + + # module list + i = 1 + for module in modules: + asm_src += ".long mod" + str(i) + "_name\n" + asm_src += ".long mod" + str(i) + "_start\n" + asm_src += ".long mod" + str(i) + "_end - mod" + str(i) + "_start\n" + i = i + 1 + asm_src += ".long 0\n" + + # module names + i = 1 + for module in modules: + asm_src += "mod" + str(i) + "_name: .string \"" + os.path.split(module)[1] + "\"; .byte 0\n" + i = i + 1 + + asm_src += "header_end:\n" + + # module data + i = 1 + for module in modules: + asm_src += ".p2align 12,0\n" + asm_src += "mod" + str(i) + "_start: .incbin \"" + module + "\"; " + asm_src += "mod" + str(i) + "_end:\n" + i = i + 1 + + return asm_src + +instructions = """ +usage: gen_romfs [-v] [-p ] -c -o [modules ...] + +Generates Genode ROM file system as ELF file loadable into a Codezero container + + -c|--core ELF binary of Genode's core + -o|--output name of ELF image to generate + -p|--prefix cross toolchain prefix + -v|--verbose print details about generated ROM file systemn +""" + +def usage(): + print instructions + +def user_error(message): + print "Error: " + message + usage + sys.exit(2) + +# default values for command-line arguments +cross_prefix = "" +core_elf = "" +dst_elf = "" + +# parse command line arguments +try: + opts, modules = getopt.getopt(sys.argv[1:], + "c:o:p:v", + ["core=", "output=", "prefix=", "verbose"]) +except getopt.GetoptError: + usage() + sys.exit(2) +for opt, arg in opts: + if opt in ("-c", "--core"): + core_elf = arg + elif opt in ("-o", "--output"): + dst_elf = arg + elif opt in ("-p", "--prefix"): + cross_prefix = arg + elif opt in ("-v", "--verbose"): + verbose = 1 + else: + user_error("invalid argument \"" + arg + "\"") + +# validate arguments +if (core_elf == ""): user_error("no core binary specified") +if (len(modules) == 0): user_error("no modules specified") +if (dst_elf == ""): user_error("no output file spefied") + +# determine destination address of the modules ELF image +modules_start_addr = first_free_addr_after_program(core_elf, cross_prefix) + +if (verbose): + print "module address: " + hex(modules_start_addr) + +# generate assembly code aggregating the module data +asm_src = generate_modules_asm(modules) + +if (verbose): + print "generated assember code:" + for line in asm_src.splitlines(): + print " " + line + +# invoke assembler and linker through the gcc front end +gcc_cmd = [cross_prefix + "gcc", + "-nostdlib", + "-x", "assembler", + "-Wl,--entry=0", + "-Wl,--section-start=.data=" + hex(modules_start_addr), + "-o", dst_elf, + "-"] + +if (verbose): + print "gcc command line:" + print " " + ' '.join(gcc_cmd) + +Popen(gcc_cmd, stdin=PIPE).communicate(asm_src)[0] diff --git a/base-fiasco/Makefile b/base-fiasco/Makefile new file mode 100644 index 000000000..633ec3156 --- /dev/null +++ b/base-fiasco/Makefile @@ -0,0 +1,44 @@ +# +# \brief Download, and unpack Fiasco and addtional needed tools (sigma0, bootstrap) +# \author Stefan Kalkowski +# \date 2011-07-18 +# + +VERBOSE ?= @ +ECHO = @echo +DOWNLOAD_DIR = download +CONTRIB_DIR = contrib +FIASCO_ARCHIVE = 3rd_fiasco.tar.bz2 +FIASCO_URI = http://downloads.sourceforge.net/project/genode/3rd/$(FIASCO_ARCHIVE) + +# +# Print help information by default +# +help: + $(ECHO) + $(ECHO) "Prepare the Fiasco base repository" + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - download and extract the Fiasco source code" + $(ECHO) "clean - clean everything except downloaded archives" + $(ECHO) "cleanall - clean everything including downloaded archives" + $(ECHO) + +$(DOWNLOAD_DIR)/$(FIASCO_ARCHIVE): + $(ECHO) "downloading source code to '$(DOWNLOAD_DIR)/'" + $(VERBOSE)mkdir -p $(DOWNLOAD_DIR) + $(VERBOSE)wget -c $(FIASCO_URI) -O $@ + +$(CONTRIB_DIR): $(DOWNLOAD_DIR)/$(FIASCO_ARCHIVE) + $(ECHO) "unpacking source code to '$(CONTRIB_DIR)/'" + $(VERBOSE)tar xjf $< + $(VERBOSE)mv 3rd $@ + $(VERBOSE)touch $@ + +prepare: $(CONTRIB_DIR) + +clean: + $(VERBOSE)rm -rf $(CONTRIB_DIR) + +cleanall: clean + $(VERBOSE)rm -rf $(DOWNLOAD_DIR) diff --git a/base-fiasco/README b/base-fiasco/README new file mode 100644 index 000000000..8129cac57 --- /dev/null +++ b/base-fiasco/README @@ -0,0 +1,4 @@ +This repository contains the L4/Fiasco-specific implementation of Genode. + +For instructions to build and start the Fiasco version of Genode, please +consult the documentation located at 'base-fiasco/doc/fiasco.txt'. diff --git a/base-fiasco/config/kernel-config.x86 b/base-fiasco/config/kernel-config.x86 new file mode 100644 index 000000000..19d0f4013 --- /dev/null +++ b/base-fiasco/config/kernel-config.x86 @@ -0,0 +1,95 @@ +# +# Automatically generated make config: don't edit +# Fiasco kernel version: SVN +# Thu Jul 21 16:51:09 2011 +# + +# +# Target configuration +# +CONFIG_IA32=y +# CONFIG_AMD64 is not set +# CONFIG_ARM is not set +CONFIG_PF_PC=y +# CONFIG_PF_UX is not set +# CONFIG_PF_REALVIEW is not set +# CONFIG_PF_INTEGRATOR is not set +# CONFIG_PF_XSCALE is not set +# CONFIG_PF_SA1100 is not set +CONFIG_ABI_V2=y +# CONFIG_ARM_PXA is not set +# CONFIG_ARM_SA is not set +# CONFIG_ARM_920T is not set +# CONFIG_ARM_926 is not set +# CONFIG_ARM_1176 is not set +# CONFIG_ARM_MPCORE is not set +# CONFIG_ARM_CORTEX_A8 is not set +# CONFIG_IA32_486 is not set +CONFIG_IA32_586=y +# CONFIG_IA32_686 is not set +# CONFIG_IA32_P2 is not set +# CONFIG_IA32_P3 is not set +# CONFIG_IA32_P4 is not set +# CONFIG_IA32_PM is not set +# CONFIG_IA32_K6 is not set +# CONFIG_IA32_K7 is not set +# CONFIG_IA32_K8 is not set +# CONFIG_AMD64_K8 is not set +CONFIG_SCHED_PIT=y +# CONFIG_SCHED_RTC is not set +# CONFIG_SCHED_APIC is not set +# CONFIG_WORKAROUND_AMD_FPU_LEAK is not set +CONFIG_REGPARM3=y + +# +# Kernel options +# +CONFIG_HANDLE_SEGMENTS=y +# CONFIG_PL0_HACK is not set +CONFIG_TASK_CAPS=y +# CONFIG_USER_LOCKS is not set +CONFIG_CONTEXT_4K=y +CONFIG_IO_PROT=y +# CONFIG_IO_PROT_IOPL_3 is not set +CONFIG_SYNC_TSC=y +CONFIG_FINE_GRAINED_CPUTIME=y + +# +# Debugging +# +CONFIG_INLINE=y +# CONFIG_NDEBUG is not set +# CONFIG_NO_FRAME_PTR is not set +# CONFIG_STACK_DEPTH is not set +# CONFIG_LIST_ALLOC_SANITY is not set +# CONFIG_BEFORE_IRET_SANITY is not set +CONFIG_GSTABS=y +# CONFIG_WATCHDOG is not set +CONFIG_SERIAL=y +CONFIG_JDB=y +# CONFIG_JDB_LOGGING is not set +# CONFIG_JDB_ACCOUNTING is not set +# CONFIG_JDB_MISC is not set +CONFIG_POWERSAVE_GETCHAR=y +# CONFIG_WARN_NONE is not set +# CONFIG_WARN_WARNING is not set +CONFIG_WARN_ANY=y + +# +# Compiling +# +CONFIG_CC="gcc" +CONFIG_CXX="g++" +CONFIG_HOST_CC="gcc" +CONFIG_HOST_CXX="g++" +# CONFIG_VERBOSE is not set +# CONFIG_MAINTAINER_MODE is not set +CONFIG_LABEL="" +CONFIG_EXPERIMENTAL=y +CONFIG_PERF_CNT=y +CONFIG_BIT32=y +CONFIG_WARN_LEVEL=2 +CONFIG_XARCH="ia32" +CONFIG_IA32_TARGET="Intel Pentium" +CONFIG_ABI="v2" +CONFIG_DECEIT_BIT_DISABLES_SWITCH=y diff --git a/base-fiasco/config/l4env-config.x86 b/base-fiasco/config/l4env-config.x86 new file mode 100644 index 000000000..826fc5bc4 --- /dev/null +++ b/base-fiasco/config/l4env-config.x86 @@ -0,0 +1,83 @@ +# +# Automatically generated by configuration tool: don't edit +# + +# +# Target Architecture +# +BUILD_ARCH_x86=y +BUILD_ARCH_arm=n +BUILD_ARCH_amd64=n +BUILD_ARCH='x86' +CPU='586' +BUILD_ABI_l4v2=y +BUILD_ABI_linux=n +BUILD_ABI='l4v2' + +# +# Paths and Directories +# +DROPS_INSTDIR='$(DROPS_STDDIR)' + +# +# Verboseness and Messages +# +DEPEND_VERBOSE_SWITCH=n +DEPEND_VERBOSE='@' +VERBOSE_SWITCH=n +SHOWMESSAGES=y +BID_COLORED_PHASES=y + +# +# Compilers and Tools +# +BIDc_USE_SPECIAL_CC=y +HOST_CC="gcc -m32" +HOST_CXX="g++ -m32" +CC="$(SYSTEM_TARGET)gcc -m32" +CXX="$(SYSTEM_TARGET)g++ -m32" + +# +# Tools +# +YACC='byacc' +LEX='flex' +CTAGS='ctags' +ETAGS='etags' + +# +# Options +# +HAVE_LDSO=n +INT_CPP_NAME_SWITCH=y +INT_LD_NAME_SWITCH=y +BID_STRIP_PROGS=n +BID_GSTAB_SW=y +BID_CFLAGS_GSTAB='-gstabs+' +BID_GCC_OMIT_FP=n +BID_GENERATE_MAPFILE=n +BID_BUILD_DOC=y + +# +# Advanced +# +USE_UCLIBC=y +USE_DIETLIBC=n +BUILD_LOADER=n +BUILD_LOADER_PICS='libl4util.a libl4util_root.a libsigma0.a libnames.a libloaderif.a libcon.a libl4rm.a libbootmod.a libcon.a libconstream-server.a libdm_generic.a libdm_mem.a libgeneric_ts.a liblogserver.a liblogserver_capsule.a libsemaphore.a libthread.a libslab.a libgeneric_fprov.a libl4env_err.a libl4env.a libroot.a libc_be_l4env_start_stop.a libc_be_minimal_log_io.a libc_be_simple_mem.a libc_be_mmap.a libc_be_mmap_util.a libuclibc_support.a librtc.a libl4env-l4lx.a' +L4_CALL_SYSCALLS=y +L4_ABS_SYSCALLS=y +BID_CPPFLAGS_SYSCALLS='-DCONFIG_L4_CALL_SYSCALLS -DCONFIG_L4_ABS_SYSCALLS' +USE_TASKLIB=n +RELEASE_MODE=n +BID_BUILD_L4DIR_ONLY=n +CONFIG_LABEL='__none__' + +# +# Paths +# +LINUX24_INCDIR='$(OBJ_BASE)/include/linux-24 $(DROPS_STDDIR)/include/linux-24' +LINUX26_INCDIR='$(OBJ_BASE)/include/$(ARCH)/l4/linux-26-headers $(DROPS_STDDIR)/include/$(ARCH)/l4/linux-26-headers $(OBJ_BASE)/include/l4/linux-26-headers $(DROPS_STDDIR)/include/l4/linux-26-headers' +DDE_INCDIR='$(OBJ_BASE)/include/$(ARCH)/l4/dde_linux $(DROPS_STDDIR)/include/$(ARCH)/l4/dde_linux' +DDE26_INCDIR='$(OBJ_BASE)/include/$(ARCH)/l4/dde_linux26 $(DROPS_STDDIR)/include/$(ARCH)/l4/dde_linux26 $(OBJ_BASE)/include/l4/dde_linux26 $(DROPS_STDDIR)/include/l4/dde_linux26' +SDL_INCDIR='$(OBJ_BASE)/include/l4/sdl $(DROPS_STDDIR)/include/l4/sdl' diff --git a/base-fiasco/doc/fiasco.txt b/base-fiasco/doc/fiasco.txt new file mode 100644 index 000000000..cfd604a2a --- /dev/null +++ b/base-fiasco/doc/fiasco.txt @@ -0,0 +1,130 @@ + + ============================================= + How to use Genode with the Fiasco microkernel + ============================================= + + + Norman Feske, Christian Helmuth + +Abstract +######## + +This documentation describes the process of building and booting the L4/Fiasco +version of Genode. It assumes that you are familiar with basic concepts +described in the introductory documentation of Genode, namely the "How to start +exploring Genode" document. + + +Preconditions +############# + +The Fiasco version of Genode relies on the following components from +the source tree of the Fiasco microkernel and the L4 environment (which also +need additional tools). + + +Tools +===== + +* Gawk +* Bison +* Python + + +The Fiasco microkernel +====================== + +Information about Fiasco are provided at its official website: + +! http://os.inf.tu-dresden.de/fiasco/prev/ + +To download the kernel and integrate it with Genode, issue the following +command from within the 'base-fiasco' directory: + +! make prepare + +This command will download a prepackaged version of the kernel tested +with Genode. The build process of the kernel is integrated with Genode's +build system. After creating a build directory using 'create_builddir' +with 'fiasco_x86' as argument: + +! /tool/create_builddir fiasco_x86 \ +! BUILD_DIR= + +From within the new , the kernel can be compiled via + +! make kernel + +When using Genode's run mechanism, there is no need to explicitly +build the kernel. The run environment (see 'base-fiasco/run/env') +takes care of it. So you can simple execute run scripts from within +the build directory, for example: + +! make run/demo + + +Behind the scenes +================= + +For using the L4/Fiasco kernel, some basic user-level components and libraries +are needed. These are subsumed under the name L4 environment an are organized +as a number of packages. These packages provide two types of components. There +are low-level components for booting up and interfacing to Fiasco and there are +higher-level components that compose a basic OS infrastructure. For Genode, we +only rely on the low-level packages. + +Previous versions of Genode included all necessary sources from the L4 +environment in the '3rd/fiasco/snapshot' subdirectory. From release 10.02, the +'3rd' directory is no longer part of the release archive and also removed from +the subversion repository. Please download the '3rd_fiasco.tar.bz2' archive. +The source are organized as follows + +:'tool': contains the tools that are used by the build processes of + the L4 environment and Fiasco. For example, the build process of Fiasco + relies on the 'preprocess' tool. + +:'kernel': contains the Fiasco microkernel. + +:'l4': contains the L4-environment source tree. The single packages + are located at 'l4/pkg'. + +From all the packages of the L4 environment, the following three are of +interest for using Genode: + +:'pkg/l4sys': contains the Fiasco system-call bindings. + + The system-call bindings are a set of C-header files that define the + application-programming interface for invoking the system calls of + Fiasco. + +:'pkg/sigma0': + + Sigma0 is the initial memory manager required to use Fiasco. + +:'pkg/bootstrap': + + Bootstrap is the program that is started by the boot loader. + After being started, Bootstrap prepares the bare machine to + accommodate Fiasco. On many embedded architectures, bootstrap + is used to create a single binary image containing all boot-time + OS components. + +To build all components under '3rd/fiasco' after extracting the +archive, issue the following commands: + +! mkdir +! make -C 3rd/fiasco BUILD_DIR=/3rd build + +The specified '' must be the absolute path to the +directory, which will contain the build directories for the third party +software and the Genode build directory. After the build, your compound +build directory contains the following subdirectories: + +:'fiasco_x86': + + All generated files and the final binary of Fiasco reside here. + +:'l4env': + + L4 environment binaries and header files. + diff --git a/base-fiasco/etc/fiasco.conf b/base-fiasco/etc/fiasco.conf new file mode 100644 index 000000000..589873dca --- /dev/null +++ b/base-fiasco/etc/fiasco.conf @@ -0,0 +1,11 @@ +# +# Fiasco-specific default configuration options +# + +# +# Directory, where to search for L4 headers +# +# When using this file as template for a customized +# '/etc/fiasco.conf'. +# +#L4_DIR = $(HOME)/src/l4build.x86 diff --git a/base-fiasco/etc/specs.conf b/base-fiasco/etc/specs.conf new file mode 100644 index 000000000..91f1b0335 --- /dev/null +++ b/base-fiasco/etc/specs.conf @@ -0,0 +1,15 @@ +# +# Description of build platform +# + +# +# To build the Fiasco-specific Genode binaries, +# use one of the the following config options. +# +SPECS = genode fiasco_x86 + +# +# To build for the ARM integrator platform, +# use the following SPECS value. +# +#SPECS = genode platform_integrator fiasco_arm diff --git a/base-fiasco/etc/tools.conf b/base-fiasco/etc/tools.conf new file mode 100644 index 000000000..a1cada69b --- /dev/null +++ b/base-fiasco/etc/tools.conf @@ -0,0 +1,8 @@ +# +# The following options let you define your cross-compile tool chain +# + +include $(BASE_DIR)/etc/tools.conf + +#CROSS_DEV_PREFIX = arm-softfloat-linux-gnu- + diff --git a/base-fiasco/include/arm/cpu/atomic.h b/base-fiasco/include/arm/cpu/atomic.h new file mode 100644 index 000000000..60e6e0ea8 --- /dev/null +++ b/base-fiasco/include/arm/cpu/atomic.h @@ -0,0 +1,39 @@ +/* + * \brief Atomic operations for ARM + * \author Norman Feske + * \date 2007-04-28 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ARM__CPU__ATOMIC_H_ +#define _INCLUDE__ARM__CPU__ATOMIC_H_ + +namespace Genode { + + extern "C" long int + l4_atomic_cmpxchg(volatile long int*, long int, long int); + + /** + * Atomic compare and exchange + * + * This function compares the value at dest with cmp_val. + * If both values are equal, dest is set to new_val. If + * both values are different, the value at dest remains + * unchanged. + * + * \return 1 if the value was successfully changed to new_val, + * 0 if cmp_val and the value at dest differ. + */ + inline int cmpxchg(volatile int *dest, int cmp_val, int new_val) + { + return l4_atomic_cmpxchg((volatile long int *)dest, cmp_val, new_val); + } +} + +#endif /* _INCLUDE__ARM__CPU__ATOMIC_H_ */ diff --git a/base-fiasco/include/base/cancelable_lock.h b/base-fiasco/include/base/cancelable_lock.h new file mode 100644 index 000000000..7ca90e6fd --- /dev/null +++ b/base-fiasco/include/base/cancelable_lock.h @@ -0,0 +1,57 @@ +/* + * \brief Basic locking primitive + * \author Norman Feske + * \date 2006-07-26 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CANCELABLE_LOCK_H_ +#define _INCLUDE__BASE__CANCELABLE_LOCK_H_ + +#include +#include +#include + +namespace Genode { + + class Cancelable_lock + { + private: + + Native_lock _native_lock; + + public: + + enum State { LOCKED, UNLOCKED }; + + /** + * Constructor + */ + Cancelable_lock(State initial = UNLOCKED); + + /** + * Try to aquire lock an block while lock is not free + * + * This function may throw a Genode::Blocking_canceled exception. + */ + void lock(); + + /** + * Release lock + */ + void unlock(); + + /** + * Lock guard + */ + typedef Genode::Lock_guard Guard; + }; +} + +#endif /* _INCLUDE__BASE__CANCELABLE_LOCK_H_ */ diff --git a/base-fiasco/include/base/ipc_msgbuf.h b/base-fiasco/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..1cb3909b8 --- /dev/null +++ b/base-fiasco/include/base/ipc_msgbuf.h @@ -0,0 +1,65 @@ +/* + * \brief Fiasco-specific layout of IPC message buffer + * \author Norman Feske + * \date 2006-06-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +namespace Genode { + + /** + * IPC message buffer layout + */ + class Msgbuf_base + { + protected: + + Genode::size_t _size; + + public: + + /* + * Begin of message buffer layout + */ + + Fiasco::l4_fpage_t rcv_fpage; + Fiasco::l4_msgdope_t size_dope; + Fiasco::l4_msgdope_t send_dope; + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; }; + + /** + * Return address of message buffer + */ + inline void *addr() { return &rcv_fpage; }; + }; + + + /** + * Instance of IPC message buffer with specified buffer size + */ + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-fiasco/include/base/ipc_pager.h b/base-fiasco/include/base/ipc_pager.h new file mode 100644 index 000000000..aef9569ff --- /dev/null +++ b/base-fiasco/include/base/ipc_pager.h @@ -0,0 +1,173 @@ +/* + * \brief Fiasco pager support + * \author Christian Helmuth + * \date 2006-06-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +namespace Genode { + + class Mapping + { + private: + + addr_t _dst_addr; + Fiasco::l4_fpage_t _fpage; + + public: + + /** + * Constructor + */ + Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, unsigned l2size = L4_LOG2_PAGESIZE, + bool rw = true, bool grant = false) + : + _dst_addr(dst_addr), + _fpage(Fiasco::l4_fpage(src_addr, l2size, rw, grant)) + { + if (write_combined) + _fpage.fp.cache = Fiasco::L4_FPAGE_BUFFERABLE; + } + + /** + * Construct invalid flexpage + */ + Mapping() : _dst_addr(0), _fpage(Fiasco::l4_fpage(0, 0, 0, 0)) { } + + Fiasco::l4_umword_t dst_addr() const { return _dst_addr; } + Fiasco::l4_fpage_t fpage() const { return _fpage; } + + /** + * Prepare map operation + * + * On Fiasco, we need to map a page locally to be able to map it to + * another address space. + */ + void prepare_map_operation() + { + addr_t core_local_addr = _fpage.fp.page << 12; + size_t mapping_size = 1 << _fpage.fp.size; + + for (addr_t i = 0; i < mapping_size; i += L4_PAGESIZE) { + if (_fpage.fp.write) + touch_read_write((unsigned char volatile *)(core_local_addr + i)); + else + touch_read((unsigned char const volatile *)(core_local_addr + i)); + } + } + }; + + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + private: + + Native_thread_id _last; /* origin of last fault message */ + addr_t _pf_addr; /* page-fault address */ + addr_t _pf_ip; /* instruction pointer of faulter */ + Mapping _reply_mapping; /* page-fault answer */ + + public: + + /** + * Constructor + */ + Ipc_pager(); + + /** + * Wait for a new page fault received as short message IPC + */ + void wait_for_fault(); + + /** + * Reply current page-fault and wait for a new one + * + * Send short flex page and wait for next short-message (register) + * IPC -- pagefault + */ + void reply_and_wait_for_fault(); + + /** + * Request instruction pointer of current page fault + */ + addr_t fault_ip() { return _pf_ip; } + + /** + * Request fault address of current page fault + */ + addr_t fault_addr() { return _pf_addr & ~3; } + + /** + * Set parameters for next reply + */ + void set_reply_mapping(Mapping m) { _reply_mapping = m; } + + /** + * Set destination for next reply + */ + void set_reply_dst(Native_capability pager_object) { + _last.raw = pager_object.local_name(); } + + /** + * Answer call without sending a flex-page mapping + * + * This function is used to acknowledge local calls from one of + * core's region-manager sessions. + */ + void acknowledge_wakeup(); + + /** + * Return thread ID of last faulter + */ + Native_thread_id last() const { return _last; } + + /** + * Return badge for faulting thread + * + * As Fiasco has no server-defined badges for page-fault messages, we + * interpret the sender ID as badge. + */ + unsigned long badge() const { + return convert_native_thread_id_to_badge(_last); } + + bool is_write_fault() const { return (_pf_addr & 2); } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + /* + * Reflection of exceptions is not supported on this platform. + */ + return false; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-fiasco/include/base/native_types.h b/base-fiasco/include/base/native_types.h new file mode 100644 index 000000000..7e0485667 --- /dev/null +++ b/base-fiasco/include/base/native_types.h @@ -0,0 +1,115 @@ +/* + * \brief Native types on L4/Fiasco + * \author Norman Feske + * \date 2008-07-26 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +namespace Fiasco { +#include + + /** + * Return invalid L4 thread ID + */ + inline l4_threadid_t invalid_l4_threadid_t() { return L4_INVALID_ID; } +} + +namespace Genode { + + typedef volatile int Native_lock; + + class Platform_thread; + + typedef Fiasco::l4_threadid_t Native_thread_id; + + struct Native_thread + { + Native_thread_id l4id; + + /** + * Only used in core + * + * For 'Thread' objects created within core, 'pt' points to + * the physical thread object, which is going to be destroyed + * on destruction of the 'Thread'. + */ + Platform_thread *pt; + }; + + inline unsigned long convert_native_thread_id_to_badge(Native_thread_id tid) + { + /* + * Fiasco has no server-defined badges for page-fault messages. + * Therefore, we have to interpret the sender ID as badge. + */ + return tid.raw; + } + + /** + * Empty UTCB type expected by the thread library + * + * On this kernel, UTCBs are not placed within the the context area. Each + * thread can request its own UTCB pointer using the kernel interface. + */ + typedef struct { } Native_utcb; + + /* + * On Fiasco, the local_name member of a capability is global + * to the whole system. Therefore, capabilities are to be + * created at a central place that prevents id clashes. + */ + class Native_capability + { + protected: + + Fiasco::l4_threadid_t _tid; + long _local_name; + + public: + + /** + * Default constructor + */ + Native_capability() + : _tid(Fiasco::invalid_l4_threadid_t()), _local_name(0) { } + + long local_name() const { return _local_name; } + Fiasco::l4_threadid_t dst() const { return _tid; } + + bool valid() const { return l4_is_invalid_id(_tid) == 0; } + + + /***************************************************** + ** Functions to be used by the Fiasco backend only ** + *****************************************************/ + + /** + * Constructor + * + * This constructor can be called to create a Fiasco + * capability by hand. It must never be used from + * generic code! + */ + Native_capability(Fiasco::l4_threadid_t tid, + Fiasco::l4_umword_t local_name) + : _tid(tid), _local_name(local_name) { } + + /** + * Access raw capability data + */ + Fiasco::l4_threadid_t tid() const { return _tid; } + }; + + typedef Fiasco::l4_threadid_t Native_connection_state; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-fiasco/include/fiasco/thread_helper.h b/base-fiasco/include/fiasco/thread_helper.h new file mode 100644 index 000000000..411c44c02 --- /dev/null +++ b/base-fiasco/include/fiasco/thread_helper.h @@ -0,0 +1,40 @@ +/* + * \brief Fiasco-specific thread helper functions + * \author Norman Feske + * \date 2007-05-03 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__FIASCO__THREAD_HELPER_H_ +#define _INCLUDE__FIASCO__THREAD_HELPER_H_ + +#include + +namespace Fiasco { +#include + + inline void print_l4_threadid(l4_threadid_t t) + { + Genode::printf("THREAD %x.%02x\n", t.id.task, t.id.lthread); + Genode::printf(" unsigned version_low:10 = %x\n", t.id.version_low); + Genode::printf(" unsigned lthread:7 = %x\n", t.id.lthread); + Genode::printf(" unsigned task:11 = %x\n", t.id.task); + } + + + /** + * Sigma0 thread ID + * + * We must use a raw hex value initializer since we're using C++ and + * l4_threadid_t is an union. + */ + const l4_threadid_t sigma0_threadid = { 0x00040000 }; +} + +#endif /* _INCLUDE__FIASCO__THREAD_HELPER_H_ */ diff --git a/base-fiasco/lib/mk/arm/startup.mk b/base-fiasco/lib/mk/arm/startup.mk new file mode 100644 index 000000000..9dda379a2 --- /dev/null +++ b/base-fiasco/lib/mk/arm/startup.mk @@ -0,0 +1,8 @@ +REQUIRES = fiasco arm +LIBS = cxx lock +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(REP_DIR)/src/platform + +vpath crt0.s $(REP_DIR)/src/platform/arm +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-fiasco/lib/mk/core_printf.mk b/base-fiasco/lib/mk/core_printf.mk new file mode 100644 index 000000000..663cf64b9 --- /dev/null +++ b/base-fiasco/lib/mk/core_printf.mk @@ -0,0 +1,5 @@ +SRC_CC = core_printf.cc +LIBS = cxx console +INC_DIR += $(REP_DIR)/src/base/console + +vpath core_printf.cc $(BASE_DIR)/src/base/console diff --git a/base-fiasco/lib/mk/ipc.mk b/base-fiasco/lib/mk/ipc.mk new file mode 100644 index 000000000..6e6443bb9 --- /dev/null +++ b/base-fiasco/lib/mk/ipc.mk @@ -0,0 +1,3 @@ +SRC_CC = ipc.cc pager.cc + +vpath %.cc $(REP_DIR)/src/base/ipc diff --git a/base-fiasco/lib/mk/l4v2_support.mk b/base-fiasco/lib/mk/l4v2_support.mk new file mode 100644 index 000000000..8bda913f9 --- /dev/null +++ b/base-fiasco/lib/mk/l4v2_support.mk @@ -0,0 +1,19 @@ +# +# Build L4env base libraries, needed by sigma0 and bootstrap + + +# ignore stage one, visit the L4 build system at second build stage +ifeq ($(called_from_lib_mk),yes) + +# packages in 'l4/pkg/' +PKGS = l4sys/lib \ + uclibc/lib/uclibc \ + uclibc/lib/include \ + crtx \ + l4util/lib \ + cxx + +include $(REP_DIR)/mk/l4_pkg.mk +all: $(PKG_TAGS) + +endif diff --git a/base-fiasco/lib/mk/lock.mk b/base-fiasco/lib/mk/lock.mk new file mode 100644 index 000000000..75eddeef1 --- /dev/null +++ b/base-fiasco/lib/mk/lock.mk @@ -0,0 +1,3 @@ +SRC_CC = lock.cc + +vpath lock.cc $(REP_DIR)/src/base/lock diff --git a/base-fiasco/lib/mk/pager.mk b/base-fiasco/lib/mk/pager.mk new file mode 100644 index 000000000..c22e66d22 --- /dev/null +++ b/base-fiasco/lib/mk/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-fiasco/lib/mk/platform.inc b/base-fiasco/lib/mk/platform.inc new file mode 100644 index 000000000..cf5ab378e --- /dev/null +++ b/base-fiasco/lib/mk/platform.inc @@ -0,0 +1,56 @@ +# +# Create prerequisites for building Genode for Fiasco +# +# Prior building Genode programs for Fiasco, the kernel bindings must be +# generated. This is done by building a minimalistic subset of the original +# userland that comes with Fiasco. +# + +# +# Execute the rules in this file only at the second build stage when we know +# about the complete build settings, e.g., the 'CROSS_DEV_PREFIX'. +# +ifeq ($(called_from_lib_mk),yes) + +# +# Sanity checks +# +ifeq ($(L4_BUILD_DIR),$(BUILD_BASE_DIR)/l4) +all: $(L4_SRC_DIR) + +$(L4_SRC_DIR): + $(VERBOSE)$(ECHO) "--> Please, execute 'make prepare' in $(REP_DIR)" + $(VERBOSE)$(ECHO) "--> before compiling Genode apps for Fiasco." + $(VERBOSE)exit 1 +endif + +# +# Create L4 build directory +# +$(BUILD_BASE_DIR)/l4/.Makeconf.bid.old: + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)cp $(L4_CONFIG) $(@:.old=) + $(VERBOSE_MK) MAKEFLAGS= make $(VERBOSE_DIR) -C $(L4_SRC_DIR)/l4 \ + O=$(BUILD_BASE_DIR)/l4 SYSTEM_TARGET="$(CROSS_DEV_PREFIX)" oldconfig + +$(BUILD_BASE_DIR)/l4/pkg/uclibc/lib/uclibc: + $(VERBOSE)mkdir -p $(BUILD_BASE_DIR)/l4/pkg/uclibc/lib/uclibc + $(VERBOSE)tar cf - --exclude .svn -C $(L4_SRC_DIR)/../uclibc ARCH-all ARCH-x86 \ + | tar xf - -C $(BUILD_BASE_DIR)/l4/pkg/uclibc/lib/uclibc + +PKGS = input/include \ + drivers/uart/include \ + l4sys/include \ + l4util/include \ + libc_support/include \ + libsigma0/include + +include $(REP_DIR)/mk/l4_pkg.mk + +all: $(PKG_TAGS) + +$(PKG_TAGS): $(BUILD_BASE_DIR)/l4/.Makeconf.bid.old +$(PKG_TAGS): $(BUILD_BASE_DIR)/l4/pkg/uclibc/lib/uclibc + +endif + diff --git a/base-fiasco/lib/mk/x86/platform.mk b/base-fiasco/lib/mk/x86/platform.mk new file mode 100644 index 000000000..e53cdd8d1 --- /dev/null +++ b/base-fiasco/lib/mk/x86/platform.mk @@ -0,0 +1,6 @@ +# +# Configuration for L4 build system (for kernel-bindings, sigma0, bootstrap). +# +L4_CONFIG = $(REP_DIR)/config/l4env-config.x86 + +include $(REP_DIR)/lib/mk/platform.inc diff --git a/base-fiasco/lib/mk/x86/startup.mk b/base-fiasco/lib/mk/x86/startup.mk new file mode 100644 index 000000000..a3b2d14bd --- /dev/null +++ b/base-fiasco/lib/mk/x86/startup.mk @@ -0,0 +1,8 @@ +REQUIRES = fiasco x86 +LIBS = cxx lock +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(BASE_DIR)/src/platform $(REP_DIR)/src/platform + +vpath crt0.s $(dir $(call select_from_repositories,src/platform/x86_32/crt0.s)) +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-fiasco/mk/l4_pkg.mk b/base-fiasco/mk/l4_pkg.mk new file mode 100644 index 000000000..d01a25794 --- /dev/null +++ b/base-fiasco/mk/l4_pkg.mk @@ -0,0 +1,69 @@ +# +# Utility for building L4 contrib packages +# +# Variables that steer the behaviour of this makefile: +# +# TARGET - name of target +# PKGS - list of L4 packages to visit in order to create +# the target +# + +LIBS += platform + +ifeq ($(filter-out $(SPECS),x86_32),) + L4_BUILD_ARCH := x86 +endif + +ifeq ($(filter-out $(SPECS),arm),) + L4_BUILD_ARCH := arm +endif + +ifeq ($(L4_BUILD_ARCH),) +all: l4_build_arch_undefined + $(VERBOSE)$(ECHO) "Error: L4_BUILD_ARCH undefined, architecture not supported" + $(VERBOSE)false +endif + +L4_PKG_DIR = $(L4_SRC_DIR)/l4/pkg +STARTUP_LIB = +PKG_TAGS = $(addsuffix .tag,$(PKGS)) + +ifeq ($(VERBOSE),) +L4_VERBOSE = V=1 +endif + +$(TARGET): $(PKG_TAGS) + +# +# We preserve the order of processing 'l4/pkg/' directories because of +# inter-package dependencies. However, within each directory, make is working +# in parallel. +# +.NOTPARALLEL: $(PKG_TAGS) + +# +# The '_GNU_SOURCE' definition is needed to convince uClibc to define the +# 'off64_t' type, which is used by bootstrap. +# +%.tag: + $(VERBOSE_MK) MAKEFLAGS= CPPFLAGS="$(CC_MARCH)" \ + CFLAGS="$(CC_MARCH)" CXXFLAGS="$(CC_MARCH) -D_GNU_SOURCE" \ + ASFLAGS="$(CC_MARCH)" LDFLAGS="$(LD_MARCH)" \ + $(MAKE) $(VERBOSE_DIR) O=$(L4_BUILD_DIR) $(L4_VERBOSE) \ + -C $(L4_PKG_DIR)/$* \ + CC="$(CROSS_DEV_PREFIX)gcc" \ + CXX="$(CROSS_DEV_PREFIX)g++" \ + LD="$(CROSS_DEV_PREFIX)ld" + $(VERBOSE)mkdir -p $(dir $@) && touch $@ + +clean cleanall: clean_tags + +# if (pseudo) target is named after a directory, remove the whole subtree +clean_prg_objects: clean_dir_named_as_target + +clean_dir_named_as_target: + $(VERBOSE)(test -d $(TARGET) && rm -rf $(TARGET)) || true + +clean_tags: + $(VERBOSE)rm -f $(PKG_TAGS) + diff --git a/base-fiasco/mk/spec-fiasco.mk b/base-fiasco/mk/spec-fiasco.mk new file mode 100644 index 000000000..3b9253af3 --- /dev/null +++ b/base-fiasco/mk/spec-fiasco.mk @@ -0,0 +1,32 @@ +# +# Specifics for the l4v2 kernel API +# + +# +# Read default and builddir-specific config files +# +# In these config files, we find the definition of L4_DIR +# +-include $(call select_from_repositories,etc/fiasco.conf) +-include $(BUILD_BASE_DIR)/etc/fiasco.conf + +L4_BUILD_DIR ?= $(BUILD_BASE_DIR)/l4 +L4_SRC_DIR ?= $(REP_DIR)/contrib/fiasco/snapshot + + +# +# L4/sys headers +# +L4_INC_DIR += $(L4_BUILD_DIR)/include \ + $(L4_BUILD_DIR)/include/l4v2 + +# +# Startup code to be used when building a program +# +STARTUP_LIB ?= startup +PRG_LIBS += $(STARTUP_LIB) + +clean_contrib: + $(VERBOSE)rm -rf $(BUILD_BASE_DIR)/l4 + +cleanall: clean_contrib diff --git a/base-fiasco/mk/spec-fiasco_arm.mk b/base-fiasco/mk/spec-fiasco_arm.mk new file mode 100644 index 000000000..8c34daad2 --- /dev/null +++ b/base-fiasco/mk/spec-fiasco_arm.mk @@ -0,0 +1,50 @@ +# +# Specifics for Fiasco on ARM +# +# The following variables must be defined by a platform spec file: +# +# L4SYS_ARM_CPU - Platform identifiert used for constructing l4sys path +# names corresponding to the ARM platform. For example, +# specify 'arm_int' for the ARM integrator board. +# RAM_BASE - Start address of physical memory. If not specified, +# the start adress 0x0 is used. +# + +SPECS += arm fiasco 32bit + +# +# ARM-specific L4/sys headers +# +L4_INC_DIR += $(L4_BUILD_DIR)/include/arm/l4v2 \ + $(L4_BUILD_DIR)/include/arm + +# +# Support for Fiasco's ARM-specific extensions of L4 +# and ARM-specific utility functions. +# +REP_INC_DIR += include/arm + +# +# Defines for L4/sys headers +# +CC_OPT += -DSYSTEM_$(L4SYS_ARM_CPU)_l4v2 +CC_OPT += -DCONFIG_L4_CALL_SYSCALLS -DL4API_l4v2 -DARCH_arm +CC_OPT += -msoft-float -fomit-frame-pointer +AS_OPT += -mfpu=softfpa + +# +# Linker options that are specific for L4 on ARM +# +RAM_BASE ?= 0x0 +LD_TEXT_ADDR ?= $(shell printf "0x%x" $$(($(RAM_BASE) + 0x00078000))) +CXX_LINK_OPT += -Wl,-Ttext=$(LINK_TEXT_ADDR) +CXX_LINK_OPT += -L$(L4_BUILD_DIR)/lib/$(L4SYS_ARM_CPU)/l4v2 +EXT_OBJECTS += -ll4sys + +# +# Also include less-specific configuration last +# +include $(call select_from_repositories,mk/spec-32bit.mk) +include $(call select_from_repositories,mk/spec-fiasco.mk) + +INC_DIR += $(L4_INC_DIR) diff --git a/base-fiasco/mk/spec-fiasco_x86.mk b/base-fiasco/mk/spec-fiasco_x86.mk new file mode 100644 index 000000000..41b1d98f3 --- /dev/null +++ b/base-fiasco/mk/spec-fiasco_x86.mk @@ -0,0 +1,25 @@ +# +# Specifics for Fiasco L4v2 on x86 +# + +SPECS += x86_32 fiasco +SPECS += pci ps2 vesa + +# +# x86-specific L4v2/sys headers +# +L4_INC_DIR += $(L4_BUILD_DIR)/include/x86/l4v2 \ + $(L4_BUILD_DIR)/include/x86 + +# +# Linker options that are specific for x86 +# +LD_TEXT_ADDR ?= 0x01000000 + +# +# Also include less-specific configuration last +# +include $(call select_from_repositories,mk/spec-x86_32.mk) +include $(call select_from_repositories,mk/spec-fiasco.mk) + +INC_DIR += $(L4_INC_DIR) diff --git a/base-fiasco/mk/spec-platform_imx.mk b/base-fiasco/mk/spec-platform_imx.mk new file mode 100644 index 000000000..61e45889d --- /dev/null +++ b/base-fiasco/mk/spec-platform_imx.mk @@ -0,0 +1,16 @@ +# +# Specifics for Freescale i.MX21 platform +# + +RAM_BASE = 0xc0000000 + +# +# Configure target CPU for gcc +# +CC_OPT += -march=armv5 + +# +# Defines for L4/sys headers +# +CC_OPT += -DCPUTYPE_imx +L4SYS_ARM_CPU = arm_imx diff --git a/base-fiasco/mk/spec-platform_integrator.mk b/base-fiasco/mk/spec-platform_integrator.mk new file mode 100644 index 000000000..a6bb3dba0 --- /dev/null +++ b/base-fiasco/mk/spec-platform_integrator.mk @@ -0,0 +1,14 @@ +# +# Specifics for ARM integrator platform +# + +# +# Configure target CPU for gcc +# +CC_OPT += -march=armv5 + +# +# Defines for L4/sys headers +# +CC_OPT += -DCPUTYPE_int +L4SYS_ARM_CPU = arm_int diff --git a/base-fiasco/mk/spec-platform_mmsp2.mk b/base-fiasco/mk/spec-platform_mmsp2.mk new file mode 100644 index 000000000..0cba20187 --- /dev/null +++ b/base-fiasco/mk/spec-platform_mmsp2.mk @@ -0,0 +1,14 @@ +# +# Specifics for MagicEyes Digital’s Multimedia Signal Processor +# + +# +# Configure target CPU for gcc +# +CC_OPT += -march=armv4t + +# +# Defines for L4/sys headers +# +CC_OPT += -DCPUTYPE_mmsp2 +L4SYS_ARM_CPU = arm_mmsp2 diff --git a/base-fiasco/run/env b/base-fiasco/run/env new file mode 100644 index 000000000..d5fe1ad29 --- /dev/null +++ b/base-fiasco/run/env @@ -0,0 +1,127 @@ +# +# \brief Fiasco-specific test-environment supplements +# \author Norman Feske +# \author Christian Helmuth +# \date 2010-08-26 +# +# This file is meant to be used as '--include' argument for 'tool/run'. +# + + +## +# Read the location of the Fiasco user directory from 'etc/fiasco.conf' +# +proc l4_dir { } { + global _l4_dir + + if {![info exists _l4_dir]} { + if {[file exists etc/fiasco.conf]} { + set _l4_dir [exec sed -n "/^L4_BUILD_DIR/s/^.*=\\s*//p" etc/fiasco.conf] + if {[file exists $_l4_dir]} { return $_l4_dir } + } + + set _l4_dir "[pwd]/l4" + if {![file exists $_l4_dir]} { + puts -nonewline stderr "Error: Could neither find the L4 build directory " + puts -nonewline stderr "within '/l4' nor at a location " + puts -nonewline stderr "specified via 'L4_BUILD_DIR = ' " + puts stderr "in /etc/fiasco.conf'." + exit 1 + } + } + return $_l4_dir +} + + +## +# Return whether the l4-buid-directory is provided from the outside +# +proc l4_dir_external { } { + if {[l4_dir] == "[pwd]/l4"} { return 0 } + return 1 +} + + +## +# Return the location of the Fiasco kernel +# +proc fiasco { } { + return [kernel_location_from_config_file etc/fiasco.conf [pwd]/kernel/fiasco/fiasco] +} + + +## +# Return whether fiasco kernel is provided from the outside +# +proc fiasco_external { } { + if {[fiasco] == "[pwd]/kernel/fiasco/fiasco"} { return 0 } + return 1 +} + + +################################## +## Test framework API functions ## +################################## + +proc create_boot_directory { } { + exec rm -rf [run_dir] + exec mkdir -p [run_dir]/genode + exec mkdir -p [run_dir]/fiasco +} + +proc bin_dir { } { + if {[have_spec x86_32]} { return "[l4_dir]/bin/x86_586" } + + puts stderr "Error: Cannot determine bin directory" + exit 1 +} + +proc build_boot_image {binaries} { + + # + # Collect contents of the ISO image + # + copy_and_strip_genode_binaries_to_run_dir $binaries + + if {![fiasco_external]} { build { kernel } } + if {![l4_dir_external]} { build { bootstrap sigma0 } } + + # assert existence of the L4 build directory + l4_dir + + puts "using fiasco kernel [fiasco]" + exec cp [fiasco] [run_dir]/fiasco/fiasco + puts "using sigma0/bootstrap at [l4_dir]" + exec cp [bin_dir]/l4v2/sigma0 [run_dir]/fiasco + exec cp [bin_dir]/bootstrap [run_dir]/fiasco + + install_iso_bootloader_to_run_dir + + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on L4/Fiasco" + puts $fh " kernel /fiasco/bootstrap -serial -modaddr=0x02000000" + puts $fh " module /fiasco/fiasco -serial -serial_esc -jdb_cmd=JH" + puts $fh " module /fiasco/sigma0" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + puts $fh " vbeset 0x117 506070" + close $fh + + create_iso_image_from_run_dir +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + spawn_qemu $wait_for_re $timeout_value } + diff --git a/base-fiasco/src/base/console/core_console.h b/base-fiasco/src/base/console/core_console.h new file mode 100644 index 000000000..e5e50de5a --- /dev/null +++ b/base-fiasco/src/base/console/core_console.h @@ -0,0 +1,30 @@ +/* + * \brief Console backend using the Fiasco kernel debugger + * \author Norman Feske + * \date 2006-04-08 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +/* Genode includes */ +#include + +namespace Genode { + + class Core_console : public Console + { + protected: + + void _out_char(char c) { Fiasco::outchar(c); } + }; +} diff --git a/base-fiasco/src/base/ipc/ipc.cc b/base-fiasco/src/base/ipc/ipc.cc new file mode 100644 index 000000000..e23fcb443 --- /dev/null +++ b/base-fiasco/src/base/ipc/ipc.cc @@ -0,0 +1,259 @@ +/* + * \brief IPC implementation for Fiasco + * \author Norman Feske + * \date 2006-06-13 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +namespace Fiasco { +#include +#include +#include +} + +using namespace Genode; + + +/***************** + ** Ipc_ostream ** + *****************/ + +void Ipc_ostream::_send() +{ + using namespace Fiasco; + + _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + sizeof(umword_t) - 1)>>2, 0); + + l4_msgdope_t result; + l4_ipc_send(_dst.tid(), _snd_msg->addr(), _dst.local_name(), + *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), + L4_IPC_NEVER, &result); + + if (L4_IPC_IS_ERROR(result)) { + PERR("Ipc error %lx", L4_IPC_ERROR(result)); + throw Genode::Ipc_error(); + } + + _write_offset = sizeof(umword_t); +} + + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) : + Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()), + _snd_msg(snd_msg), _dst(dst) +{ + _write_offset = sizeof(umword_t); +} + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() +{ + using namespace Fiasco; + + l4_msgdope_t result; + + /* + * Wait until we get a proper message and thereby + * ignore receive message cuts on the server-side. + * This error condition should be handled by the + * client. The server does not bother. + */ + do { + _rcv_msg->size_dope = L4_IPC_DOPE(_rcv_msg->size()>>2, 0); + + l4_ipc_wait(&_rcv_cs, _rcv_msg->addr(), + reinterpret_cast(&_rcv_msg->buf[0]), + reinterpret_cast(&_rcv_msg->buf[sizeof(umword_t)]), + L4_IPC_NEVER, &result); + + if (L4_IPC_IS_ERROR(result)) + PERR("Ipc error %lx", L4_IPC_ERROR(result)); + + } while (L4_IPC_IS_ERROR(result)); + + /* reset buffer read offset */ + _read_offset = sizeof(umword_t); +} + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg): + Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), + Native_capability(Fiasco::l4_myself(), 0), + _rcv_msg(rcv_msg) +{ + using namespace Fiasco; + + _rcv_cs = L4_INVALID_ID; + + _read_offset = sizeof(umword_t); +} + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() +{ + using namespace Fiasco; + + l4_msgdope_t ipc_result; + long rec_badge; + + _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + 2*sizeof(umword_t) - 1)>>2, 0); + _rcv_msg->size_dope = L4_IPC_DOPE(_rcv_msg->size()>>2, 0); + + l4_ipc_call(_dst.tid(), + _write_offset <= 2*sizeof(umword_t) ? L4_IPC_SHORT_MSG : _snd_msg->addr(), + _dst.local_name(), + *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), + _rcv_msg->addr(), + reinterpret_cast(&rec_badge), + reinterpret_cast(&_rcv_msg->buf[sizeof(umword_t)]), + L4_IPC_NEVER, &ipc_result); + + if (L4_IPC_IS_ERROR(ipc_result)) { + + if (L4_IPC_ERROR(ipc_result) == L4_IPC_RECANCELED) + throw Genode::Blocking_canceled(); + + PERR("Ipc error %lx", L4_IPC_ERROR(ipc_result)); + throw Genode::Ipc_error(); + } + + /* + * Reset buffer read and write offsets. We shadow the first mword of the + * send message buffer (filled via '_write_offset') with the local name of + * the invoked remote object. We shadow the first mword of the receive + * buffer (retrieved via '_read_offset') with the local name of the reply + * capability ('rec_badge'), which is bogus in the L4/Fiasco case. In both + * cases, we skip the shadowed message mword when reading/writing the + * message payload. + */ + _write_offset = _read_offset = sizeof(umword_t); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg): + Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) +{ } + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_prepare_next_reply_wait() +{ + /* now we have a request to reply */ + _reply_needed = true; + + /* leave space for return value at the beginning of the msgbuf */ + _write_offset = 2*sizeof(umword_t); + + /* receive buffer offset */ + _read_offset = sizeof(umword_t); +} + + +void Ipc_server::_wait() +{ + /* wait for new server request */ + try { Ipc_istream::_wait(); } catch (Blocking_canceled) { } + + /* define destination of next reply */ + _dst = Native_capability(_rcv_cs, badge()); + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() +{ + using namespace Fiasco; + + _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + sizeof(umword_t) - 1)>>2, 0); + + l4_msgdope_t result; + l4_ipc_send(_dst.tid(), _snd_msg->addr(), _dst.local_name(), + *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), + L4_IPC_SEND_TIMEOUT_0, &result); + + if (L4_IPC_IS_ERROR(result)) + PERR("Ipc error %lx, ignored", L4_IPC_ERROR(result)); + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply_wait() +{ + using namespace Fiasco; + + if (_reply_needed) { + + l4_msgdope_t ipc_result; + + _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + sizeof(umword_t) - 1)>>2, 0); + _rcv_msg->size_dope = L4_IPC_DOPE(_rcv_msg->size()>>2, 0); + + /* + * Use short IPC for reply if possible. + * This is the common case of returning + * an integer as RPC result. + */ + l4_ipc_reply_and_wait( + _dst.tid(), + _write_offset <= 2*sizeof(umword_t) ? L4_IPC_SHORT_MSG : _snd_msg->addr(), + _dst.local_name(), + *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), + &_rcv_cs, _rcv_msg->addr(), + reinterpret_cast(&_rcv_msg->buf[0]), + reinterpret_cast(&_rcv_msg->buf[sizeof(umword_t)]), + L4_IPC_SEND_TIMEOUT_0, &ipc_result); + + if (L4_IPC_IS_ERROR(ipc_result)) { + PERR("Ipc error %lx", L4_IPC_ERROR(ipc_result)); + + /* + * The error conditions could be a message cut (which + * we want to ignore on the server side) or a reply failure + * (for example, if the caller went dead during the call. + * In both cases, we do not reflect the error condition to + * the user but want to wait for the next proper incoming + * message. So let's just wait now. + */ + _wait(); + } + + } else _wait(); + + /* define destination of next reply */ + _dst = Native_capability(_rcv_cs, badge()); + + _prepare_next_reply_wait(); +} + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg): + Ipc_istream(rcv_msg), + Ipc_ostream(Native_capability(), snd_msg), _reply_needed(false) +{ } diff --git a/base-fiasco/src/base/ipc/pager.cc b/base-fiasco/src/base/ipc/pager.cc new file mode 100644 index 000000000..4216d85a7 --- /dev/null +++ b/base-fiasco/src/base/ipc/pager.cc @@ -0,0 +1,69 @@ +/* + * \brief Pager support for Fiasco + * \author Christian Helmuth + * \date 2006-06-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +namespace Fiasco { +#include +#include +} + +using namespace Genode; +using namespace Fiasco; + + +void Ipc_pager::wait_for_fault() +{ + l4_msgdope_t result; + + do { + l4_ipc_wait(&_last, + L4_IPC_SHORT_MSG, &_pf_addr, &_pf_ip, + L4_IPC_NEVER, &result); + + if (L4_IPC_IS_ERROR(result)) + PERR("Ipc error %lx", L4_IPC_ERROR(result)); + + } while (L4_IPC_IS_ERROR(result)); +} + + +void Ipc_pager::reply_and_wait_for_fault() +{ + l4_msgdope_t result; + + l4_ipc_reply_and_wait(_last, + L4_IPC_SHORT_FPAGE, _reply_mapping.dst_addr(), + _reply_mapping.fpage().fpage, &_last, + L4_IPC_SHORT_MSG, &_pf_addr, &_pf_ip, + L4_IPC_SEND_TIMEOUT_0, &result); + + if (L4_IPC_IS_ERROR(result)) { + PERR("Ipc error %lx", L4_IPC_ERROR(result)); + + /* ignore all errors and wait for next proper message */ + wait_for_fault(); + } +} + + +void Ipc_pager::acknowledge_wakeup() +{ + /* answer wakeup call from one of core's region-manager sessions */ + l4_msgdope_t result; + l4_ipc_send(_last, L4_IPC_SHORT_MSG, 0, 0, L4_IPC_SEND_TIMEOUT_0, &result); +} + + +Ipc_pager::Ipc_pager() : Native_capability(Fiasco::l4_myself(), 0) { } diff --git a/base-fiasco/src/base/lock/lock.cc b/base-fiasco/src/base/lock/lock.cc new file mode 100644 index 000000000..773d7d855 --- /dev/null +++ b/base-fiasco/src/base/lock/lock.cc @@ -0,0 +1,50 @@ +/* + * \brief Lock implementation + * \author Norman Feske + * \date 2007-10-15 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* L4/Fiasco includes */ +namespace Fiasco { +#include +} + +using namespace Genode; + + +Cancelable_lock::Cancelable_lock(Cancelable_lock::State initial) +: _native_lock(UNLOCKED) +{ + if (initial == LOCKED) + lock(); +} + + +void Cancelable_lock::lock() +{ + /* + * XXX: How to notice cancel-blocking signals issued when being outside the + * 'l4_ipc_sleep' system call? + */ + while (!Genode::cmpxchg(&_native_lock, UNLOCKED, LOCKED)) + if (Fiasco::l4_ipc_sleep(Fiasco::l4_ipc_timeout(0, 0, 500, 0)) != L4_IPC_RETIMEOUT) + throw Genode::Blocking_canceled(); +} + + +void Cancelable_lock::unlock() +{ + _native_lock = UNLOCKED; +} diff --git a/base-fiasco/src/base/pager/pager.cc b/base-fiasco/src/base/pager/pager.cc new file mode 100644 index 000000000..91fd42bd7 --- /dev/null +++ b/base-fiasco/src/base/pager/pager.cc @@ -0,0 +1,117 @@ +/* + * \brief Fiasco pager framework + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-14 + * + * FIXME Isn't this file generic? + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +namespace Fiasco { +#include +#include +} + +using namespace Genode; + + +/********************** + ** Pager activation ** + **********************/ + +void Pager_activation_base::entry() +{ + Ipc_pager pager; + _cap = pager; + _cap_valid.unlock(); + + pager.wait_for_fault(); + while (1) { + + /* lookup referenced object */ + Pager_object *obj = _ep ? _ep->obj_by_id(pager.badge()) : 0; + + /* handle request */ + if (obj) { + if (obj->pager(pager)) + /* something strange occured - leave thread in pagefault */ + pager.wait_for_fault(); + else + pager.reply_and_wait_for_fault(); + } else { + + /* prevent threads outside of core to mess with our wake-up interface */ + enum { CORE_TASK_ID = 4 }; + if (pager.last().id.task != CORE_TASK_ID) { + + PWRN("page fault from unknown partner %x.%02x", + (int)pager.last().id.task, (int)pager.last().id.lthread); + + } else { + + /* + * We got a request from one of cores region-manager sessions + * to answer the pending page fault of a resolved region-manager + * client. Hence, we have to send the page-fault reply to the + * specified thread and answer the call of the region-manager + * session. + * + * When called from a region-manager session, we receive the + * core-local address of the targeted pager object via the + * first message word, which corresponds to the 'fault_ip' + * argument of normal page-fault messages. + */ + obj = reinterpret_cast(pager.fault_ip()); + + /* send reply to the calling region-manager session */ + pager.acknowledge_wakeup(); + + /* answer page fault of resolved pager object */ + pager.set_reply_dst(obj->cap()); + pager.acknowledge_wakeup(); + } + pager.wait_for_fault(); + } + }; +} + + +/********************** + ** Pager entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *, Pager_activation_base *a) +: _activation(a) +{ _activation->ep(this); } + + +void Pager_entrypoint::dissolve(Pager_object *obj) +{ + remove(obj); +} + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + /* return invalid capability if no activation is present */ + if (!_activation) return Pager_capability(); + + Native_thread_id tid = _activation->cap().tid(); + Native_capability cap(_activation->cap().tid(), obj->badge()); + + /* add server object to object pool */ + obj->cap(cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return reinterpret_cap_cast(cap); +} diff --git a/base-fiasco/src/bootstrap/target.mk b/base-fiasco/src/bootstrap/target.mk new file mode 100644 index 000000000..e2a41b44f --- /dev/null +++ b/base-fiasco/src/bootstrap/target.mk @@ -0,0 +1,5 @@ +TARGET = bootstrap +PKGS = bootstrap +LIBS = l4v2_support + +include $(REP_DIR)/mk/l4_pkg.mk diff --git a/base-fiasco/src/core/arm/platform_arm.cc b/base-fiasco/src/core/arm/platform_arm.cc new file mode 100644 index 000000000..81c895a68 --- /dev/null +++ b/base-fiasco/src/core/arm/platform_arm.cc @@ -0,0 +1,24 @@ +/* + * \brief Platform support specific to ARM + * \author Norman Feske + * \date 2007-10-13 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "platform.h" + +using namespace Genode; + +void Platform::_setup_io_port_alloc() +{ + /* + * This is just a dummy init function for the I/O port allocator. + * ARM does not I/O port support. + */ +} diff --git a/base-fiasco/src/core/arm/target.mk b/base-fiasco/src/core/arm/target.mk new file mode 100644 index 000000000..1be0ccfdb --- /dev/null +++ b/base-fiasco/src/core/arm/target.mk @@ -0,0 +1,7 @@ +include $(PRG_DIR)/../target.inc + +REQUIRES += arm +SRC_CC += platform_arm.cc + +vpath io_port_session_component.cc $(GEN_CORE_DIR)/arm + diff --git a/base-fiasco/src/core/include/map_local.h b/base-fiasco/src/core/include/map_local.h new file mode 100644 index 000000000..f4fd7c148 --- /dev/null +++ b/base-fiasco/src/core/include/map_local.h @@ -0,0 +1,76 @@ +/* + * \brief Core-local mapping + * \author Norman Feske + * \date 2010-02-15 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__MAP_LOCAL_H_ +#define _CORE__INCLUDE__MAP_LOCAL_H_ + +/* core includes */ +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +} + +namespace Genode { + + /** + * Map page locally within core + * + * On Fiasco, all mapping originate from virtual addresses. At startup, + * core obtains the whole memory sigma0 in a one-to-one fashion. Hence, + * core-local addresses normally correspond to physical addresses. + * + * \param from_addr core-virtual source address + * \param to_addr core-virtual destination address + * \param num_pages number of pages to remap + */ + inline bool map_local(addr_t from_addr, addr_t to_addr, size_t num_pages) + { + Native_thread_id core_pager = platform_specific()->core_pager()->native_thread_id(); + + addr_t offset = 0; + size_t page_size = get_page_size(); + size_t page_size_log2 = get_page_size_log2(); + for (unsigned i = 0; i < num_pages; i++, offset += page_size) { + + using namespace Fiasco; + + /* perform echo request to the core pager */ + l4_umword_t dummy = 0; + l4_msgdope_t ipc_result; + l4_fpage_t from_fpage = l4_fpage(from_addr + offset, + page_size_log2, true, false); + enum { ECHO_LOCAL_MAP_REQUEST = 0 }; + l4_ipc_call(core_pager, L4_IPC_SHORT_MSG, + from_fpage.raw, /* normally page-fault addr */ + ECHO_LOCAL_MAP_REQUEST, /* normally page-fault IP */ + L4_IPC_MAPMSG(to_addr + offset, page_size_log2), + &dummy, &dummy, + L4_IPC_NEVER, &ipc_result); + + if (L4_IPC_IS_ERROR(ipc_result)) { + PWRN("could not locally remap 0x%lx to 0x%lx, error code is %ld", + from_addr, to_addr, L4_IPC_ERROR(ipc_result)); + return false; + } + } + return true; + } +} + +#endif /* _CORE__INCLUDE__MAP_LOCAL_H_ */ + diff --git a/base-fiasco/src/core/include/platform.h b/base-fiasco/src/core/include/platform.h new file mode 100644 index 000000000..7052fb68a --- /dev/null +++ b/base-fiasco/src/core/include/platform.h @@ -0,0 +1,157 @@ +/* + * \brief Fiasco platform + * \author Christian Helmuth + * \author Norman Feske + * \date 2007-09-10 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_H_ +#define _CORE__INCLUDE__PLATFORM_H_ + +#include +#include + +#include "platform_generic.h" +#include "platform_thread.h" +#include "platform_pd.h" +#include "multiboot.h" + + +namespace Genode { + + class Platform : public Platform_generic + { + private: + + /* + * Shortcut for the type of allocator instances for physical resources + */ + typedef Synchronized_range_allocator Phys_allocator; + + Platform_pd *_core_pd; /* core protection domain object */ + Phys_allocator _ram_alloc; /* RAM allocator */ + Phys_allocator _io_mem_alloc; /* MMIO allocator */ + Phys_allocator _io_port_alloc; /* I/O port allocator */ + Phys_allocator _irq_alloc; /* IRQ allocator */ + Phys_allocator _region_alloc; /* virtual memory allocator for core */ + Multiboot_info _mb_info; /* multiboot information */ + Rom_fs _rom_fs; /* ROM file system */ + Rom_module _kip_rom; /* ROM module for Fiasco KIP */ + + addr_t _vm_start; /* begin of virtual memory */ + size_t _vm_size; /* size of virtual memory */ + + /* + * We do not export any boot module loaded before FIRST_ROM. + */ + enum { FIRST_ROM = 3 }; + + /** + * Setup base resources + * + * - Map and provide KIP as ROM module + * - Initializes region allocator + * - Initializes multiboot info structure + */ + void _setup_basics(); + + /** + * Setup RAM, IO_MEM, and region allocators + */ + void _setup_mem_alloc(); + + /** + * Setup I/O port space allocator + */ + void _setup_io_port_alloc(); + + /** + * Setup IRQ allocator + */ + void _setup_irq_alloc(); + + /** + * Parse multi-boot information and update ROM database + */ + void _setup_rom(); + + /** + * Setup pager for core-internal threads + */ + void _setup_core_pager(); + + public: + + /** + * Pager object representing the pager of core namely sigma0 + */ + struct Sigma0 : public Pager_object + { + /** + * Constructor + */ + Sigma0(); + + int pager(Ipc_pager &ps) { /* never called */ return -1; } + }; + + /** + * Return singleton instance of Sigma0 pager object + */ + static Sigma0 *sigma0(); + + /** + * Core pager thread that handles core-internal page-faults + */ + struct Core_pager : public Platform_thread, public Pager_object + { + /** + * Constructor + */ + Core_pager(Platform_pd *core_pd); + + int pager(Ipc_pager &ps) { /* never called */ return -1; } + }; + + /** + * Return singleton instance of core pager object + */ + Core_pager *core_pager(); + + /** + * Constructor + */ + Platform(); + + /** + * Accessor for core pd object + */ + Platform_pd *core_pd() { return _core_pd; } + + + /******************************** + ** Generic platform interface ** + ********************************/ + + Allocator *core_mem_alloc() { return &_ram_alloc; } + Range_allocator *ram_alloc() { return &_ram_alloc; } + Range_allocator *io_mem_alloc() { return &_io_mem_alloc; } + Range_allocator *io_port_alloc() { return &_io_port_alloc; } + Range_allocator *irq_alloc() { return &_irq_alloc; } + Range_allocator *region_alloc() { return &_region_alloc; } + addr_t vm_start() const { return _vm_start; } + size_t vm_size() const { return _vm_size; } + Rom_fs *rom_fs() { return &_rom_fs; } + + void wait_for_exit(); + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-fiasco/src/core/include/platform_pd.h b/base-fiasco/src/core/include/platform_pd.h new file mode 100644 index 000000000..55c486041 --- /dev/null +++ b/base-fiasco/src/core/include/platform_pd.h @@ -0,0 +1,182 @@ +/* + * \brief L4/Fiasco protection domain facility + * \author Christian Helmuth + * \date 2006-04-11 + * + * Protection domains are L4 tasks under Fiasco and serve as base + * container for the platform. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_PD_H_ +#define _CORE__INCLUDE__PLATFORM_PD_H_ + +#include + +namespace Fiasco { +#include +} + +namespace Genode { + + class Platform_thread; + class Platform_pd + { + private: + + enum { + VERSION_BITS = 10, + PD_FIRST = 0x10, + PD_MAX = (1 << 11) - 1, /* leave 0x7ff free for L4_INVALID_ID */ + PD_VERSION_MAX = (1 << 10) - 1, + PD_INVALID = -1, + THREAD_MAX = (1 << 7), + }; + + unsigned _pd_id; /* plain pd number */ + unsigned _version; /* version number */ + + Fiasco::l4_taskid_t _l4_task_id; /* L4 task ID */ + + + /********************************************** + ** Threads of this protection domain object ** + **********************************************/ + + Platform_thread *_threads[THREAD_MAX]; + + /** + * Initialize thread allocator + */ + void _init_threads(); + + /** + * Thread iteration for one task + */ + Platform_thread *_next_thread(); + + /** + * Thread allocation + * + * Again a special case for Core thread0. + */ + int _alloc_thread(int thread_id, Platform_thread *thread); + + /** + * Thread deallocation + * + * No special case for Core thread0 here - we just never call it. + */ + void _free_thread(int thread_id); + + + /****************** + ** PD allocator ** + ******************/ + + struct Pd_alloc + { + unsigned reserved : 1; + unsigned free : 1; + unsigned version : VERSION_BITS; + + Pd_alloc(bool r, bool f, unsigned v) + : reserved(r), free(f), version(v) { } + + Pd_alloc() : reserved(0), free(0), version(0) { } + }; + + static Pd_alloc *_pds() + { + static Pd_alloc static_pds[PD_MAX]; + return static_pds; + } + + /** + * Protection-domain creation + * + * The syscall parameter propagates if any L4 kernel function + * should be used. We need the special case for the Core startup. + */ + void _create_pd(bool syscall); + + /** + * Protection domain destruction + * + * No special case for Core here - we just never call it. + */ + void _destroy_pd(); + + /** + * Protection domain allocation + * + * Find free L4 task and use it. We need the special case for Core + * startup. + */ + int _alloc_pd(signed pd_id); + + /** + * Protection domain deallocation + * + * No special case for Core here - we just never call it. + */ + void _free_pd(); + + + /*************** + ** Debugging ** + ***************/ + + void _debug_log_pds(void); + void _debug_log_threads(void); + + public: + + /** + * Constructor + */ + Platform_pd(signed pd_id = PD_INVALID, bool create = true); + + /** + * Destructor + */ + ~Platform_pd(); + + /** + * Initialize L4 task facility + */ + static void init(); + + /** + * Bind thread to protection domain + * + * \return 0 on success or + * -1 if thread ID allocation failed. + * + * This function allocates the physical L4 thread ID. + */ + int bind_thread(Platform_thread *thread); + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread *thread); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent) { return 0; } + + int pd_id() const { return _pd_id; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ diff --git a/base-fiasco/src/core/include/platform_thread.h b/base-fiasco/src/core/include/platform_thread.h new file mode 100644 index 000000000..41d9215d1 --- /dev/null +++ b/base-fiasco/src/core/include/platform_thread.h @@ -0,0 +1,142 @@ +/* + * \brief Fiasco thread facility + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__PLATFORM_THREAD_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +namespace Genode { + + class Platform_pd; + class Platform_thread + { + private: + + int _thread_id; /* plain thread number */ + Native_thread_id _l4_thread_id; /* L4 thread ID */ + char _name[32]; /* thread name that will be + registered at the kernel + debugger */ + Platform_pd *_platform_pd; /* protection domain thread + is bound to */ + Pager_object *_pager; + + public: + + enum { + THREAD_INVALID = -1, /* invalid thread number */ + }; + + /** + * Constructor + */ + Platform_thread(const char *name = 0, unsigned priority = 0, + int thread_id = THREAD_INVALID); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * This thread is about to be bound + * + * \param thread_id local thread ID + * \param l4_thread_id final L4 thread ID + * \param pd platform pd, thread is bound to + */ + void bind(int thread_id, Native_thread_id l4_thread_id, + Platform_pd *pd); + + /** + * Unbind this thread + */ + void unbind(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Return/set pager + */ + Pager_object *pager() const { return _pager; } + void pager(Pager_object *pager) { _pager = pager; } + + /** + * Return identification of thread when faulting + */ + unsigned long pager_object_badge() const { + return convert_native_thread_id_to_badge(_l4_thread_id); } + + + /******************************* + ** Fiasco-specific Accessors ** + *******************************/ + + int thread_id() const { return _thread_id; } + Native_thread_id native_thread_id() const { return _l4_thread_id; } + const char *name() const { return _name; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-fiasco/src/core/include/util.h b/base-fiasco/src/core/include/util.h new file mode 100644 index 000000000..cda2f5641 --- /dev/null +++ b/base-fiasco/src/core/include/util.h @@ -0,0 +1,121 @@ +/* + * \brief Fiasco utilities + * \author Christian Helmuth + * \date 2006-04-11 + * + * Is very practical now, but please keep the errors of the l4util pkg in mind. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__UTIL_H_ +#define _CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +#include +} + +namespace Genode { + + inline void log_event(const char *s) + { + Fiasco::fiasco_tbuf_log(s); + } + + inline void log_event(const char *s, unsigned v1, unsigned v2, unsigned v3) + { + Fiasco::fiasco_tbuf_log_3val(s, v1, v2, v3); + } + + inline void panic(const char *s) + { + using namespace Fiasco; + outstring(s); + enter_kdebug("> panic <"); + } + + inline void touch_ro(const void *addr, unsigned size) + { + using namespace Fiasco; + unsigned char const volatile *bptr; + unsigned char const *eptr; + + bptr = (unsigned char const volatile *)(((unsigned)addr) & L4_PAGEMASK); + eptr = (unsigned char const *)(((unsigned)addr + size - 1) & L4_PAGEMASK); + for ( ; bptr <= eptr; bptr += L4_PAGESIZE) + touch_read(bptr); + } + + inline void touch_rw(const void *addr, unsigned size) + { + using namespace Fiasco; + unsigned char volatile *bptr; + unsigned char const *eptr; + + bptr = (unsigned char volatile *)(((unsigned)addr) & L4_PAGEMASK); + eptr = (unsigned char const *)(((unsigned)addr + size - 1) & L4_PAGEMASK); + for (; bptr <= eptr; bptr += L4_PAGESIZE) + touch_read_write(bptr); + } + + inline addr_t trunc_page(addr_t addr) + { + using namespace Fiasco; + return l4_trunc_page(addr); + } + + inline addr_t round_page(addr_t addr) + { + using namespace Fiasco; + return l4_round_page(addr); + } + + inline addr_t round_superpage(addr_t addr) + { + using namespace Fiasco; + return l4_round_superpage(addr); + } + + inline size_t get_page_size() { return L4_PAGESIZE; } + + inline size_t get_page_size_log2() { return L4_LOG2_PAGESIZE; } + + inline size_t get_super_page_size() { return L4_SUPERPAGESIZE; } + + inline size_t get_super_page_size_log2() { return L4_LOG2_SUPERPAGESIZE; } + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long badge) + { + Native_thread_id tid; + tid.raw = badge; + printf("%s (%s pf_addr=%p pf_ip=%p from %x.%02x)\n", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, + (int)tid.id.task, (int)tid.id.lthread); + } + + inline addr_t map_src_addr(addr_t core_local_addr, addr_t phys_addr) { + return core_local_addr; } + + inline size_t constrain_map_size_log2(size_t size_log2) { return size_log2; } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-fiasco/src/core/io_mem_session_support.cc b/base-fiasco/src/core/io_mem_session_support.cc new file mode 100644 index 000000000..14ea9b13b --- /dev/null +++ b/base-fiasco/src/core/io_mem_session_support.cc @@ -0,0 +1,95 @@ +/* + * \brief Fiasco-specific implementation of the IO_MEM session interface + * \author Christian Helmuth + * \date 2006-08-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +} + +using namespace Genode; + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ + platform()->region_alloc()->free(reinterpret_cast(base)); +} + + +static inline bool can_use_super_page(addr_t base, size_t size) { + return (base & (get_super_page_size() - 1)) == 0 + && (size >= get_super_page_size()); } + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ + using namespace Fiasco; + + /* align large I/O dataspaces on a super-page boundary within core */ + size_t alignment = (size >= get_super_page_size()) ? get_super_page_size_log2() + : get_page_size_log2(); + + /* find appropriate region for mapping */ + void *local_base = 0; + if (!platform()->region_alloc()->alloc_aligned(size, &local_base, alignment)) + return 0; + + /* call sigma0 for I/O region */ + int err; + l4_umword_t request; + l4_umword_t dw0, dw1; + l4_msgdope_t result; + l4_msgtag_t tag; + + l4_threadid_t sigma0 = sigma0_threadid; + + unsigned offset = 0; + while (size) { + /* FIXME what about caching demands? */ + /* FIXME what about read / write? */ + + /* special case for page0, which is RAM in sigma0/x86 */ + if (base + offset == 0) + request = SIGMA0_REQ_FPAGE_RAM; + else + request = SIGMA0_REQ_FPAGE_IOMEM; + + size_t page_size_log2 = get_page_size_log2(); + if (can_use_super_page(base + offset, size)) + page_size_log2 = get_super_page_size_log2(); + + err = l4_ipc_call_tag(sigma0, + L4_IPC_SHORT_MSG, + request, + l4_fpage(base + offset, page_size_log2, 0, 0).fpage, + l4_msgtag(L4_MSGTAG_SIGMA0, 0, 0, 0), + L4_IPC_MAPMSG((addr_t)local_base + offset, page_size_log2), + &dw0, &dw1, + L4_IPC_NEVER, &result, &tag); + + if (err || !l4_ipc_fpage_received(result)) { + PERR("%d %d", err, l4_ipc_fpage_received(result)); + return 0; + } + + offset += 1 << page_size_log2; + size -= 1 << page_size_log2; + } + + return (addr_t)local_base; +} diff --git a/base-fiasco/src/core/irq_session_component.cc b/base-fiasco/src/core/irq_session_component.cc new file mode 100644 index 000000000..89e726f53 --- /dev/null +++ b/base-fiasco/src/core/irq_session_component.cc @@ -0,0 +1,122 @@ +/* + * \brief Core implementation of IRQ sessions + * \author Christian Helmuth + * \date 2007-09-13 + * + * FIXME ram quota missing + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +} + +using namespace Genode; + + +bool Irq_session_component::Irq_control_component::associate_to_irq(unsigned irq_number) +{ + using namespace Fiasco; + + int err; + l4_threadid_t irq_tid; + l4_umword_t dw0, dw1; + l4_msgdope_t result; + + l4_make_taskid_from_irq(irq_number, &irq_tid); + + /* boost thread to IRQ priority */ + enum { IRQ_PRIORITY = 0xC0 }; + + l4_sched_param_t param = {sp:{prio:IRQ_PRIORITY, small:0, state:0, time_exp:0, time_man:0}}; + l4_threadid_t ext_preempter = L4_INVALID_ID; + l4_threadid_t partner = L4_INVALID_ID; + l4_sched_param_t old_param; + l4_thread_schedule(l4_myself(), param, &ext_preempter, &partner, &old_param); + + err = l4_ipc_receive(irq_tid, + L4_IPC_SHORT_MSG, &dw0, &dw1, + L4_IPC_BOTH_TIMEOUT_0, &result); + + if (err != L4_IPC_RETIMEOUT) PERR("IRQ association failed"); + + return (err == L4_IPC_RETIMEOUT); +} + + +void Irq_session_component::wait_for_irq() +{ + using namespace Fiasco; + + l4_threadid_t irq_tid; + l4_umword_t dw0=0, dw1=0; + l4_msgdope_t result; + + l4_make_taskid_from_irq(_irq_number, &irq_tid); + + do { + l4_ipc_call(irq_tid, + L4_IPC_SHORT_MSG, 0, 0, + L4_IPC_SHORT_MSG, &dw0, &dw1, + L4_IPC_NEVER, &result); + + if (L4_IPC_IS_ERROR(result)) PERR("Ipc error %lx", L4_IPC_ERROR(result)); + } while (L4_IPC_IS_ERROR(result)); +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _ep(cap_session, STACK_SIZE, "irqctrl"), + _control_cap(_ep.manage(&_control_component)), + _control_client(_control_cap) +{ + bool shared = Arg_string::find_arg(args, "irq_shared").bool_value(false); + if (shared) { + PWRN("IRQ sharing not supported"); + throw Root::Invalid_args(); + } + + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + if (irq_number == -1 || !irq_alloc || + irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK) { + PERR("Unavailable IRQ %lx requested", irq_number); + throw Root::Invalid_args(); + } + _irq_number = irq_number; + + if (!_control_client.associate_to_irq(irq_number)) { + PWRN("IRQ association failed"); + throw Root::Invalid_args(); + } + + /* initialize capability */ + _irq_cap = Irq_session_capability(_ep.manage(this)); +} + + +Irq_session_component::~Irq_session_component() +{ + PERR("Implement me, immediately!"); +} + diff --git a/base-fiasco/src/core/platform.cc b/base-fiasco/src/core/platform.cc new file mode 100644 index 000000000..f9782720c --- /dev/null +++ b/base-fiasco/src/core/platform.cc @@ -0,0 +1,509 @@ +/* + * \brief Fiasco platform interface implementation + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +#include +#include +#include +} + +using namespace Genode; + + +static const bool verbose = true; +static const bool verbose_core_pf = false; +static const bool verbose_region_alloc = false; + + +/*********************************** + ** Core address space management ** + ***********************************/ + +static Synchronized_range_allocator &_core_address_ranges() +{ + static Synchronized_range_allocator _core_address_ranges(0); + return _core_address_ranges; +} + +enum { PAGER_STACK_ELEMENTS = 1024 }; +static unsigned long _core_pager_stack[PAGER_STACK_ELEMENTS]; +static unsigned _core_pager_arg; + + +/** + * Core pager "service loop" + */ +static void _core_pager_loop() +{ + unsigned pd_id = _core_pager_arg; + + using namespace Fiasco; + + l4_threadid_t t; + l4_umword_t dw0, dw1; + l4_msgdope_t r; + + bool send_reply = false; + + while (1) { + + if (send_reply) + /* unblock faulter and wait for next pagefault */ + l4_ipc_reply_and_wait(t, L4_IPC_SHORT_MSG, 0, 0, + &t, L4_IPC_SHORT_MSG, &dw0, &dw1, + L4_IPC_NEVER, &r); + else + l4_ipc_wait(&t, L4_IPC_SHORT_MSG, &dw0, &dw1, L4_IPC_NEVER, &r); + + /* ignore messages from non-core pds */ + if (t.id.task != pd_id) break; + + /* detect local map request */ + if (dw1 == 0) { + l4_msgdope_t ipc_result; + l4_ipc_send(t, L4_IPC_SHORT_FPAGE, 0, dw0, + L4_IPC_SEND_TIMEOUT_0, &ipc_result); + send_reply = false; + continue; + } + + bool rw = dw0 & 2; + addr_t pfa = dw0 & ~2; + + if (pfa < L4_PAGESIZE) { + + /* NULL pointer access */ + PERR("Possible null pointer %s in %x.%02x at %lx IP %lx", + rw ? "WRITE" : "READ", (int)t.id.task, (int)t.id.lthread, pfa, dw1); + /* do not unblock faulter */ + send_reply = false; + continue; + } else if (!_core_address_ranges().valid_addr(pfa)) { + + /* page-fault address is not in RAM */ + PERR("%s access outside of RAM in %x.%02x at %lx IP %lx", + rw ? "WRITE" : "READ", (int)t.id.task, (int)t.id.lthread, pfa, dw1); + /* do not unblock faulter */ + send_reply = false; + continue; + } else if (verbose_core_pf) + PDBG("pfa=%lx ip=%lx thread %x.%02x", pfa, dw1, (int)t.id.task, (int)t.id.lthread); + + /* my pf handler is sigma0 - just touch the appropriate page */ + if (rw) + touch_rw((void *)pfa, 1); + else + touch_ro((void *)pfa, 1); + + send_reply = true; + } +} + + +Platform::Sigma0::Sigma0() : Pager_object(0) +{ + cap(reinterpret_cap_cast(Native_capability(Fiasco::sigma0_threadid, 0))); +} + + +Platform::Sigma0 *Platform::sigma0() +{ + static Sigma0 _sigma0; + return &_sigma0; +} + + +Platform::Core_pager::Core_pager(Platform_pd *core_pd) +: + Platform_thread("core.pager"), Pager_object(0) +{ + Platform_thread::pager(sigma0()); + + core_pd->bind_thread(this); + cap(Native_capability(native_thread_id(), 0)); + + /* pager needs to know core's pd ID */ + _core_pager_arg = core_pd->pd_id(); + + /* stack begins at the top end of the '_core_pager_stack' array */ + void *sp = (void *)&_core_pager_stack[PAGER_STACK_ELEMENTS - 1]; + start((void *)_core_pager_loop, sp); + + using namespace Fiasco; + + /* pager0 receives pagefaults from me - for NULL pointer detection */ + l4_umword_t d; + l4_threadid_t preempter = L4_INVALID_ID; + l4_threadid_t pager = native_thread_id(); + l4_thread_ex_regs(l4_myself(), ~0UL, ~0UL, &preempter, &pager, &d, &d, &d); +} + + +Platform::Core_pager *Platform::core_pager() +{ + static Core_pager _core_pager(core_pd()); + return &_core_pager; +} + + +/*********************************** + ** Helper for L4 region handling ** + ***********************************/ + +struct Region +{ + addr_t start; + addr_t end; + + Region() : start(0), end(0) { } + Region(addr_t s, addr_t e) : start(s), end(e) { } +}; + + +/** + * Log region + */ +static inline void print_region(Region r) +{ + printf("[%08lx,%08lx) %08lx", r.start, r.end, r.end - r.start); +} + + +/** + * Add region to allocator + */ +static inline void add_region(Region r, Range_allocator &alloc) +{ + if (verbose_region_alloc) { + printf("%p add: ", &alloc); print_region(r); printf("\n"); + } + + /* adjust region */ + addr_t start = trunc_page(r.start); + addr_t end = round_page(r.end); + + alloc.add_range(start, end - start); +} + + +/** + * Remove region from allocator + */ +static inline void remove_region(Region r, Range_allocator &alloc) +{ + if (verbose_region_alloc) { + printf("%p remove: ", &alloc); print_region(r); printf("\n"); + } + + /* adjust region */ + addr_t start = trunc_page(r.start); + addr_t end = round_page(r.end); + + alloc.remove_range(start, end - start); +} + + +/** + * Request any RAM page from Sigma0 + */ +static inline int sigma0_req_region(addr_t *addr, unsigned log2size) +{ + using namespace Fiasco; + + /* XXX sigma0 always maps pages RW */ + l4_umword_t req_fpage = l4_fpage(0, log2size, 0, 0).fpage; + void* rcv_window = L4_IPC_MAPMSG(0, L4_WHOLE_ADDRESS_SPACE); + addr_t base; + l4_fpage_t rcv_fpage; + l4_msgdope_t result; + l4_msgtag_t tag; + + int err = l4_ipc_call_tag(Fiasco::sigma0_threadid, + L4_IPC_SHORT_MSG, SIGMA0_REQ_FPAGE_ANY, req_fpage, + l4_msgtag(L4_MSGTAG_SIGMA0, 0, 0, 0), + rcv_window, &base, (l4_umword_t *)&rcv_fpage, + L4_IPC_NEVER, &result, &tag); + int ret = (err || !l4_ipc_fpage_received(result)); + + if (!ret) touch_rw((void *)addr, 1); + + *addr = base; + return ret; +} + + +void Platform::_setup_mem_alloc() +{ + /* + * Completely map program image by touching all pages read-only to + * prevent sigma0 from handing out those page as anonymous memory. + */ + volatile const char *beg, *end; + beg = (const char *)(((unsigned)&_prog_img_beg) & L4_PAGEMASK); + end = (const char *)&_prog_img_end; + for ( ; beg < end; beg += L4_PAGESIZE) (void)(*beg); + + /* request pages of known page size starting with largest */ + size_t log2_sizes[] = { L4_LOG2_SUPERPAGESIZE, L4_LOG2_PAGESIZE }; + + for (unsigned i = 0; i < sizeof(log2_sizes)/sizeof(*log2_sizes); ++i) { + size_t log2_size = log2_sizes[i]; + size_t size = 1 << log2_size; + + int err = 0; + addr_t addr; + Region region; + + /* request any page of current size from sigma0 */ + do { + err = sigma0_req_region(&addr, log2_size); + if (!err) { + /* XXX do not allocate page0 */ + if (addr == 0) { + Fiasco::l4_fpage_unmap(Fiasco::l4_fpage(0, log2_size, 0, 0), + L4_FP_FLUSH_PAGE | L4_FP_ALL_SPACES); + continue; + } + + region.start = addr; region.end = addr + size; + add_region(region, _ram_alloc); + add_region(region, _core_address_ranges()); + remove_region(region, _io_mem_alloc); + remove_region(region, _region_alloc); + } + } while (!err); + } +} + + +void Platform::_setup_irq_alloc() { + _irq_alloc.add_range(0, 0x10); } + + +void Platform::_setup_basics() +{ + using namespace Fiasco; + + int err; + + /* region allocator is not setup yet */ + + /* map KIP one-to-one */ + void *fpage = L4_IPC_MAPMSG(0, L4_WHOLE_ADDRESS_SPACE); + l4_umword_t dw0, dw1; + l4_msgdope_t r; + l4_msgtag_t tag; + + err = l4_ipc_call_tag(Fiasco::sigma0_threadid, + L4_IPC_SHORT_MSG, SIGMA0_REQ_KIP, 0, + l4_msgtag(L4_MSGTAG_SIGMA0, 0, 0, 0), + fpage, &dw0, &dw1, + L4_IPC_NEVER, &r, &tag); + + bool amok = false; + if (err) { + printf("IPC error %d\n", err); + amok = true; + } + if (!l4_ipc_fpage_received(r)) { + printf("No fpage received\n"); + amok = true; + } + + if (amok) + panic("kip mapping failed"); + + /* store mapping base from received mapping */ + l4_kernel_info_t *kip = (l4_kernel_info_t *)dw0; + + if (kip->magic != L4_KERNEL_INFO_MAGIC) + panic("Sigma0 mapped something but not the KIP"); + + if (verbose) { + printf("\n"); + printf("KIP @ %p\n", kip); + printf(" magic: %08x\n", kip->magic); + printf(" version: %08x\n", kip->version); + printf(" sigma0 "); printf(" esp: %08lx eip: %08lx\n", kip->sigma0_esp, kip->sigma0_eip); + printf(" sigma1 "); printf(" esp: %08lx eip: %08lx\n", kip->sigma1_esp, kip->sigma1_eip); + printf(" root "); printf(" esp: %08lx eip: %08lx\n", kip->root_esp, kip->root_eip); + } + + /* add KIP as ROM module */ + _kip_rom = Rom_module((addr_t)kip, L4_PAGESIZE, "l4v2_kip"); + _rom_fs.insert(&_kip_rom); + + /* update multi-boot info pointer from KIP */ + void *mb_info_ptr = (void *)kip->user_ptr; + _mb_info = Multiboot_info(mb_info_ptr); + if (verbose) printf("MBI @ %p\n", mb_info_ptr); + + /* parse memory descriptors - look for virtual memory configuration */ + /* XXX we support only one VM region (here and also inside RM) */ + using L4::Kip::Mem_desc; + + _vm_start = 0; _vm_size = 0; + Mem_desc *desc = Mem_desc::first(kip); + + for (unsigned i = 0; i < Mem_desc::count(kip); ++i) + if (desc[i].is_virtual()) { + _vm_start = round_page(desc[i].start()); + _vm_size = trunc_page(desc[i].end() - _vm_start + 1); + + break; + } + if (_vm_size == 0) + panic("Virtual memory configuration not found"); + + /* configure applicable address space but never use page0 */ + _vm_size = _vm_start == 0 ? _vm_size - L4_PAGESIZE : _vm_size; + _vm_start = _vm_start == 0 ? L4_PAGESIZE : _vm_start; + _region_alloc.add_range(_vm_start, _vm_size); + + /* preserve context area in core's virtual address space */ + _region_alloc.remove_range(Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + Thread_base::CONTEXT_AREA_VIRTUAL_SIZE); + + /* I/O memory could be the whole user address space */ + /* FIXME if the kernel helps to find out max address - use info here */ + _io_mem_alloc.add_range(0, ~0); + + /* remove KIP and MBI area from region and IO_MEM allocator */ + remove_region(Region((addr_t)kip, (addr_t)kip + L4_PAGESIZE), _region_alloc); + remove_region(Region((addr_t)kip, (addr_t)kip + L4_PAGESIZE), _io_mem_alloc); + remove_region(Region((addr_t)mb_info_ptr, (addr_t)mb_info_ptr + _mb_info.size()), _region_alloc); + remove_region(Region((addr_t)mb_info_ptr, (addr_t)mb_info_ptr + _mb_info.size()), _io_mem_alloc); + + /* remove core program image memory from region and IO_MEM allocator */ + addr_t img_start = (addr_t) &_prog_img_beg; + addr_t img_end = (addr_t) &_prog_img_end; + remove_region(Region(img_start, img_end), _region_alloc); + remove_region(Region(img_start, img_end), _io_mem_alloc); + + /* image is accessible by core */ + add_region(Region(img_start, img_end), _core_address_ranges()); +} + + +void Platform::_setup_rom() +{ + Rom_module rom; + + for (unsigned i = FIRST_ROM; i < _mb_info.num_modules(); i++) { + if (!(rom = _mb_info.get_module(i)).valid()) continue; + + Rom_module *new_rom = new(core_mem_alloc()) Rom_module(rom); + _rom_fs.insert(new_rom); + + if (verbose) + printf(" mod[%d] [%p,%p) %s\n", i, + (void *)new_rom->addr(), ((char *)new_rom->addr()) + new_rom->size(), + new_rom->name()); + + /* zero remainder of last ROM page */ + size_t count = L4_PAGESIZE - rom.size() % L4_PAGESIZE; + if (count != L4_PAGESIZE) + memset(reinterpret_cast(rom.addr() + rom.size()), 0, count); + + /* remove ROM area from region and IO_MEM allocator */ + remove_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _region_alloc); + remove_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _io_mem_alloc); + + /* add area to core-accessible ranges */ + add_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _core_address_ranges()); + } +} + + +Platform::Platform() : + _ram_alloc(0), _io_mem_alloc(core_mem_alloc()), + _io_port_alloc(core_mem_alloc()), _irq_alloc(core_mem_alloc()), + _region_alloc(core_mem_alloc()) +{ + /* + * We must be single-threaded at this stage and so this is safe. + */ + static bool initialized = 0; + if (initialized) panic("Platform constructed twice!"); + initialized = true; + + _setup_basics(); + _setup_mem_alloc(); + _setup_io_port_alloc(); + _setup_irq_alloc(); + _setup_rom(); + + if (verbose) { + printf(":ram_alloc: "); _ram_alloc.raw()->dump_addr_tree(); + printf(":region_alloc: "); _region_alloc.raw()->dump_addr_tree(); + printf(":io_mem: "); _io_mem_alloc.raw()->dump_addr_tree(); + printf(":io_port: "); _io_port_alloc.raw()->dump_addr_tree(); + printf(":irq: "); _irq_alloc.raw()->dump_addr_tree(); + printf(":rom_fs: "); _rom_fs.print_fs(); + printf(":core ranges: "); _core_address_ranges().raw()->dump_addr_tree(); + } + + Fiasco::l4_threadid_t myself = Fiasco::l4_myself(); + + Platform_pd::init(); + + /* setup pd object for core pd */ + _core_pd = new(core_mem_alloc()) Platform_pd(myself.id.task, false); + + /* + * We setup the thread object for thread0 in core pd using a special + * interface that allows us to specify the lthread number. + */ + Platform_thread *core_thread = new(core_mem_alloc()) Platform_thread("core.main", myself.id.lthread); + core_thread->pager(sigma0()); + _core_pd->bind_thread(core_thread); + + /* we never call _core_thread.start(), so set name directly */ + Fiasco::fiasco_register_thread_name(core_thread->native_thread_id(), core_thread->name()); +} + + +/******************************** + ** Generic platform interface ** + ********************************/ + +void Platform::wait_for_exit() +{ + /* + * On Fiasco, Core never exits. So let us sleep forever. + */ + sleep_forever(); +} + + +void Core_parent::exit(int exit_value) { } diff --git a/base-fiasco/src/core/platform_pd.cc b/base-fiasco/src/core/platform_pd.cc new file mode 100644 index 000000000..329ad3d22 --- /dev/null +++ b/base-fiasco/src/core/platform_pd.cc @@ -0,0 +1,290 @@ +/* + * \brief Fiasco protection domain facility + * \author Christian Helmuth + * \date 2006-04-11 + * + * On Fiasco, the pd class has several duties: + * + * - It is an allocator for L4 tasks and cares for versioning and recycling. We + * do this with "static class members". + * - L4 threads are tied to L4 tasks and there are only 128 per L4 task. So + * each pd object is an allocator for its threads. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +using namespace Fiasco; +using namespace Genode; + + +static const bool verbose = false; + + +/************************** + ** Static class members ** + **************************/ + +static bool _init = false; + + +void Platform_pd::init() +{ + if (_init) return; + + unsigned i; + Pd_alloc reserved(true, true, 0); + Pd_alloc free(false, true, 0); + + /* mark reserved protection domains */ + for (i = 0; i < PD_FIRST; ++i) _pds()[i] = reserved; + /* init remainder */ + for ( ; i < PD_MAX; ++i) _pds()[i] = free; + + _init = true; +} + + +/**************************** + ** Private object members ** + ****************************/ + +void Platform_pd::_create_pd(bool syscall) +{ + l4_threadid_t l4t = l4_myself(); + l4t.id.task = _pd_id; + l4t.id.lthread = 0; + l4t.id.version_low = _version; + + l4_taskid_t nt; + if (syscall) + nt = l4_task_new(l4t, 0, 0, 0, l4t); + else + nt = l4t; + + if (l4_is_nil_id(nt)) + panic("pd creation failed"); + + _l4_task_id = nt; +} + + +void Platform_pd::_destroy_pd() +{ + l4_threadid_t l4t = _l4_task_id; + + /* L4 task deletion is: make inactive with myself as chief in 2nd parameter */ + l4_taskid_t nt = l4_task_new(l4t, convert_native_thread_id_to_badge(l4_myself()), + 0, 0, L4_NIL_ID); + + if (l4_is_nil_id(nt)) + panic("pd destruction failed"); + + _l4_task_id = L4_INVALID_ID; +} + + +int Platform_pd::_alloc_pd(signed pd_id) +{ + if (pd_id == PD_INVALID) { + unsigned i; + + for (i = PD_FIRST; i < PD_MAX; i++) + if (_pds()[i].free) break; + + /* no free protection domains available */ + if (i == PD_MAX) return -1; + + pd_id = i; + } else { + if (!_pds()[pd_id].reserved || !_pds()[pd_id].free) + return -1; + } + + _pds()[pd_id].free = 0; + + _pd_id = pd_id; + _version = _pds()[pd_id].version; + + return pd_id; +} + + +void Platform_pd::_free_pd() +{ + unsigned t = _pd_id; + + /* XXX check and log double-free? */ + if (_pds()[t].free) return; + + /* maximum reuse count reached leave non-free */ + if (_pds()[t].version == PD_VERSION_MAX) return; + + _pds()[t].free = 1; + ++_pds()[t].version; +} + + +void Platform_pd::_init_threads() +{ + unsigned i; + + for (i = 0; i < THREAD_MAX; ++i) + _threads[i] = 0; +} + + +Platform_thread* Platform_pd::_next_thread() +{ + unsigned i; + + /* look for bound thread */ + for (i = 0; i < THREAD_MAX; ++i) + if (_threads[i]) break; + + /* no bound threads */ + if (i == THREAD_MAX) return 0; + + return _threads[i]; +} + + +int Platform_pd::_alloc_thread(int thread_id, Platform_thread *thread) +{ + int i = thread_id; + + /* look for free thread */ + if (thread_id == Platform_thread::THREAD_INVALID) { + for (i = 0; i < THREAD_MAX; ++i) + if (!_threads[i]) break; + + /* no free threads available */ + if (i == THREAD_MAX) return -1; + } else { + if (_threads[i]) return -2; + } + + _threads[i] = thread; + + return i; +} + + +void Platform_pd::_free_thread(int thread_id) +{ + if (!_threads[thread_id]) + PWRN("double-free of thread %x.%x detected", _pd_id, thread_id); + + _threads[thread_id] = 0; +} + + +/*************************** + ** Public object members ** + ***************************/ + +int Platform_pd::bind_thread(Platform_thread *thread) +{ + /* thread_id is THREAD_INVALID by default - only core is the special case */ + int thread_id = thread->thread_id(); + l4_threadid_t l4_thread_id; + + int t = _alloc_thread(thread_id, thread); + if (t < 0) { + PERR("Thread alloc failed"); + return -1; + } + thread_id = t; + + l4_thread_id = _l4_task_id; + l4_thread_id.id.lthread = thread_id; + + /* finally inform thread about binding */ + thread->bind(thread_id, l4_thread_id, this); + + if (verbose) _debug_log_threads(); + return 0; +} + + +void Platform_pd::unbind_thread(Platform_thread *thread) +{ + int thread_id = thread->thread_id(); + + /* unbind thread before proceeding */ + thread->unbind(); + + _free_thread(thread_id); + + if (verbose) _debug_log_threads(); +} + + +Platform_pd::Platform_pd(signed pd_id, bool create) +{ + /* check correct init */ + if (!_init) + panic("init pd facility via Platform_pd::init() before using it!"); + + /* init threads */ + _init_threads(); + + int ret = _alloc_pd(pd_id); + if (ret < 0) { + _debug_log_pds(); + panic("pd alloc failed"); + } + + _create_pd(create); +} + + +Platform_pd::~Platform_pd() +{ + /* unbind all threads */ + while (Platform_thread *t = _next_thread()) unbind_thread(t); + + _destroy_pd(); + _free_pd(); +} + + +/*********************** + ** Debugging support ** + ***********************/ + +void Platform_pd::_debug_log_threads() +{ + int i; + printf("[%02x] ", _pd_id); + for (i = 0; i < THREAD_MAX; ++i) { + printf("%c", !_threads[i] ? '.' : 'X'); + if (i == 63) printf("\n "); + } + printf("\n"); +} + + +void Platform_pd::_debug_log_pds() +{ + int i; + for (i = 0; i < PD_MAX; ++i) + printf("[%02x] %d %d %d\n", i, _pds()[i].reserved, _pds()[i].free, + _pds()[i].version); +} diff --git a/base-fiasco/src/core/platform_thread.cc b/base-fiasco/src/core/platform_thread.cc new file mode 100644 index 000000000..fbd632197 --- /dev/null +++ b/base-fiasco/src/core/platform_thread.cc @@ -0,0 +1,153 @@ +/* + * \brief Fiasco thread facility + * \author Christian Helmuth + * \date 2006-04-11 + * + * This provides a thread object and uses l4_inter_task_ex_regs() for L4 thread + * manipulation. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +#include +} + +using namespace Genode; +using namespace Fiasco; + + +int Platform_thread::start(void *ip, void *sp) +{ + l4_umword_t dummy, old_eflags; + l4_threadid_t thread = _l4_thread_id; + l4_threadid_t pager = _pager ? _pager->cap().tid() : L4_INVALID_ID; + l4_threadid_t preempter = L4_INVALID_ID; + l4_threadid_t cap_handler = L4_INVALID_ID; + + l4_inter_task_ex_regs(thread, (l4_umword_t)ip, (l4_umword_t)sp, + &preempter, &pager, &cap_handler, + &old_eflags, &dummy, &dummy, + 0, l4_utcb_get()); + if (old_eflags == ~0UL) + PWRN("old eflags == ~0 on ex_regs %x.%x", (int)thread.id.task, (int)thread.id.lthread); + + fiasco_register_thread_name(thread, _name); + return 0; +} + + +void Platform_thread::pause() +{ + PDBG("not implemented"); +} + + +void Platform_thread::resume() +{ + PDBG("not implemented"); +} + + +void Platform_thread::bind(int thread_id, l4_threadid_t l4_thread_id, Platform_pd *pd) +{ + _thread_id = thread_id; + _l4_thread_id = l4_thread_id; + _platform_pd = pd; +} + + +void Platform_thread::unbind() +{ + l4_umword_t dummy, old_eflags; + l4_threadid_t thread = _l4_thread_id; + l4_threadid_t pager = thread; + l4_threadid_t preempter = L4_INVALID_ID; + l4_threadid_t cap_handler = L4_INVALID_ID; + + fiasco_register_thread_name(thread, ""); + + /* + * The Fiasco thread is halted by setting itself as pager and forcing + * pagefault at 0, where Genode never maps a page. The bottom line is the + * thread blocks in IPC to itself. + */ + l4_inter_task_ex_regs(thread, 0, 0, + &preempter, &pager, &cap_handler, + &old_eflags, &dummy, &dummy, + 0, l4_utcb_get()); + if (old_eflags == ~0UL) + PWRN("old eflags == ~0 on ex_regs %x.%x", (int)thread.id.task, (int)thread.id.lthread); + + _thread_id = THREAD_INVALID; + _l4_thread_id = L4_INVALID_ID; + _platform_pd = 0; +} + + +int Platform_thread::state(Thread_state *state_dst) +{ + l4_umword_t old_eflags, ip, sp; + l4_threadid_t thread = _l4_thread_id; + l4_threadid_t pager = L4_INVALID_ID; + l4_threadid_t preempter = L4_INVALID_ID; + l4_threadid_t cap_handler = L4_INVALID_ID; + + l4_inter_task_ex_regs(thread, ~0UL, ~0UL, + &preempter, &pager, &cap_handler, + &old_eflags, &ip, &sp, + L4_THREAD_EX_REGS_NO_CANCEL, l4_utcb_get()); + if (old_eflags == ~0UL) + PWRN("old eflags == ~0 on ex_regs %x.%x", (int)thread.id.task, (int)thread.id.lthread); + + /* fill thread state structure */ + state_dst->ip = ip; + state_dst->sp = sp; + + return 0; +} + + +void Platform_thread::cancel_blocking() +{ + l4_umword_t dummy; + l4_threadid_t invalid = L4_INVALID_ID; + + l4_inter_task_ex_regs(_l4_thread_id, ~0UL, ~0UL, + &invalid, &invalid, &invalid, + &dummy, &dummy, &dummy, 0, l4_utcb_get()); +} + + +Platform_thread::Platform_thread(const char *name, unsigned, int thread_id) +: _thread_id(thread_id), _l4_thread_id(L4_INVALID_ID), _pager(0) +{ + strncpy(_name, name, sizeof(_name)); +} + + +Platform_thread::~Platform_thread() +{ + /* + * We inform our protection domain about thread destruction, which will end up in + * Thread::unbind() + */ + if (_platform_pd) + _platform_pd->unbind_thread(this); +} diff --git a/base-fiasco/src/core/ram_session_support.cc b/base-fiasco/src/core/ram_session_support.cc new file mode 100644 index 000000000..9fe8d2c55 --- /dev/null +++ b/base-fiasco/src/core/ram_session_support.cc @@ -0,0 +1,27 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Norman Feske + * \date 2006-07-03 + * + * On L4, each dataspace _is_ a shared memory object. + * Therefore, these functions are empty. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "ram_session_component.h" + +using namespace Genode; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { } + +void Ram_session_component::_clear_ds(Dataspace_component *ds) +{ + memset((void *)ds->phys_addr(), 0, ds->size()); +} diff --git a/base-fiasco/src/core/rm_session_support.cc b/base-fiasco/src/core/rm_session_support.cc new file mode 100644 index 000000000..c2aa7f603 --- /dev/null +++ b/base-fiasco/src/core/rm_session_support.cc @@ -0,0 +1,48 @@ +/* + * \brief Fiasco-specific part of RM-session implementation + * \author Norman Feske + * \date 2009-04-10 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +} + +using namespace Genode; + +static const bool verbose_unmap = false; + + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + /* + * Fiasco's 'unmap' syscall unmaps the specified flexpage from all address + * spaces to which we mapped the pages. We cannot target this operation to + * a specific L4 task. Hence, we unmap the dataspace from all tasks, not + * only for this RM client. + */ + if (verbose_unmap) { + Fiasco::l4_threadid_t tid; tid.raw = badge(); + printf("RM client %p (%x.%x) unmap core-local [%lx,%lx)\n", + this, tid.id.task, tid.id.lthread, core_local_base, core_local_base + size); + } + + using namespace Fiasco; + + addr_t addr = core_local_base; + for (; addr < core_local_base + size; addr += L4_PAGESIZE) + l4_fpage_unmap(l4_fpage(addr, L4_LOG2_PAGESIZE, 0, 0), + L4_FP_FLUSH_PAGE); +} diff --git a/base-fiasco/src/core/target.inc b/base-fiasco/src/core/target.inc new file mode 100644 index 000000000..16505825c --- /dev/null +++ b/base-fiasco/src/core/target.inc @@ -0,0 +1,51 @@ +TARGET = core +REQUIRES = fiasco +LIBS = cxx ipc heap core_printf process pager lock raw_signal raw_server + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = main.cc \ + multiboot_info.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_start.cc \ + thread_bootstrap.cc \ + platform_thread.cc \ + platform_pd.cc \ + platform.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + dump_alloc.cc \ + context_area.cc + +INC_DIR += $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include + +vpath main.cc $(GEN_CORE_DIR) +vpath multiboot_info.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath signal_source_component.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath context_area.cc $(GEN_CORE_DIR) +vpath thread_bootstrap.cc $(BASE_DIR)/src/base/thread +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath %.cc $(REP_DIR)/src/core diff --git a/base-fiasco/src/core/thread_start.cc b/base-fiasco/src/core/thread_start.cc new file mode 100644 index 000000000..1041e5ecc --- /dev/null +++ b/base-fiasco/src/core/thread_start.cc @@ -0,0 +1,62 @@ +/* + * \brief Implementation of Thread API interface on top of Platform_thread + * \author Norman Feske + * \date 2006-05-03 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +using namespace Genode; + + +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + sleep_forever(); +} + + +void Thread_base::start() +{ + /* create and start platform thread */ + _tid.pt = new(platform()->core_mem_alloc()) Platform_thread(_context->name); + + platform_specific()->core_pd()->bind_thread(_tid.pt); + + _tid.pt->pager(platform_specific()->core_pager()); + _tid.l4id = _tid.pt->native_thread_id(); + + _tid.pt->start((void *)_thread_start, _context->stack); +} + + +void Thread_base::cancel_blocking() +{ + /* + * Within core, we never need to unblock threads + */ +} + + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() +{ + /* destruct platform thread */ + destroy(platform()->core_mem_alloc(), _tid.pt); +} diff --git a/base-fiasco/src/core/x86/platform_x86.cc b/base-fiasco/src/core/x86/platform_x86.cc new file mode 100644 index 000000000..4b79db17b --- /dev/null +++ b/base-fiasco/src/core/x86/platform_x86.cc @@ -0,0 +1,52 @@ +/* + * \brief Platform support specific to x86 + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +#include "platform.h" +#include "util.h" + +namespace Fiasco { +#include +} + +using namespace Genode; +using namespace Fiasco; + +void Platform::_setup_io_port_alloc() +{ + l4_fpage_t fp; + l4_umword_t dummy; + l4_msgdope_t result; + l4_msgtag_t tag; + + /* get all I/O ports at once */ + int error = l4_ipc_call_tag(Fiasco::sigma0_threadid, + L4_IPC_SHORT_MSG, + l4_iofpage(0, L4_WHOLE_IOADDRESS_SPACE, 0).fpage, 0, + l4_msgtag(L4_MSGTAG_IO_PAGE_FAULT, 0, 0, 0), + L4_IPC_IOMAPMSG(0, L4_WHOLE_IOADDRESS_SPACE), + &dummy, &fp.fpage, + L4_IPC_NEVER, &result, &tag); + + if (error || + !(l4_ipc_fpage_received(result) /* got something */ + && fp.iofp.f == 0xf /* got IO ports */ + && fp.iofp.iosize == L4_WHOLE_IOADDRESS_SPACE + && fp.iofp.iopage == 0)) /* got whole IO space */ + + panic("Received no I/O ports from sigma0"); + + /* setup allocator */ + _io_port_alloc.add_range(0, 0x10000); +} diff --git a/base-fiasco/src/core/x86/target.mk b/base-fiasco/src/core/x86/target.mk new file mode 100644 index 000000000..719b8306e --- /dev/null +++ b/base-fiasco/src/core/x86/target.mk @@ -0,0 +1,7 @@ +include $(PRG_DIR)/../target.inc + +REQUIRES += x86 +SRC_CC += platform_x86.cc + +vpath io_port_session_component.cc $(GEN_CORE_DIR)/x86 + diff --git a/base-fiasco/src/kernel/target.inc b/base-fiasco/src/kernel/target.inc new file mode 100644 index 000000000..b736792ff --- /dev/null +++ b/base-fiasco/src/kernel/target.inc @@ -0,0 +1,23 @@ +TARGET = fiasco +REQUIRES += fiasco +FIASCO_BUILD_DIR = $(BUILD_BASE_DIR)/kernel/$(TARGET) +FIASCO = $(FIASCO_BUILD_DIR)/fiasco +FIASCO_SRC = $(REP_DIR)/contrib/fiasco/snapshot/kernel/fiasco +STARTUP_LIB = + +$(TARGET): $(FIASCO) + +$(FIASCO_BUILD_DIR): + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) SYSTEM_TARGET="$(CROSS_DEV_PREFIX)" \ + $(VERBOSE_DIR) -C $(FIASCO_SRC) BUILDDIR=$@ + $(VERBOSE)cp $(KERNEL_CONFIG) $@/globalconfig.out + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) SYSTEM_TARGET="$(CROSS_DEV_PREFIX)" \ + $(VERBOSE_DIR) -C $@ oldconfig + +$(FIASCO): $(FIASCO_BUILD_DIR) + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) SYSTEM_TARGET="$(CROSS_DEV_PREFIX)" \ + $(VERBOSE_DIR) -C $(FIASCO_BUILD_DIR) + $(VERBOSE)ln -sf $@ $(BUILD_BASE_DIR)/bin/$(TARGET) + +clean cleanall: + $(VERBOSE)rm -rf $(FIASCO_BUILD_DIR) diff --git a/base-fiasco/src/kernel/x86/target.mk b/base-fiasco/src/kernel/x86/target.mk new file mode 100644 index 000000000..8bf4175e6 --- /dev/null +++ b/base-fiasco/src/kernel/x86/target.mk @@ -0,0 +1,4 @@ +REQUIRES = x86 32bit +KERNEL_CONFIG = $(REP_DIR)/config/kernel-config.x86 + +-include $(PRG_DIR)/../target.inc diff --git a/base-fiasco/src/platform/_main_helper.h b/base-fiasco/src/platform/_main_helper.h new file mode 100644 index 000000000..6cfa9d232 --- /dev/null +++ b/base-fiasco/src/platform/_main_helper.h @@ -0,0 +1,19 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Christian Prochaska + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM___MAIN_HELPER_H_ +#define _PLATFORM___MAIN_HELPER_H_ + +static void main_thread_bootstrap() { } + +#endif /* _PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-fiasco/src/platform/arm/Makefile b/base-fiasco/src/platform/arm/Makefile new file mode 100644 index 000000000..adf975841 --- /dev/null +++ b/base-fiasco/src/platform/arm/Makefile @@ -0,0 +1,25 @@ +SRC = ../x86/genode.ld +TARGET = genode.ld + +all: + @echo "--- available targets ---" + @echo " genode.ld - generate $(TARGET) from $(SRC)" + @echo " cleanall - remove generated file" + +# +# NOTE: We change the start address to 0x60000, which +# is the same address as used by the original +# roottask. +# +# On L4x0, the thread ID type is only 32bit instead of 64bit +# for L4v2. Therefore, we have to adapt the place holder for +# thread ID part of the parent capability. +# +genode.ld: + cp $(SRC) $@ + sed -i "s/= 0x[0-9]\+;/= 0x00060000;/" $@ + sed -i "54s/^.*$$/\t\tLONG(0xffffffff);/" $@ + +clean cleanall: + rm -f $(TARGET) + diff --git a/base-fiasco/src/platform/arm/_main.cc b/base-fiasco/src/platform/arm/_main.cc new file mode 100644 index 000000000..8a9babe9e --- /dev/null +++ b/base-fiasco/src/platform/arm/_main.cc @@ -0,0 +1,124 @@ +/** + * \brief Startup code for Fiasco/ARM + * \author Norman Feske + * \date 2007-04-30 + * + * Call constructors for static objects before calling main(). + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +namespace Fiasco { +#include +} + +/* Genode */ +#include +#include +#include +#include + +namespace Genode { + + /** + * Return constructed parent capability + */ + Parent_capability parent_cap() + { + Fiasco::l4_threadid_t tid = *(Fiasco::l4_threadid_t *)&_parent_cap_thread_id; + return Parent_capability(Native_capability(tid, _parent_cap_local_name)); + } +} + +using namespace Genode; + + +/*************** + ** C++ stuff ** + ***************/ + +/* + * This symbol must be defined when exception + * headers are defined in the linker script. + */ +extern "C" __attribute__((weak)) void *__gxx_personality_v0(void) +{ + Fiasco::outstring("What a surprise! This function is really used? Sorry - not implemented\n"); + return 0; +} + + +/** + * Resolve symbols needed by libsupc++ to make + * the linker happy. + * + * FIXME: implement us! + */ +extern "C" __attribute__((weak)) int atexit(void) { + Fiasco::outstring("atexit() called - not implemented!\n"); + return 0; +}; +extern "C" __attribute__((weak)) int memcmp(void) { + Fiasco::outstring("memcmp() called - not implemented!\n"); + return 0; +}; +extern "C" __attribute__((weak)) int strncmp(void) { + Fiasco::outstring("strncmp() called - not implemented!\n"); + return 0; +}; + + +extern int main(int argc, char **argv); + +extern void init_exception_handling(); /* implemented in base/cxx */ + + +/* FIXME no support for commandline + * ask parent for argc and argv */ +static char argv0[] = { '_', 'm', 'a', 'i', 'n'}; +static char *argv[1] = { argv0 }; + + +/* + * Define 'environ' pointer that is supposed to be exported by + * the startup code and relied on by any libC. Because we have no + * UNIX environment, however, we set this pointer to NULL. + */ +__attribute__((weak)) char **environ = (char **)0; + + +/** + * C entry function called by the crt0 startup code + */ +extern "C" int _main() +{ + /* call constructors for static objects */ + void (**func)(); + for (func = &_ctors_end; func != &_ctors_start; (*--func)()); + + /* initialize exception handling */ + init_exception_handling(); + + /* completely map program image by touching all pages read-only */ + volatile const char *beg, *end; + beg = (const char *)(((unsigned)&_prog_img_beg) & L4_PAGEMASK); + end = (const char *)&_prog_img_end; + for ( ; beg < end; beg += L4_PAGESIZE) (void)(*beg); + + /* call real main function */ + /* XXX no support for commandline */ + int ret = main(1, argv); + + /* inform parent about program exit */ + env()->parent()->exit(ret); + + PDBG("main() returned %d", ret); + sleep_forever(); + + return ret; +} diff --git a/base-fiasco/src/platform/arm/crt0.s b/base-fiasco/src/platform/arm/crt0.s new file mode 100644 index 000000000..14b23c62a --- /dev/null +++ b/base-fiasco/src/platform/arm/crt0.s @@ -0,0 +1,37 @@ +/** + * \brief Startup code for Genode applications on ARM + * \author Norman Feske + * \date 2007-04-28 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/*--- .text (program code) -------------------------*/ + .text + + .globl _start +_start: + + ldr sp, .initial_sp + b _main + +.initial_sp: .word _stack_high + + .globl __dso_handle +__dso_handle: + .long 0 + +/*--- .bss (non-initialized data) ------------------*/ +.section ".bss" + + .globl _stack_low +_stack_low: + .space 64*1024 + .globl _stack_high +_stack_high: + diff --git a/base-fiasco/src/sigma0/target.mk b/base-fiasco/src/sigma0/target.mk new file mode 100644 index 000000000..b9c6271d9 --- /dev/null +++ b/base-fiasco/src/sigma0/target.mk @@ -0,0 +1,5 @@ +TARGET = sigma0 +PKGS = sigma0/server +LIBS = l4v2_support + +include $(REP_DIR)/mk/l4_pkg.mk diff --git a/base-foc/Makefile b/base-foc/Makefile new file mode 100644 index 000000000..24bdd778c --- /dev/null +++ b/base-foc/Makefile @@ -0,0 +1,98 @@ +# +# \brief Checkout Fiasco.OC and addtional needed tools (sigma0, bootstrap) +# \author Stefan Kalkowski +# \date 2011-03-31 +# + +VERBOSE ?= @ +ECHO = @echo +SVN_URI = http://svn.tudos.org/repos/oc/tudos/trunk +SVN_REV = 38 +CONTRIB_DIR = contrib +PATCHES = $(shell find patches -name *.patch) + +SVN_TARGETS = tools/preprocess \ + kernel/fiasco \ + l4/conf \ + l4/doc \ + l4/mk \ + l4/tool \ + l4/pkg/bootstrap \ + l4/pkg/cxx \ + l4/pkg/drivers-frst \ + l4/pkg/l4sys \ + l4/pkg/l4util \ + l4/pkg/ldscripts \ + l4/pkg/libsigma0 \ + l4/pkg/sigma0 \ + l4/pkg/uclibc-headers \ + l4/pkg/uclibc-minimal \ + l4/pkg/uclibc \ + l4/pkg/libvcpu + +# +# Print help information by default +# +help:: + +# realpath is there to follow symlink; if contrib dir does not exists yet, +# create new directory +REAL_CONTRIB_DIR := $(realpath $(CONTRIB_DIR)) +ifeq ($(REAL_CONTRIB_DIR),) +REAL_CONTRIB_DIR := $(CONTRIB_DIR) +endif + +prepare: $(REAL_CONTRIB_DIR)/.svn update_contrib_subdirs apply_patches + +help:: + $(ECHO) + $(ECHO) "Check out upstream source code of Fiasco.OC" + $(ECHO) + $(ECHO) "The source code will be located at the '$(CONTRIB_DIR)/' directory." + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - checkout upstream source codes" + $(ECHO) "clean - remove upstream source codes" + $(ECHO) + +$(CONTRIB_DIR): + $(VERBOSE)mkdir -p $@ + +# use '.svn' subdirectory as rule to enable the use of a symbolic link as +# contrib directory +$(REAL_CONTRIB_DIR)/.svn: $(CONTRIB_DIR) + $(VERBOSE)svn co -r $(SVN_REV) --depth immediates $(SVN_URI) $(dir $@) + $(VERBOSE)svn co -r $(SVN_REV) --depth files $(SVN_URI)/l4 $(dir $@)/l4 + $(VERBOSE)svn co -r $(SVN_REV) --depth files $(SVN_URI)/l4/pkg $(dir $@)/l4/pkg + +# used phony to always update the SVN on 'make prepare' +# (before updating, we need to revert our custom patches) +update_contrib_subdirs: $(addprefix $(REAL_CONTRIB_DIR)/,$(SVN_TARGETS)) + $(ECHO) "updating . to revision $(SVN_REV)" + $(VERBOSE)svn revert $(REAL_CONTRIB_DIR) + $(VERBOSE)svn up -r $(SVN_REV) -N $(REAL_CONTRIB_DIR)/ + $(ECHO) "updating l4 to revision $(SVN_REV)" + $(VERBOSE)svn revert $(REAL_CONTRIB_DIR)/l4 + $(VERBOSE)svn up -r $(SVN_REV) -N $(REAL_CONTRIB_DIR)/l4 + $(ECHO) "updating l4/pkg to revision $(SVN_REV)" + $(VERBOSE)svn revert $(REAL_CONTRIB_DIR)/l4/pkg + $(VERBOSE)svn up -r $(SVN_REV) -N $(REAL_CONTRIB_DIR)/l4/pkg + $(VERBOSE)for i in $(SVN_TARGETS); do \ + echo "updating $$i to revision $(SVN_REV)"; \ + svn revert -R $(REAL_CONTRIB_DIR)/$$i; \ + svn up -r $(SVN_REV) $(REAL_CONTRIB_DIR)/$$i; done + +# for resolving the dependencies of 'update_contrib_subdirs' +$(REAL_CONTRIB_DIR)/%: + $(VERBOSE)svn up -r $(SVN_REV) $(SVN_URI)/$* $@ + +apply_patches: + $(ECHO) "applying patches to '$(REAL_CONTRIB_DIR)/'" + $(VERBOSE)for i in $(PATCHES); do \ + patch -N -d $(REAL_CONTRIB_DIR) -p0 < $$i; done + +# if $(CONTRIB_DIR) is a symlink, leave $(REAL_CONTRIB_DIR) alone +clean:: + $(VERBOSE)rm -rf $(CONTRIB_DIR) + +.NOTPARALLEL: diff --git a/base-foc/README b/base-foc/README new file mode 100644 index 000000000..53d39fd57 --- /dev/null +++ b/base-foc/README @@ -0,0 +1,9 @@ +This repository contains the port of Genode to the Fiasco.OC microkernel. +For further information, please refer to the following documents: + +:[http://genode.org/community/wiki/GenodeOnFiascoOC - Genode on Fiasco.OC Wiki page]: + This Wiki page contains the information on how to build and use + Genode with Fiasco.OC. + +:[http://os.inf.tu-dresden.de/fiasco]: + Official website for the Fiasco.OC microkernel. diff --git a/base-foc/config/pbxa9.kernel b/base-foc/config/pbxa9.kernel new file mode 100644 index 000000000..7568ff534 --- /dev/null +++ b/base-foc/config/pbxa9.kernel @@ -0,0 +1,83 @@ +# +# Automatically generated make config: don't edit +# Fiasco configuration +# + +# +# Target configuration +# +# CONFIG_IA32 is not set +# CONFIG_AMD64 is not set +CONFIG_ARM=y +CONFIG_PF_REALVIEW=y +# CONFIG_PF_IMX is not set +# CONFIG_PF_S3C2410 is not set +# CONFIG_PF_TEGRA2 is not set +# CONFIG_PF_OMAP is not set +# CONFIG_PF_SA1100 is not set +# CONFIG_PF_XSCALE is not set +# CONFIG_PF_KIRKWOOD is not set +# CONFIG_PF_INTEGRATOR is not set +CONFIG_BSP_NAME="realview" +# CONFIG_PF_REALVIEW_EB is not set +# CONFIG_PF_REALVIEW_PB11MP is not set +CONFIG_PF_REALVIEW_PBX=y +# CONFIG_PF_REALVIEW_VEXPRESS is not set +CONFIG_PF_REALVIEW_RAM_PHYS_BASE_0x0=y +# CONFIG_PF_REALVIEW_RAM_PHYS_BASE_0x2 is not set +# CONFIG_PF_REALVIEW_RAM_PHYS_BASE_0x7 is not set +CONFIG_PF_REALVIEW_RAM_PHYS_BASE=0x0 +CONFIG_ABI_VF=y +CONFIG_PF_ARM_MP_CAPABLE=y +CONFIG_CAN_ARM_CPU_CORTEX_A9=y +CONFIG_CAN_ARM_CACHE_L2CXX0=y +CONFIG_ARM_CORTEX_A9=y +# CONFIG_ARM_ALIGNMENT_CHECK is not set +# CONFIG_ARM_CA9_ENABLE_SWP is not set +CONFIG_ARM_CACHE_L2CXX0=y +CONFIG_FPU=y + +# +# Kernel options +# +CONFIG_CONTEXT_4K=y +# CONFIG_FINE_GRAINED_CPUTIME is not set +CONFIG_SCHED_FIXED_PRIO=y + +# +# Debugging +# +# CONFIG_INLINE is not set +# CONFIG_NDEBUG is not set +CONFIG_NO_FRAME_PTR=y +# CONFIG_STACK_DEPTH is not set +# CONFIG_LIST_ALLOC_SANITY is not set +CONFIG_SERIAL=y +CONFIG_JDB=y +CONFIG_JDB_LOGGING=y +CONFIG_JDB_DISASM=y +# CONFIG_JDB_GZIP is not set +# CONFIG_VMEM_ALLOC_TEST is not set +# CONFIG_DEBUG_KERNEL_PAGE_FAULTS is not set +# CONFIG_WARN_NONE is not set +CONFIG_WARN_WARNING=y +# CONFIG_WARN_ANY is not set + +# +# Compiling +# +CONFIG_CC="gcc" +CONFIG_CXX="g++" +CONFIG_HOST_CC="gcc" +CONFIG_HOST_CXX="g++" +# CONFIG_VERBOSE is not set +# CONFIG_MAINTAINER_MODE is not set +CONFIG_LABEL="" +# CONFIG_EXPERIMENTAL is not set +CONFIG_PERF_CNT=y +CONFIG_BIT32=y +CONFIG_ARM_V7=y +CONFIG_ARM_V6PLUS=y +CONFIG_WARN_LEVEL=1 +CONFIG_XARCH="arm" +CONFIG_ABI="vf" diff --git a/base-foc/config/rva9.user b/base-foc/config/rva9.user new file mode 100644 index 000000000..73704f9b2 --- /dev/null +++ b/base-foc/config/rva9.user @@ -0,0 +1,64 @@ +# +# Automatically generated make config: don't edit +# L4Re Configuration +# Thu Jul 14 16:26:37 2011 +# +# CONFIG_BUILD_ARCH_x86 is not set +# CONFIG_BUILD_ARCH_amd64 is not set +CONFIG_BUILD_ARCH_arm=y +# CONFIG_BUILD_ARCH_ppc32 is not set +# CONFIG_BUILD_ARCH_sparc is not set +CONFIG_BUILD_ARCH="arm" +CONFIG_BUILD_ABI_l4f=y +CONFIG_BUILD_ABI="l4f" +CONFIG_CPU="armv7a" +# CONFIG_CPU_ARM_ARMV4 is not set +# CONFIG_CPU_ARM_ARMV4T is not set +# CONFIG_CPU_ARM_ARMV5 is not set +# CONFIG_CPU_ARM_ARMV5T is not set +# CONFIG_CPU_ARM_ARMV5TE is not set +# CONFIG_CPU_ARM_ARMV6 is not set +# CONFIG_CPU_ARM_ARMV6T2 is not set +# CONFIG_CPU_ARM_ARMV6ZK is not set +CONFIG_CPU_ARM_ARMV7A=y +# CONFIG_CPU_ARM_ARMV7R is not set +CONFIG_CPU_ARMV6KPLUS=y +CONFIG_CPU_ARMV6PLUS=y + +# +# Platform +# +# CONFIG_PLATFORM_ARM_integrator is not set +CONFIG_PLATFORM_ARM_rv=y +# CONFIG_PLATFORM_ARM_imx21 is not set +# CONFIG_PLATFORM_ARM_imx51 is not set +# CONFIG_PLATFORM_ARM_omap3evm is not set +# CONFIG_PLATFORM_ARM_beagleboard is not set +# CONFIG_PLATFORM_ARM_pandaboard is not set +# CONFIG_PLATFORM_ARM_tegra2 is not set +# CONFIG_PLATFORM_ARM_custom is not set +CONFIG_ARM_PLATFORM_TYPE="rv" +CONFIG_RAM_BASE=0x0 +CONFIG_RAM_SIZE_MB=256 +# CONFIG_USE_DROPS_STDDIR is not set +# CONFIG_USE_DICE is not set +CONFIG_DROPS_STDDIR="/path/to/l4re" +CONFIG_DROPS_INSTDIR="/path/to/l4re" +CONFIG_BID_COLORED_PHASES=y + +# +# Building +# +CONFIG_YACC="yacc" +CONFIG_LEX="flex" +CONFIG_CTAGS="ctags" +CONFIG_ETAGS="etags" +CONFIG_HAVE_LDSO=y +CONFIG_INT_CPP_NAME_SWITCH=y +CONFIG_INT_LD_NAME_SWITCH=y +# CONFIG_BID_STRIP_PROGS is not set +# CONFIG_BID_GCC_OMIT_FP is not set +# CONFIG_BID_GENERATE_MAPFILE is not set +# CONFIG_BID_BUILD_DOC is not set +# CONFIG_RELEASE_MODE is not set +CONFIG_LABEL="" diff --git a/base-foc/config/vea9x4.kernel b/base-foc/config/vea9x4.kernel new file mode 100644 index 000000000..630a9ab8b --- /dev/null +++ b/base-foc/config/vea9x4.kernel @@ -0,0 +1,85 @@ +# +# Automatically generated make config: don't edit +# Fiasco configuration +# + +# +# Target configuration +# +# CONFIG_IA32 is not set +# CONFIG_AMD64 is not set +CONFIG_ARM=y +CONFIG_PF_REALVIEW=y +# CONFIG_PF_IMX is not set +# CONFIG_PF_S3C2410 is not set +# CONFIG_PF_TEGRA2 is not set +# CONFIG_PF_OMAP is not set +# CONFIG_PF_SA1100 is not set +# CONFIG_PF_XSCALE is not set +# CONFIG_PF_KIRKWOOD is not set +# CONFIG_PF_INTEGRATOR is not set +CONFIG_BSP_NAME="realview" +# CONFIG_PF_REALVIEW_EB is not set +# CONFIG_PF_REALVIEW_PB11MP is not set +# CONFIG_PF_REALVIEW_PBX is not set +CONFIG_PF_REALVIEW_VEXPRESS=y +CONFIG_PF_REALVIEW_RAM_PHYS_BASE_0x0=y +# CONFIG_PF_REALVIEW_RAM_PHYS_BASE_0x6 is not set +CONFIG_PF_REALVIEW_RAM_PHYS_BASE=0x0 +CONFIG_ABI_VF=y +CONFIG_PF_ARM_MP_CAPABLE=y +CONFIG_CAN_ARM_CPU_CORTEX_A9=y +CONFIG_ARM_CORTEX_A9=y +# CONFIG_ARM_ALIGNMENT_CHECK is not set +# CONFIG_ARM_TZ is not set +# CONFIG_ARM_CA9_ENABLE_SWP is not set +CONFIG_FPU=y + +# +# Kernel options +# +# CONFIG_MP is not set +CONFIG_CONTEXT_4K=y +# CONFIG_FINE_GRAINED_CPUTIME is not set +CONFIG_SCHED_FIXED_PRIO=y +# CONFIG_SCHED_WFQ is not set +# CONFIG_SCHED_FP_WFQ is not set +# CONFIG_DISABLE_VIRT_OBJ_SPACE is not set + +# +# Debugging +# +# CONFIG_INLINE is not set +# CONFIG_NDEBUG is not set +CONFIG_NO_FRAME_PTR=y +# CONFIG_STACK_DEPTH is not set +# CONFIG_LIST_ALLOC_SANITY is not set +CONFIG_SERIAL=y +CONFIG_JDB=y +CONFIG_JDB_LOGGING=y +CONFIG_JDB_DISASM=y +# CONFIG_JDB_GZIP is not set +# CONFIG_VMEM_ALLOC_TEST is not set +# CONFIG_DEBUG_KERNEL_PAGE_FAULTS is not set +# CONFIG_WARN_NONE is not set +CONFIG_WARN_WARNING=y +# CONFIG_WARN_ANY is not set + +# +# Compiling +# +CONFIG_CC="gcc" +CONFIG_CXX="g++" +CONFIG_HOST_CC="gcc" +CONFIG_HOST_CXX="g++" +# CONFIG_VERBOSE is not set +# CONFIG_MAINTAINER_MODE is not set +CONFIG_LABEL="" +CONFIG_EXPERIMENTAL=y +CONFIG_PERF_CNT=y +CONFIG_BIT32=y +CONFIG_ARM_V7=y +CONFIG_ARM_V6PLUS=y +CONFIG_WARN_LEVEL=1 +CONFIG_XARCH="arm" +CONFIG_ABI="vf" diff --git a/base-foc/config/x86_32.kernel b/base-foc/config/x86_32.kernel new file mode 100644 index 000000000..36f9c530e --- /dev/null +++ b/base-foc/config/x86_32.kernel @@ -0,0 +1,87 @@ +# +# Automatically generated make config: don't edit +# Fiasco configuration +# Wed Sep 14 14:33:24 2011 +# + +# +# Target configuration +# +CONFIG_IA32=y +# CONFIG_AMD64 is not set +# CONFIG_ARM is not set +CONFIG_PF_PC=y +# CONFIG_PF_UX is not set +CONFIG_ABI_VF=y +# CONFIG_IA32_486 is not set +CONFIG_IA32_586=y +# CONFIG_IA32_686 is not set +# CONFIG_IA32_P2 is not set +# CONFIG_IA32_P3 is not set +# CONFIG_IA32_P4 is not set +# CONFIG_IA32_PM is not set +# CONFIG_IA32_CORE2 is not set +# CONFIG_IA32_ATOM is not set +# CONFIG_IA32_K6 is not set +# CONFIG_IA32_K7 is not set +# CONFIG_IA32_K8 is not set +# CONFIG_IA32_K10 is not set +# CONFIG_CPU_VIRT is not set +CONFIG_SCHED_PIT=y +# CONFIG_SCHED_RTC is not set +# CONFIG_SCHED_APIC is not set +# CONFIG_WORKAROUND_AMD_FPU_LEAK is not set +CONFIG_REGPARM3=y + +# +# Kernel options +# +CONFIG_CONTEXT_4K=y +CONFIG_IO_PROT=y +# CONFIG_IO_PROT_IOPL_3 is not set +# CONFIG_SYNC_TSC is not set +# CONFIG_FINE_GRAINED_CPUTIME is not set +CONFIG_SCHED_FIXED_PRIO=y + +# +# Debugging +# +CONFIG_INLINE=y +# CONFIG_NDEBUG is not set +# CONFIG_NO_FRAME_PTR is not set +# CONFIG_STACK_DEPTH is not set +# CONFIG_ALLOW_RO_TEXT is not set +# CONFIG_LIST_ALLOC_SANITY is not set +# CONFIG_BEFORE_IRET_SANITY is not set +# CONFIG_IRQ_SPINNER is not set +CONFIG_WATCHDOG=y +CONFIG_SERIAL=y +CONFIG_JDB=y +# CONFIG_JDB_LOGGING is not set +CONFIG_JDB_DISASM=y +CONFIG_JDB_GZIP=y +# CONFIG_JDB_ACCOUNTING is not set +# CONFIG_JDB_MISC is not set +CONFIG_POWERSAVE_GETCHAR=y +CONFIG_USER_SINGLE_STEP=y +# CONFIG_WARN_NONE is not set +CONFIG_WARN_WARNING=y +# CONFIG_WARN_ANY is not set + +# +# Compiling +# +CONFIG_CC="gcc" +CONFIG_CXX="g++" +CONFIG_HOST_CC="gcc" +CONFIG_HOST_CXX="g++" +# CONFIG_VERBOSE is not set +# CONFIG_MAINTAINER_MODE is not set +CONFIG_LABEL="" +# CONFIG_EXPERIMENTAL is not set +CONFIG_PERF_CNT=y +CONFIG_BIT32=y +CONFIG_WARN_LEVEL=1 +CONFIG_XARCH="ia32" +CONFIG_IA32_TARGET="Intel Pentium" +CONFIG_ABI="vf" diff --git a/base-foc/config/x86_64.kernel b/base-foc/config/x86_64.kernel new file mode 100644 index 000000000..76a2390fc --- /dev/null +++ b/base-foc/config/x86_64.kernel @@ -0,0 +1,73 @@ +# +# Automatically generated make config: don't edit +# Fiasco configuration +# + +# +# Target configuration +# +# CONFIG_IA32 is not set +CONFIG_AMD64=y +# CONFIG_ARM is not set +CONFIG_PF_PC=y +CONFIG_ABI_VF=y +CONFIG_AMD64_K8=y +# CONFIG_AMD64_CORE2 is not set +# CONFIG_AMD64_ATOM is not set +# CONFIG_AMD64_K10 is not set +# CONFIG_CPU_VIRT is not set +# CONFIG_SCHED_PIT is not set +# CONFIG_SCHED_RTC is not set +CONFIG_SCHED_APIC=y +# CONFIG_SCHED_HPET is not set +# CONFIG_WORKAROUND_AMD_FPU_LEAK is not set + +# +# Kernel options +# +CONFIG_CONTEXT_4K=y +CONFIG_IO_PROT=y +# CONFIG_IO_PROT_IOPL_3 is not set +# CONFIG_FINE_GRAINED_CPUTIME is not set +CONFIG_SCHED_FIXED_PRIO=y + +# +# Debugging +# +CONFIG_INLINE=y +# CONFIG_NDEBUG is not set +CONFIG_NO_FRAME_PTR=y +CONFIG_STACK_DEPTH=y +# CONFIG_ALLOW_RO_TEXT is not set +# CONFIG_LIST_ALLOC_SANITY is not set +# CONFIG_BEFORE_IRET_SANITY is not set +# CONFIG_IRQ_SPINNER is not set +CONFIG_WATCHDOG=y +CONFIG_SERIAL=y +CONFIG_JDB=y +CONFIG_JDB_LOGGING=y +CONFIG_JDB_DISASM=y +CONFIG_JDB_GZIP=y +CONFIG_JDB_MISC=y +CONFIG_POWERSAVE_GETCHAR=y +# CONFIG_WARN_NONE is not set +CONFIG_WARN_WARNING=y +# CONFIG_WARN_ANY is not set + +# +# Compiling +# +CONFIG_CC="gcc" +CONFIG_CXX="g++" +CONFIG_HOST_CC="gcc" +CONFIG_HOST_CXX="g++" +# CONFIG_VERBOSE is not set +# CONFIG_MAINTAINER_MODE is not set +CONFIG_LABEL="" +# CONFIG_EXPERIMENTAL is not set +CONFIG_PERF_CNT=y +CONFIG_BIT64=y +CONFIG_WARN_LEVEL=1 +CONFIG_XARCH="amd64" +CONFIG_IA32_TARGET="AMD Opteron" +CONFIG_ABI="vf" diff --git a/base-foc/doc/foc.txt b/base-foc/doc/foc.txt new file mode 100644 index 000000000..3771f3cef --- /dev/null +++ b/base-foc/doc/foc.txt @@ -0,0 +1,142 @@ + + =================================== + Genode on the Fiasco.OC microkernel + =================================== + + + Stefan Kalkowski + + +Fiasco.OC is a microkernel developed by the OS group of the TU-Dresden. It's +an object-oriented capability-based system for x86, ARM and PowerPC platforms. + +This document provides brief instructions about downloading, building and +booting the Fiasco.OC version of Genode. + + +Prerequisites +############# + +You need certain tools to use the Fiasco.OC build system. On Debian/Ubuntu +systems you have to install the following packages: + +! apt-get install make gawk pkg-config subversion patch + +Moreover, you need to download and install the tool-chain used by Genode. Have +a look at this page: + +:[http://genode.org/download/tool-chain]: + Genode tool-chain + +If you want to use the so called run-scripts in Genode, a mechanism that +automates building, integration and testing of components, you have to install +the following, additional package: + +! apt-get install expect + + +Building the Fiasco.OC version of Genode +######################################## + +The current version of Genode is available at the public subversion repository: + +:http://genode.org/download/subversion-repository: + Information about accessing the Genode public subversion repository + +After you've fetched the Genode source tree from the subversion repository, or +downloaded the latest release tar archive, you need the Fiasco.OC source code, +its kernel-bindings, additional bootstrap tools etc. To simplify that step, +you can use a Makefile in the 'base-foc' directory of the Genode source tree, +just do: + +! cd base-foc +! make prepare + +This will install all necessary third-party source code in the 'contrib' folder. + +Now, go to a directory where you want the Genode/Fiasco.OC build directory to +remain. Use the helper script in the 'tool' directory of the Genode +source tree to create the initial build environment. You need to state the +build directory you want to create, and the hardware architecture to run +Fiasco.OC/Genode on. Choose 'foc_x86_32', 'foc_x86_64', or 'foc_pbxa9' +depending on whether you want to build for the 32-bit or 64-bit X86 +architecture, or for ARMs Cortex-A9. + +! /tool/create_builddir foc_x86_32 \ +! BUILD_DIR= + +Now, go to the newly created build directory and type make: + +! cd +! make + +This will build the Fiasco.OC kernel, its bootstrap code, and every Genode component, +that runs on top of Fiasco.OC. + +If you just want to give Genode/Fiasco.OC a try, you can call e.g.: the demo run-script +instead of building everything: + +! cd +! make run/demo + + +Running L4Linux on top of Genode +################################ + +To get the L4Linux running on top of Genode, you have to change to the +'ports-foc' repository within your Genode source tree and do a 'make prepare': + +! cd ports-foc +! make prepare + +This will fetch the currently supported version from the L4Linux subversion +repository, and apply a patch to it, that is needed to execute it on top of +Genode. + +Before compiling L4Linux for Genode/Fiasco.OC you have to integrate the 'ports-foc' +repository into your build environment. Therefore edit the 'etc/build.conf' file +in your build directory, and uncomment the following line: + +! REPOSITORIES += $(GENODE_DIR)/ports-foc + +After that you can build and run L4Linux by issuing: + +! make run/l4linux + +in your build directory. This run-script boots a single L4Linux instance into +a minimal console environment. The script depends on an 'initrd.gz' archive, +which has to reside in 'bin' in your build directory. You can find an example +initramfs here: + +:[http://genode.org/files/release-11.05/l4lx/x86/initrd.gz]: + Initramfs archive for X86 + +:[http://genode.org/files/release-11.05/l4lx/arm/initrd.gz]: + Initramfs archive for ARM + + +Integration of Fiasco.OC with Genode +#################################### + +If you don't want the Genode build system to build the Fiasco.OC kernel for +you, but you want to provide your own version, you have to state in the +'etc/foc.conf' file within your build directory, where to find it: + +! L4_BUILD_DIR = +! KERNEL = + +The first variable states where to find the kernel bindings (the L4RE build +directory), the second one states where your kernel binary can be found. +After adding these variable to the file, you have to do a full cleanup +in your build directory to ensure, that the right bindings are used: + +! make cleanall + +From now on, run-scripts will use your provided kernel. + + +Further Information +################### + +:[http://os.inf.tu-dresden.de/fiasco]: + Official website for the Fiasco.OC microkernel. diff --git a/base-foc/etc/foc.conf b/base-foc/etc/foc.conf new file mode 100644 index 000000000..46c90d33e --- /dev/null +++ b/base-foc/etc/foc.conf @@ -0,0 +1,20 @@ +# +# Fiasco.OC-specific default configuration options +# + +# +# Directory, where to search for L4 headers +# +# When using this file as template for a customized +# '/etc/kernel.conf'. +# +#L4_BUILD_DIR = $(HOME)/src/l4build.x86 + +# +# Path to the Fiasco.OC kernel +# +# When using this file as template for a customized +# '/etc/kernel.conf'. +# +#KERNEL = $(HOME)/src/fiasco-build.x86/fiasco + diff --git a/base-foc/etc/specs.conf b/base-foc/etc/specs.conf new file mode 100644 index 000000000..81779dc15 --- /dev/null +++ b/base-foc/etc/specs.conf @@ -0,0 +1,8 @@ +# +# Description of build platform +# + +# +# By default, build Fiasco.OC binaries for ia32. +# +SPECS ?= genode foc_x86_32 diff --git a/base-foc/include/arm/cpu/atomic.h b/base-foc/include/arm/cpu/atomic.h new file mode 100644 index 000000000..c87c79575 --- /dev/null +++ b/base-foc/include/arm/cpu/atomic.h @@ -0,0 +1,54 @@ +/* + * \brief Atomic operations for ARM + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2007-04-28 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ARM__CPU__ATOMIC_H_ +#define _INCLUDE__ARM__CPU__ATOMIC_H_ + +namespace Genode { + + /** + * Atomic compare and exchange + * + * This function compares the value at dest with cmp_val. + * If both values are equal, dest is set to new_val. If + * both values are different, the value at dest remains + * unchanged. + * + * \return 1 if the value was successfully changed to new_val, + * 0 if cmp_val and the value at dest differ. + */ + inline int cmpxchg(volatile int *dest, int cmp_val, int new_val) + { + unsigned long equal, not_exclusive; + + __asm__ __volatile__( + "@ cmpxchg\n" + " 1: \n" + " ldrex %0, [%2] \n" + " cmp %0, %3 \n" + " bne 2f \n" + " strexeq %0, %4, [%2]\n" + " teqeq %0, #0 \n" + " bne 1b \n" + " moveq %1, #1 \n" + " 2: \n" + " movne %1, #0 \n" + : "=&r" (not_exclusive), "=&r" (equal) + : "r" (dest), "r" (cmp_val), "r" (new_val) + : "cc"); + return equal && !not_exclusive; + } +} + +#endif /* _INCLUDE__ARM__CPU__ATOMIC_H_ */ diff --git a/base-foc/include/base/cap_sel_alloc.h b/base-foc/include/base/cap_sel_alloc.h new file mode 100644 index 000000000..a39ea2157 --- /dev/null +++ b/base-foc/include/base/cap_sel_alloc.h @@ -0,0 +1,68 @@ +/* + * \brief Interface for process-local capability-selector allocation + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2010-01-19 + * + * This interface is Fiasco-specific and not part of the Genode API. It should + * only be used internally by the framework or by Fiasco-specific code. The + * implementation of the interface is part of the environment library. + * + * This implementation is borrowed by the nova-platform equivalent. + * (TODO: merge it) + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CAP_SEL_ALLOC_H_ +#define _INCLUDE__BASE__CAP_SEL_ALLOC_H_ + +#include + +namespace Genode +{ + class Capability_allocator + { + private: + + addr_t _cap_idx; + + /** + * Constructor + */ + Capability_allocator(); + + public: + + /** + * Return singleton instance of 'Capability_allocator' + */ + static Capability_allocator* allocator(); + + /** + * Allocate range of capability selectors + * + * \param num_caps_log2 number of capability selectors. By default, + * the function returns a single capability selector. + * \return first capability selector of allocated range, + * or 0 if allocation failed + */ + addr_t alloc(size_t num_caps = 1); + + /** + * Release range of capability selectors + * + * \param cap first capability selector of range + * \param num_caps_log2 number of capability selectors to free. + */ + void free(addr_t cap, size_t num_caps = 1); + }; +} + +#endif /* _INCLUDE__BASE__CAP_SEL_ALLOC_H_ */ + diff --git a/base-foc/include/base/ipc.h b/base-foc/include/base/ipc.h new file mode 100644 index 000000000..938eb00c5 --- /dev/null +++ b/base-foc/include/base/ipc.h @@ -0,0 +1,56 @@ +/* + * \brief Fiasco.OC-specific supplements to the IPC framework + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2010-01-27 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_H_ +#define _INCLUDE__BASE__IPC_H_ + +#include + +namespace Fiasco { +#include +#include +#include +} + +inline void Genode::Ipc_ostream::_marshal_capability(Genode::Native_capability const &cap) +{ + long unique_id = cap.local_name(); + _write_to_buf(unique_id); + if (unique_id) + _snd_msg->snd_append_cap_sel(cap.dst()); +} + + +inline void Genode::Ipc_istream::_unmarshal_capability(Genode::Native_capability &cap) +{ + using namespace Fiasco; + + /* extract Genode internal capability label from message buffer */ + long unique_id = 0; + _read_from_buf(unique_id); + + if (!unique_id) { + cap = Native_capability(); + return; + } + + /* allocate new cap slot and grant cap to it out of receive window */ + Genode::addr_t cap_sel = Capability_allocator::allocator()->alloc(); + l4_task_map(L4_BASE_TASK_CAP, L4_BASE_TASK_CAP, + l4_obj_fpage(_rcv_msg->rcv_cap_sel(), 0, L4_FPAGE_RWX), + cap_sel | L4_ITEM_MAP | L4_MAP_ITEM_GRANT); + cap = Native_capability(cap_sel, unique_id); +} + +#endif /* _INCLUDE__BASE__IPC_H_ */ diff --git a/base-foc/include/base/ipc_msgbuf.h b/base-foc/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..4a2d00055 --- /dev/null +++ b/base-foc/include/base/ipc_msgbuf.h @@ -0,0 +1,152 @@ +/* + * \brief IPC message buffer layout for Fiasco.OC + * \author Stefan Kalkowski + * \date 2010-11-30 + * + * On Fiasco.OC, IPC is used to transmit plain data and capabilities. + * Therefore the message buffer contains both categories of payload. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +/* Genode includes */ +#include + +/* Fiasco.OC includes */ +namespace Fiasco { +#include +#include +} + +namespace Genode { + + class Msgbuf_base + { + public: + + enum { MAX_CAP_ARGS_LOG2 = 2, MAX_CAP_ARGS = 1 << MAX_CAP_ARGS_LOG2 }; + + protected: + + size_t _size; + + /** + * Number of capability selectors to send. + */ + size_t _snd_cap_sel_cnt; + + /** + * Capability selectors to delegate. + */ + addr_t _snd_cap_sel[MAX_CAP_ARGS]; + + /** + * Base of capability receive window. + */ + addr_t _rcv_cap_sel_base; + + /** + * Read counter for unmarshalling portal capability selectors + */ + addr_t _rcv_cap_sel_cnt; + + char _msg_start[]; /* symbol marks start of message */ + + public: + + /** + * Constructor + */ + Msgbuf_base() + : _rcv_cap_sel_base(Capability_allocator::allocator()->alloc(MAX_CAP_ARGS)) + { + rcv_reset(); + snd_reset(); + } + + /* + * Begin of actual message buffer + */ + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; }; + + /** + * Return address of message buffer + */ + inline void *addr() { return &_msg_start[0]; }; + + /** + * Reset portal capability selector payload + */ + inline void snd_reset() { _snd_cap_sel_cnt = 0; } + + /** + * Append capability selector to message buffer + */ + inline bool snd_append_cap_sel(addr_t cap_sel) + { + if (_snd_cap_sel_cnt >= MAX_CAP_ARGS) + return false; + + _snd_cap_sel[_snd_cap_sel_cnt++] = cap_sel; + return true; + } + + /** + * Return number of marshalled capability selectors + */ + inline size_t snd_cap_sel_cnt() { return _snd_cap_sel_cnt; } + + /** + * Return capability selector to send. + * + * \param i index (0 ... 'snd_cap_sel_cnt()' - 1) + * \return capability selector, or 0 if index is invalid + */ + addr_t snd_cap_sel(unsigned i) { + return i < _snd_cap_sel_cnt ? _snd_cap_sel[i] : 0; } + + /** + * Return address of capability receive window. + */ + addr_t rcv_cap_sel_base() { return _rcv_cap_sel_base; } + + /** + * Reset capability receive window + */ + void rcv_reset() { _rcv_cap_sel_cnt = 0; } + + /** + * Return next received capability selector. + * + * \return capability selector, or 0 if index is invalid + */ + addr_t rcv_cap_sel() { + return _rcv_cap_sel_base + _rcv_cap_sel_cnt++ * Fiasco::L4_CAP_SIZE; } + }; + + + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-foc/include/base/ipc_pager.h b/base-foc/include/base/ipc_pager.h new file mode 100644 index 000000000..a5084d85d --- /dev/null +++ b/base-foc/include/base/ipc_pager.h @@ -0,0 +1,200 @@ +/* + * \brief Fiasco.OC pager support + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-06-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +namespace Genode { + + class Mapping + { + private: + + addr_t _dst_addr; + addr_t _src_addr; + bool _write_combined; + unsigned _log2size; + bool _rw; + bool _grant; + + public: + + /** + * Constructor + */ + Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, unsigned l2size = L4_LOG2_PAGESIZE, + bool rw = true, bool grant = false) + : _dst_addr(dst_addr), _src_addr(src_addr), + _write_combined(write_combined), _log2size(l2size), + _rw(rw), _grant(grant) { } + + /** + * Construct invalid flexpage + */ + Mapping() : _dst_addr(0), _src_addr(0), _write_combined(false), + _log2size(0), _rw(false), _grant(false) { } + + Fiasco::l4_umword_t dst_addr() const { return _dst_addr; } + bool grant() const { return _grant; } + + Fiasco::l4_fpage_t fpage() const + { + // TODO: write combined + //if (write_combined) + // _fpage.fp.cache = Fiasco::L4_FPAGE_BUFFERABLE; + + unsigned char rights = _rw ? Fiasco::L4_FPAGE_RW : Fiasco::L4_FPAGE_RO; + return Fiasco::l4_fpage(_src_addr, _log2size, rights); + } + + /** + * Prepare map operation + * + * On Fiasco, we need to map a page locally to be able to map it to + * another address space. + */ + void prepare_map_operation() + { + size_t mapping_size = 1 << _log2size; + for (addr_t i = 0; i < mapping_size; i += L4_PAGESIZE) { + if (_rw) + touch_read_write((unsigned char volatile *)(_src_addr + i)); + else + touch_read((unsigned char const volatile *)(_src_addr + i)); + } + } + }; + + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + public: + + enum Msg_type { PAGEFAULT, WAKE_UP, PAUSE, EXCEPTION }; + + private: + + Native_thread _last; /* origin of last fault */ + addr_t _pf_addr; /* page-fault address */ + addr_t _pf_ip; /* ip of faulter */ + Mapping _reply_mapping; /* page-fault answer */ + unsigned long _badge; /* badge of faulting thread */ + Fiasco::l4_msgtag_t _tag; /* receive message tag */ + Fiasco::l4_exc_regs_t _regs; /* exception registers */ + Msg_type _type; + + void _parse_msg_type(void); + void _parse_exception(void); + void _parse_pagefault(void); + void _parse(unsigned long label); + + public: + + /** + * Constructor + */ + Ipc_pager(); + + /** + * Wait for a new page fault received as short message IPC + */ + void wait_for_fault(); + + /** + * Reply current page-fault and wait for a new one + * + * Send short flex page and wait for next short-message (register) + * IPC -- pagefault + */ + void reply_and_wait_for_fault(); + + /** + * Request instruction pointer of current page fault + */ + addr_t fault_ip() { return _pf_ip; } + + /** + * Request fault address of current page fault + */ + addr_t fault_addr() { return _pf_addr & ~3; } + + /** + * Set parameters for next reply + */ + void set_reply_mapping(Mapping m) { _reply_mapping = m; } + + /** + * Set destination for next reply + */ + void set_reply_dst(Native_capability pager_object) { + _last = pager_object.dst(); } + + /** + * Answer call without sending a flex-page mapping + * + * This function is used to acknowledge local calls from one of + * core's region-manager sessions. + */ + void acknowledge_wakeup(); + + /** + * Return thread ID of last faulter + */ + Native_thread last() const { return _last; } + + /** + * Return badge for faulting thread + */ + unsigned long badge() { return _badge; } + + bool is_write_fault() const { return (_pf_addr & 2); } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + return _type == Ipc_pager::EXCEPTION; + } + + /** + * Return the type of ipc we received at last. + */ + Msg_type msg_type() { return _type; }; + + /** + * Copy the exception registers from the last exception + * to the given thread_state object. + */ + void copy_regs(Thread_state *state); + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-foc/include/base/native_types.h b/base-foc/include/base/native_types.h new file mode 100644 index 000000000..fcfe1d33d --- /dev/null +++ b/base-foc/include/base/native_types.h @@ -0,0 +1,87 @@ +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +namespace Fiasco { +#include +#include +#include + + class Fiasco_capability + { + private: + + l4_cap_idx_t _cap_idx; + + public: + + enum Cap_selectors { + INVALID_CAP = L4_INVALID_CAP, + TASK_CAP = L4_BASE_TASK_CAP, + PARENT_CAP = 0x8UL << L4_CAP_SHIFT, + THREADS_BASE_CAP = 0x9UL << L4_CAP_SHIFT, + USER_BASE_CAP = 0x200UL << L4_CAP_SHIFT, + THREAD_GATE_CAP = 0, + THREAD_PAGER_CAP = 0x1UL << L4_CAP_SHIFT, + THREAD_IRQ_CAP = 0x2UL << L4_CAP_SHIFT, + THREAD_CAP_SLOT = THREAD_IRQ_CAP + L4_CAP_SIZE, + MAIN_THREAD_CAP = THREADS_BASE_CAP + THREAD_GATE_CAP + }; + + Fiasco_capability(l4_cap_idx_t cap = L4_INVALID_CAP) + : _cap_idx(cap) { } + + Fiasco_capability(void* cap) + : _cap_idx((l4_cap_idx_t)cap) { } + + bool valid() const { return !(_cap_idx & Fiasco::L4_INVALID_CAP_BIT) + && _cap_idx != 0; } + + operator l4_cap_idx_t () { return _cap_idx; } + }; + + enum Utcb_regs { + UTCB_TCR_BADGE = 1, + UTCB_TCR_THREAD_OBJ = 2 + }; +} + +namespace Genode { + + typedef volatile int Native_lock; + typedef Fiasco::Fiasco_capability Native_thread_id; + typedef Fiasco::Fiasco_capability Native_thread; + typedef Fiasco::Fiasco_capability Native_task; + typedef Fiasco::l4_utcb_t* Native_utcb; + + class Native_capability + { + private: + + Native_thread _cap_sel; + int _unique_id; + + public: + + /** + * Default constructor creates an invalid capability + */ + Native_capability() : _unique_id(0) { } + + /** + * Construct capability manually + */ + Native_capability(Native_thread cap_sel, int unique_id) + : _cap_sel(cap_sel), _unique_id(unique_id) { } + + int local_name() const { return _unique_id; } + Native_thread dst() const { return _cap_sel; } + Native_thread_id tid() const { return _cap_sel; } + + bool valid() const { return _cap_sel.valid() && _unique_id != 0; } + + }; + + typedef int Native_connection_state; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-foc/include/base/thread_state.h b/base-foc/include/base/thread_state.h new file mode 100644 index 000000000..b88ffd5c7 --- /dev/null +++ b/base-foc/include/base/thread_state.h @@ -0,0 +1,40 @@ +/* + * \brief Thread state + * \author Stefan Kalkowski + * \date 2010-01-20 + * + * This file contains the Fiasco.OC specific part of the thread state. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__THREAD_STATE_H_ +#define _INCLUDE__BASE__THREAD_STATE_H_ + +#include +#include +#include + +namespace Genode { + + struct Thread_state : public Cpu_state + { + Native_capability cap; /* capability selector with thread cap */ + unsigned exceptions; /* counts exceptions raised by the thread */ + bool paused; /* indicates whether thread is stopped */ + bool in_exception; /* true if thread is currently in exception */ + Lock lock; + + /** + * Constructor + */ + Thread_state() : cap(), exceptions(0), paused(false) { } + }; +} + +#endif /* _INCLUDE__BASE__THREAD_STATE_H_ */ diff --git a/base-foc/include/foc_cpu_session/client.h b/base-foc/include/foc_cpu_session/client.h new file mode 100644 index 000000000..92bb43149 --- /dev/null +++ b/base-foc/include/foc_cpu_session/client.h @@ -0,0 +1,79 @@ +/* + * \brief Client-side cpu session Fiasco.OC extension + * \author Stefan Kalkowski + * \date 2011-04-04 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__FOC_CPU_SESSION__CLIENT_H_ +#define _INCLUDE__FOC_CPU_SESSION__CLIENT_H_ + +#include +#include +#include + +namespace Genode { + + struct Foc_cpu_session_client : Rpc_client + { + explicit Foc_cpu_session_client(Cpu_session_capability session) + : Rpc_client(static_cap_cast(session)) { } + + Thread_capability create_thread(Name const &name) { + return call(name); } + + void kill_thread(Thread_capability thread) { + call(thread); } + + Thread_capability first() { + return call(); } + + Thread_capability next(Thread_capability curr) { + return call(curr); } + + int set_pager(Thread_capability thread, Pager_capability pager) { + return call(thread, pager); } + + int start(Thread_capability thread, addr_t ip, addr_t sp) { + return call(thread, ip, sp); } + + void pause(Thread_capability thread) { + call(thread); } + + void resume(Thread_capability thread) { + call(thread); } + + void cancel_blocking(Thread_capability thread) { + call(thread); } + + int name(Thread_capability thread, char *name_dst, size_t name_len) + { + PWRN("name called, this function is deprecated"); + return -1; + } + + int state(Thread_capability thread, Thread_state *dst_state) { + return call(thread, dst_state); } + + void exception_handler(Thread_capability thread, Signal_context_capability handler) { + call(thread, handler); } + + void enable_vcpu(Thread_capability cap, addr_t vcpu_state) { + call(cap, vcpu_state); } + + Native_capability native_cap(Thread_capability cap) { + return call(cap); } + + Native_capability alloc_irq() { + return call(); } + }; + +} + +#endif /* _INCLUDE__FOC_CPU_SESSION__CLIENT_H_ */ diff --git a/base-foc/include/foc_cpu_session/connection.h b/base-foc/include/foc_cpu_session/connection.h new file mode 100644 index 000000000..118db4ad7 --- /dev/null +++ b/base-foc/include/foc_cpu_session/connection.h @@ -0,0 +1,41 @@ +/* + * \brief Connection to Fiasco.OC specific cpu service + * \author Stefan Kalkowski + * \date 2011-04-04 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__FOC_CPU_SESSION__CONNECTION_H_ +#define _INCLUDE__FOC_CPU_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Foc_cpu_connection : Connection, Foc_cpu_session_client + { + /** + * Constructor + * + * \param label initial session label + * \param priority designated priority of all threads created + * with this CPU session + */ + Foc_cpu_connection(const char *label = "", + long priority = DEFAULT_PRIORITY) + : + Connection( + session("priority=0x%lx, ram_quota=32K, label=\"%s\"", + priority, label)), + Foc_cpu_session_client(cap()) { } + }; +} + +#endif /* _INCLUDE__FOC_CPU_SESSION__CONNECTION_H_ */ diff --git a/base-foc/include/foc_cpu_session/foc_cpu_session.h b/base-foc/include/foc_cpu_session/foc_cpu_session.h new file mode 100644 index 000000000..fd7b66b7b --- /dev/null +++ b/base-foc/include/foc_cpu_session/foc_cpu_session.h @@ -0,0 +1,46 @@ +/* + * \brief Cpu session interface extension for Fiasco.OC + * \author Stefan Kalkowski + * \date 2011-04-04 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__FOC_CPU_SESSION__FOC_CPU_SESSION_H_ +#define _INCLUDE__FOC_CPU_SESSION__FOC_CPU_SESSION_H_ + +#include +#include + +namespace Genode { + + struct Foc_cpu_session : Cpu_session + { + virtual ~Foc_cpu_session() { } + + virtual void enable_vcpu(Thread_capability cap, addr_t vcpu_state) = 0; + + virtual Native_capability native_cap(Thread_capability cap) = 0; + + virtual Native_capability alloc_irq() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_enable_vcpu, void, enable_vcpu, Thread_capability, addr_t); + GENODE_RPC(Rpc_native_cap, Native_capability, native_cap, Thread_capability); + GENODE_RPC(Rpc_alloc_irq, Native_capability, alloc_irq); + + GENODE_RPC_INTERFACE_INHERIT(Cpu_session, + Rpc_enable_vcpu, Rpc_native_cap, Rpc_alloc_irq); + }; +} + +#endif /* _INCLUDE__FOC_CPU_SESSION__FOC_CPU_SESSION_H_ */ diff --git a/base-foc/include/foc_pd_session/client.h b/base-foc/include/foc_pd_session/client.h new file mode 100644 index 000000000..fcc3390a2 --- /dev/null +++ b/base-foc/include/foc_pd_session/client.h @@ -0,0 +1,38 @@ +/* + * \brief Client-side Fiasco.OC specific PD session interface + * \author Stefan Kalkowski + * \date 2011-04-14 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__FOC_PD_SESSION__CLIENT_H_ +#define _INCLUDE__FOC_PD_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Foc_pd_session_client : Rpc_client + { + explicit Foc_pd_session_client(Capability session) + : Rpc_client(session) { } + + int bind_thread(Thread_capability thread) { + return call(thread); } + + int assign_parent(Parent_capability parent) { + return call(parent); } + + Native_capability task_cap() { return call(); } + }; + +} + +#endif /* _INCLUDE__FOC_PD_SESSION__CLIENT_H_ */ diff --git a/base-foc/include/foc_pd_session/connection.h b/base-foc/include/foc_pd_session/connection.h new file mode 100644 index 000000000..f7d2bc145 --- /dev/null +++ b/base-foc/include/foc_pd_session/connection.h @@ -0,0 +1,40 @@ +/* + * \brief Connection to Fiasco.OC specific PD service + * \author Stefan Kalkowski + * \date 2011-04-14 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__FOC_PD_SESSION__CONNECTION_H_ +#define _INCLUDE__FOC_PD_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Foc_pd_connection : Connection, Foc_pd_session_client + { + /** + * Constructor + * + * \param args additional session arguments + */ + Foc_pd_connection(const char *args = 0) + : + Connection( + session("ram_quota=4K%s%s", + args ? ", " : "", + args ? args : "")), + Foc_pd_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__FOC_PD_SESSION__CONNECTION_H_ */ diff --git a/base-foc/include/foc_pd_session/foc_pd_session.h b/base-foc/include/foc_pd_session/foc_pd_session.h new file mode 100644 index 000000000..56ce644f2 --- /dev/null +++ b/base-foc/include/foc_pd_session/foc_pd_session.h @@ -0,0 +1,40 @@ +/* + * \brief Fiasco.OC specific PD session extension + * \author Stefan Kalkowski + * \date 2011-04-14 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__FOC_PD_SESSION__FOC_PD_SESSION_H_ +#define _INCLUDE__FOC_PD_SESSION__FOC_PD_SESSION_H_ + +#include +#include +#include + +namespace Genode { + + struct Foc_pd_session : Pd_session + { + virtual ~Foc_pd_session() { } + + virtual Native_capability task_cap() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_task_cap, Native_capability, task_cap); + + GENODE_RPC_INTERFACE_INHERIT(Pd_session, Rpc_task_cap); + }; +} + +#endif /* _INCLUDE__FOC_PD_SESSION__FOC_PD_SESSION_H_ */ diff --git a/base-foc/include/signal_session/foc_source.h b/base-foc/include/signal_session/foc_source.h new file mode 100644 index 000000000..b27b4da75 --- /dev/null +++ b/base-foc/include/signal_session/foc_source.h @@ -0,0 +1,35 @@ +/* + * \brief Fiasco.OC-specific signal source RPC interface + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2011-04-12 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__FOC_SOURCE_H_ +#define _INCLUDE__SIGNAL_SESSION__FOC_SOURCE_H_ + +#include +#include + +namespace Genode { + + struct Foc_signal_source : Signal_source + { + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_request_semaphore, Native_capability, _request_semaphore); + + GENODE_RPC_INTERFACE_INHERIT(Signal_source, Rpc_request_semaphore); + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__FOC_SOURCE_H_ */ diff --git a/base-foc/include/signal_session/source_client.h b/base-foc/include/signal_session/source_client.h new file mode 100644 index 000000000..540e6431c --- /dev/null +++ b/base-foc/include/signal_session/source_client.h @@ -0,0 +1,91 @@ +/* + * \brief Fiasco.OC-specific signal-source client interface + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2010-02-03 + * + * On Fiasco.OC, the signal source server does not provide a blocking + * 'wait_for_signal' function because this kernel does no support + * out-of-order IPC replies. Instead, we use an IRQ kernel-object + * to let the client block until a signal is present at the + * server. The IRQ object gets initialized with the first + * call of 'wait_for_signal()'. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ +#define _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ + +#include +#include +#include + +namespace Fiasco { +#include +} + +namespace Genode { + + class Signal_source_client : public Rpc_client + { + private: + + /** + * Capability with 'dst' referring to a Fiasco.OC IRQ object + */ + Native_capability _sem; + + /** + * Request Fiasco.OC IRQ object from signal-source server + */ + void _init_sem() + { + using namespace Fiasco; + + /* request mapping of semaphore capability selector */ + _sem = call(); + + l4_msgtag_t tag = l4_irq_attach(_sem.dst(), 0, + Thread_base::myself()->tid()); + if (l4_error(tag)) + PERR("l4_irq_attach failed with %ld!", l4_error(tag)); + } + + public: + + /** + * Constructor + */ + Signal_source_client(Signal_source_capability cap) + : Rpc_client(static_cap_cast(cap)) + { _init_sem(); } + + + /***************************** + ** Signal source interface ** + *****************************/ + + Signal wait_for_signal() + { + using namespace Fiasco; + + /* block on semaphore, will be unblocked if signal is available */ + l4_irq_receive(_sem.dst(), L4_IPC_NEVER); + + /* + * Now that the server has unblocked the semaphore, we are sure + * that there is a signal pending. The following 'wait_for_signal' + * request will be immediately answered. + */ + return call(); + } + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ */ diff --git a/base-foc/include/signal_session/source_rpc_object.h b/base-foc/include/signal_session/source_rpc_object.h new file mode 100644 index 000000000..96d1cd776 --- /dev/null +++ b/base-foc/include/signal_session/source_rpc_object.h @@ -0,0 +1,39 @@ +/* + * \brief Signal-source server interface + * \author Norman Feske + * \date 2010-02-03 + * + * This file is only included by 'signal_session/server.h' and relies on the + * headers included there. No include guards are needed. It is a separate + * header file to make it easily replaceable by a platform-specific + * implementation. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SOURCE_SERVER_H_ +#define _INCLUDE__SIGNAL_SESSION__SOURCE_SERVER_H_ + +#include +#include + +namespace Genode { + + struct Signal_source_rpc_object : Rpc_object + { + protected: + + Native_capability _blocking_semaphore; + + public: + + Native_capability _request_semaphore() { return _blocking_semaphore; } + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SOURCE_SERVER_H_ */ diff --git a/base-foc/lib/mk/arm/ipc.mk b/base-foc/lib/mk/arm/ipc.mk new file mode 100644 index 000000000..33f3e6cfd --- /dev/null +++ b/base-foc/lib/mk/arm/ipc.mk @@ -0,0 +1,4 @@ +SRC_CC = arm/pager_exception.cc arm/pager.cc + +include $(REP_DIR)/lib/mk/ipc.inc + diff --git a/base-foc/lib/mk/arm/platform.inc b/base-foc/lib/mk/arm/platform.inc new file mode 100644 index 000000000..d0bb4139f --- /dev/null +++ b/base-foc/lib/mk/arm/platform.inc @@ -0,0 +1,8 @@ +# +# Create mirror for architecture-specific L4sys header files +# +L4_INC_TARGETS = arm/l4/sys \ + arm/l4f/l4/sys \ + arm/l4/vcpu + +include $(REP_DIR)/lib/mk/platform.inc diff --git a/base-foc/lib/mk/arm/startup.mk b/base-foc/lib/mk/arm/startup.mk new file mode 100644 index 000000000..14b52c669 --- /dev/null +++ b/base-foc/lib/mk/arm/startup.mk @@ -0,0 +1,8 @@ +SRC_S = crt0.s +SRC_CC = _main.cc +LIBS += cxx lock + +INC_DIR += $(REP_DIR)/src/platform $(BASE_DIR)/src/platform + +vpath crt0.s $(BASE_DIR)/src/platform/arm +vpath _main.cc $(BASE_DIR)/src/platform diff --git a/base-foc/lib/mk/arm/syscalls.mk b/base-foc/lib/mk/arm/syscalls.mk new file mode 100644 index 000000000..ff94d707d --- /dev/null +++ b/base-foc/lib/mk/arm/syscalls.mk @@ -0,0 +1,5 @@ +SRC_C += utcb.c +SRC_S += atomic_ops_s.S + +vpath atomic_ops_s.S $(L4_BUILD_DIR)/source/pkg/l4sys/lib/src/ARCH-arm +vpath utcb.c $(L4_BUILD_DIR)/source/pkg/l4sys/lib/src diff --git a/base-foc/lib/mk/cap_alloc.mk b/base-foc/lib/mk/cap_alloc.mk new file mode 100644 index 000000000..33f7d67d9 --- /dev/null +++ b/base-foc/lib/mk/cap_alloc.mk @@ -0,0 +1,3 @@ +SRC_CC = cap_sel_alloc.cc + +vpath cap_sel_alloc.cc $(REP_DIR)/src/base/env diff --git a/base-foc/lib/mk/core_printf.mk b/base-foc/lib/mk/core_printf.mk new file mode 100644 index 000000000..663cf64b9 --- /dev/null +++ b/base-foc/lib/mk/core_printf.mk @@ -0,0 +1,5 @@ +SRC_CC = core_printf.cc +LIBS = cxx console +INC_DIR += $(REP_DIR)/src/base/console + +vpath core_printf.cc $(BASE_DIR)/src/base/console diff --git a/base-foc/lib/mk/env.mk b/base-foc/lib/mk/env.mk new file mode 100644 index 000000000..fc7176970 --- /dev/null +++ b/base-foc/lib/mk/env.mk @@ -0,0 +1,6 @@ +SRC_CC = env.cc context_area.cc cap_sel_alloc.cc +LIBS = ipc heap log_console lock + +vpath env.cc $(BASE_DIR)/src/base/env +vpath context_area.cc $(BASE_DIR)/src/base/env +vpath cap_sel_alloc.cc $(REP_DIR)/src/base/env diff --git a/base-foc/lib/mk/ipc.inc b/base-foc/lib/mk/ipc.inc new file mode 100644 index 000000000..9a9abfc51 --- /dev/null +++ b/base-foc/lib/mk/ipc.inc @@ -0,0 +1,5 @@ +LIBS = syscalls +SRC_CC += ipc.cc pager.cc +INC_DIR += $(REP_DIR)/src/base/lock + +vpath %.cc $(REP_DIR)/src/base/ipc diff --git a/base-foc/lib/mk/l4re_support.mk b/base-foc/lib/mk/l4re_support.mk new file mode 100644 index 000000000..805809386 --- /dev/null +++ b/base-foc/lib/mk/l4re_support.mk @@ -0,0 +1,14 @@ +# +# Build L4re base libraries, needed by sigma0 and bootstrap + + +# ignore stage one, visit the L4 build system at second build stage +ifeq ($(called_from_lib_mk),yes) + +# packages in 'l4/pkg/' +PKGS = uclibc-headers uclibc-minimal l4util cxx + +include $(REP_DIR)/mk/l4_pkg.mk +all: $(PKG_TAGS) + +endif diff --git a/base-foc/lib/mk/lock.mk b/base-foc/lib/mk/lock.mk new file mode 100644 index 000000000..83ab108db --- /dev/null +++ b/base-foc/lib/mk/lock.mk @@ -0,0 +1,5 @@ +SRC_CC = lock.cc +INC_DIR += $(REP_DIR)/src/base/lock +#INC_DIR += $(REP_DIR)/src/platform + +vpath lock.cc $(BASE_DIR)/src/base/lock diff --git a/base-foc/lib/mk/pager.mk b/base-foc/lib/mk/pager.mk new file mode 100644 index 000000000..eb4f42e1e --- /dev/null +++ b/base-foc/lib/mk/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-foc/lib/mk/platform.inc b/base-foc/lib/mk/platform.inc new file mode 100644 index 000000000..c2309db08 --- /dev/null +++ b/base-foc/lib/mk/platform.inc @@ -0,0 +1,69 @@ +# +# Create prerequisites for building Genode for Fiasco.OC +# +# Prior building Genode programs for Fiasco.OC, the kernel bindings must be +# generated. This is done by building a minimalistic subset of the original +# userland (L4re) that comes with Fiasco.OC. +# + +# +# Execute the rules in this file only at the second build stage when we know +# about the complete build settings, e.g., the 'CROSS_DEV_PREFIX'. +# +ifeq ($(called_from_lib_mk),yes) + +# +# Create mirror for architecture-specific L4sys header files +# +L4_INC_TARGETS += l4/sys \ + l4f/l4/sys \ + l4/sigma0 \ + l4/vcpu + +all: $(addprefix $(BUILD_BASE_DIR)/include/,$(L4_INC_TARGETS)) + +$(BUILD_BASE_DIR)/include/%: + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)ln -sf $(L4_BUILD_DIR)/include/$* $@ + +# +# Sanity checks +# +ifeq ($(L4_BUILD_DIR),$(BUILD_BASE_DIR)/l4) +ifeq ($(L4_CONFIG),) +all: $(REP_DIR)/contrib l4_config_not_defined +l4_config_not_defined: + $(VERBOSE)$(ECHO) "Error: L4_CONFIG is not defined, platform not supported" + @false +endif +endif + +$(REP_DIR)/contrib: + $(VERBOSE)$(ECHO) "--> Please, execute 'make prepare' in $(REP_DIR)" + $(VERBOSE)$(ECHO) "--> before compiling Genode apps for Fiasco.OC." + $(VERBOSE)$(ECHO) "--> Run 'make cleanall' before next compilation." + $(VERBOSE)exit 1 + +# +# Create L4 build directory +# +# Resetting the 'MAKEFLAGS' is important because otherwise, the L4 +# build system will stuble over predefined variables, i.e., 'LIB' +# +$(BUILD_BASE_DIR)/l4/.kconfig: $(REP_DIR)/contrib + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) $(VERBOSE_DIR) -C $(REP_DIR)/contrib/l4 B=$(dir $@) \ + DROPSCONF_DEFCONFIG="$(L4_CONFIG)" \ + VERBOSE="$(VERBOSE)" SYSTEM_TARGET="$(CROSS_DEV_PREFIX)" + +PKGS = ldscripts \ + l4sys \ + libsigma0 \ + libvcpu/include + +include $(REP_DIR)/mk/l4_pkg.mk +all: $(PKG_TAGS) + +$(PKG_TAGS): $(BUILD_BASE_DIR)/l4/.kconfig + +endif + diff --git a/base-foc/lib/mk/platform_pbxa9/platform.mk b/base-foc/lib/mk/platform_pbxa9/platform.mk new file mode 100644 index 000000000..067f34694 --- /dev/null +++ b/base-foc/lib/mk/platform_pbxa9/platform.mk @@ -0,0 +1,6 @@ +# +# Configuration for L4 build system (for kernel-bindings, sigma0, bootstrap). +# +L4_CONFIG = $(call select_from_repositories,config/rva9.user) + +include $(REP_DIR)/lib/mk/arm/platform.inc diff --git a/base-foc/lib/mk/platform_vea9x4/platform.mk b/base-foc/lib/mk/platform_vea9x4/platform.mk new file mode 100644 index 000000000..067f34694 --- /dev/null +++ b/base-foc/lib/mk/platform_vea9x4/platform.mk @@ -0,0 +1,6 @@ +# +# Configuration for L4 build system (for kernel-bindings, sigma0, bootstrap). +# +L4_CONFIG = $(call select_from_repositories,config/rva9.user) + +include $(REP_DIR)/lib/mk/arm/platform.inc diff --git a/base-foc/lib/mk/raw_server.mk b/base-foc/lib/mk/raw_server.mk new file mode 100644 index 000000000..3487331b9 --- /dev/null +++ b/base-foc/lib/mk/raw_server.mk @@ -0,0 +1,4 @@ +SRC_CC = server.cc common.cc + +vpath server.cc $(REP_DIR)/src/base/server +vpath common.cc $(BASE_DIR)/src/base/server diff --git a/base-foc/lib/mk/server.mk b/base-foc/lib/mk/server.mk new file mode 100644 index 000000000..de155386e --- /dev/null +++ b/base-foc/lib/mk/server.mk @@ -0,0 +1,3 @@ +LIBS = thread + +include $(REP_DIR)/lib/mk/raw_server.mk diff --git a/base-foc/lib/mk/thread.mk b/base-foc/lib/mk/thread.mk new file mode 100644 index 000000000..74ce01970 --- /dev/null +++ b/base-foc/lib/mk/thread.mk @@ -0,0 +1,3 @@ +SRC_CC = thread.cc thread_start.cc thread_bootstrap.cc + +vpath %.cc $(REP_DIR)/src/base/thread diff --git a/base-foc/lib/mk/x86/syscalls.mk b/base-foc/lib/mk/x86/syscalls.mk new file mode 100644 index 000000000..80154535d --- /dev/null +++ b/base-foc/lib/mk/x86/syscalls.mk @@ -0,0 +1,5 @@ +SRC_C += utcb.c +SRC_S += syscalls_direct.S + +vpath syscalls_direct.S $(L4_BUILD_DIR)/source/pkg/l4sys/lib/src/ARCH-x86 +vpath utcb.c $(L4_BUILD_DIR)/source/pkg/l4sys/lib/src diff --git a/base-foc/lib/mk/x86_32/ipc.mk b/base-foc/lib/mk/x86_32/ipc.mk new file mode 100644 index 000000000..010cede4f --- /dev/null +++ b/base-foc/lib/mk/x86_32/ipc.mk @@ -0,0 +1,3 @@ +SRC_CC = x86/pager_exception.cc x86_32/pager.cc + +include $(REP_DIR)/lib/mk/ipc.inc diff --git a/base-foc/lib/mk/x86_32/platform.mk b/base-foc/lib/mk/x86_32/platform.mk new file mode 100644 index 000000000..fedc7acf3 --- /dev/null +++ b/base-foc/lib/mk/x86_32/platform.mk @@ -0,0 +1,13 @@ +# +# Configuration for L4 build system (for kernel-bindings, sigma0, bootstrap). +# +L4_CONFIG = $(call select_from_repositories,contrib/l4/mk/defconfig/config.x86) + +# +# Create mirror for architecture-specific L4sys header files +# +L4_INC_TARGETS = x86/l4/sys \ + x86/l4f/l4/sys \ + x86/l4/vcpu + +include $(REP_DIR)/lib/mk/platform.inc diff --git a/base-foc/lib/mk/x86_32/startup.mk b/base-foc/lib/mk/x86_32/startup.mk new file mode 100644 index 000000000..8c2ef8fe0 --- /dev/null +++ b/base-foc/lib/mk/x86_32/startup.mk @@ -0,0 +1,8 @@ +SRC_S = crt0.s +SRC_CC = _main.cc +LIBS += cxx lock + +INC_DIR += $(REP_DIR)/src/platform $(BASE_DIR)/src/platform + +vpath crt0.s $(BASE_DIR)/src/platform/x86_32 +vpath _main.cc $(BASE_DIR)/src/platform diff --git a/base-foc/lib/mk/x86_64/ipc.mk b/base-foc/lib/mk/x86_64/ipc.mk new file mode 100644 index 000000000..9586dd77b --- /dev/null +++ b/base-foc/lib/mk/x86_64/ipc.mk @@ -0,0 +1,3 @@ +SRC_CC = x86/pager_exception.cc x86_64/pager.cc + +include $(REP_DIR)/lib/mk/ipc.inc diff --git a/base-foc/lib/mk/x86_64/platform.mk b/base-foc/lib/mk/x86_64/platform.mk new file mode 100644 index 000000000..5a12904c7 --- /dev/null +++ b/base-foc/lib/mk/x86_64/platform.mk @@ -0,0 +1,13 @@ +# +# Configuration for L4 build system (for kernel-bindings, sigma0, bootstrap). +# +L4_CONFIG = $(call select_from_repositories,contrib/l4/mk/defconfig/config.amd64) + +# +# Create mirror for architecture-specific L4sys header files +# +L4_INC_TARGETS = amd64/l4/sys \ + amd64/l4f/l4/sys \ + amd64/l4/vcpu + +include $(REP_DIR)/lib/mk/platform.inc diff --git a/base-foc/lib/mk/x86_64/startup.mk b/base-foc/lib/mk/x86_64/startup.mk new file mode 100644 index 000000000..cc3c00505 --- /dev/null +++ b/base-foc/lib/mk/x86_64/startup.mk @@ -0,0 +1,8 @@ +SRC_S = crt0.s +SRC_CC = _main.cc +LIBS += cxx lock + +INC_DIR += $(REP_DIR)/src/platform $(BASE_DIR)/src/platform + +vpath crt0.s $(BASE_DIR)/src/platform/x86_64 +vpath _main.cc $(BASE_DIR)/src/platform diff --git a/base-foc/mk/l4_pkg.mk b/base-foc/mk/l4_pkg.mk new file mode 100644 index 000000000..07e4d55da --- /dev/null +++ b/base-foc/mk/l4_pkg.mk @@ -0,0 +1,60 @@ +# +# Utility for building L4 contrib packages +# +# Variables that steer the behaviour of this makefile: +# +# TARGET - name of target +# PKGS - list of L4 packages to visit in order to create +# the target +# + +LIBS += platform + +ifeq ($(filter-out $(SPECS),x86_32),) + L4_BUILD_ARCH := x86_586 +endif + +ifeq ($(filter-out $(SPECS),x86_64),) + L4_BUILD_ARCH := amd_k9 +endif + +ifeq ($(filter-out $(SPECS),arm_v7a),) + L4_BUILD_ARCH := arm_armv7a +endif + +ifeq ($(L4_BUILD_ARCH),) +all: l4_build_arch_undefined + $(VERBOSE)$(ECHO) "Error: L4_BUILD_ARCH undefined, architecture not supported" + $(VERBOSE)false +endif + +L4_BUILD_DIR = $(BUILD_BASE_DIR)/l4 +L4_BUILD_OPT = SYSTEM_TARGET=$(CROSS_DEV_PREFIX) +L4_PKG_DIR = $(REP_DIR)/contrib/l4/pkg +STARTUP_LIB = +PKG_TAGS = $(addsuffix .tag,$(PKGS)) + +$(TARGET): $(PKG_TAGS) + +# +# We preserve the order of processing 'l4/pkg/' directories because of +# inter-package dependencies. However, within each directory, make is working +# in parallel. +# +.NOTPARALLEL: $(PKG_TAGS) + +%.tag: + $(VERBOSE_MK) $(MAKE) $(VERBOSE_DIR) O=$(L4_BUILD_DIR) -C $(L4_PKG_DIR)/$* "$(L4_BUILD_OPT)" + $(VERBOSE)mkdir -p $(dir $@) && touch $@ + +clean cleanall: clean_tags + +# if (pseudo) target is named after a directory, remove the whole subtree +clean_prg_objects: clean_dir_named_as_target + +clean_dir_named_as_target: + $(VERBOSE)(test -d $(TARGET) && rm -rf $(TARGET)) || true + +clean_tags: + $(VERBOSE)rm -f $(PKG_TAGS) + diff --git a/base-foc/mk/spec-foc.mk b/base-foc/mk/spec-foc.mk new file mode 100644 index 000000000..4a383fec8 --- /dev/null +++ b/base-foc/mk/spec-foc.mk @@ -0,0 +1,52 @@ +# +# Specifics for the Fiasco.OC kernel API +# + +-include $(call select_from_repositories,etc/foc.conf) +-include $(BUILD_BASE_DIR)/etc/foc.conf + +# +# L4/sys headers +# +L4_INC_DIR += $(BUILD_BASE_DIR)/include/ +L4F_INC_DIR += $(BUILD_BASE_DIR)/include/l4f + +# +# L4 build directory, if not defined by 'foc.conf', use directory local +# to the Genode build directory. +# +L4_BUILD_DIR ?= $(BUILD_BASE_DIR)/l4 + +# +# Build everything with -fPIC because the Fiasco.OC syscall bindings +# rely on 'ebx' (on x86) being handled with care. Without -fPIC enabled, +# the syscall bindings break. +# +CC_OPT += -fPIC + +# +# Use 'regparm=0' call instead of an inline function, when accessing +# the utcb. This is needed to stay compatible with L4linux +# +CC_OPT += -DL4SYS_USE_UTCB_WRAP=1 + +# +# Startup code to be used when building a program +# +STARTUP_LIB ?= startup +PRG_LIBS += $(STARTUP_LIB) + +all: + +# +# Clean rules for removing the side effects of building the platform +# library +# +clean_includes: + $(VERBOSE)rm -rf $(BUILD_BASE_DIR)/include + +clean_contrib: + $(VERBOSE)rm -rf $(BUILD_BASE_DIR)/l4 + +cleanall: clean_contrib clean_includes + diff --git a/base-foc/mk/spec-foc_arm.mk b/base-foc/mk/spec-foc_arm.mk new file mode 100644 index 000000000..312e8aa01 --- /dev/null +++ b/base-foc/mk/spec-foc_arm.mk @@ -0,0 +1,37 @@ +# +# Specifics for Fiasco.OC on ARM +# + +SPECS += foc + +# +# Linker options that are specific for arm +# +LD_TEXT_ADDR ?= 0x01000000 + +# +# ARM-specific L4/sys headers +# +L4_INC_DIR = $(BUILD_BASE_DIR)/include/arm +L4F_INC_DIR = $(BUILD_BASE_DIR)/include/arm/l4f + +# +# Support for Fiasco.OC's ARM-specific atomic functions +# +REP_INC_DIR += include/arm + +# +# Defines for L4/sys headers +# +CC_OPT += -DCONFIG_L4_CALL_SYSCALLS -DARCH_arm + +# +# Architecture-specific L4sys header files +# +L4_INC_TARGETS = arm/l4/sys \ + arm/l4f/l4/sys \ + arm/l4/vcpu + +include $(call select_from_repositories,mk/spec-foc.mk) + +INC_DIR += $(L4F_INC_DIR) $(L4_INC_DIR) diff --git a/base-foc/mk/spec-foc_pbxa9.mk b/base-foc/mk/spec-foc_pbxa9.mk new file mode 100644 index 000000000..eec6e570e --- /dev/null +++ b/base-foc/mk/spec-foc_pbxa9.mk @@ -0,0 +1,4 @@ +SPECS += foc_arm platform_pbxa9 + +include $(call select_from_repositories,mk/spec-platform_pbxa9.mk) +include $(call select_from_repositories,mk/spec-foc_arm.mk) diff --git a/base-foc/mk/spec-foc_vea9x4.mk b/base-foc/mk/spec-foc_vea9x4.mk new file mode 100644 index 000000000..031fa605c --- /dev/null +++ b/base-foc/mk/spec-foc_vea9x4.mk @@ -0,0 +1,4 @@ +SPECS += foc_arm platform_vea9x4 + +include $(call select_from_repositories,mk/spec-platform_vea9x4.mk) +include $(call select_from_repositories,mk/spec-foc_arm.mk) diff --git a/base-foc/mk/spec-foc_x86_32.mk b/base-foc/mk/spec-foc_x86_32.mk new file mode 100644 index 000000000..341693d41 --- /dev/null +++ b/base-foc/mk/spec-foc_x86_32.mk @@ -0,0 +1,25 @@ +# +# Specifics for Fiasco.OC on x86 +# + +SPECS += x86_32 foc +SPECS += pci ps2 vesa + +# +# Linker options that are specific for x86 +# +LD_TEXT_ADDR ?= 0x01000000 + +# +# L4/sys headers +# +L4_INC_DIR = $(BUILD_BASE_DIR)/include/x86 +L4F_INC_DIR = $(BUILD_BASE_DIR)/include/x86/l4f + +# +# Also include less-specific configuration last +# +include $(call select_from_repositories,mk/spec-x86_32.mk) +include $(call select_from_repositories,mk/spec-foc.mk) + +INC_DIR += $(L4F_INC_DIR) $(L4_INC_DIR) diff --git a/base-foc/mk/spec-foc_x86_64.mk b/base-foc/mk/spec-foc_x86_64.mk new file mode 100644 index 000000000..fe126d344 --- /dev/null +++ b/base-foc/mk/spec-foc_x86_64.mk @@ -0,0 +1,30 @@ +# +# Specifics for Fiasco.OC on x86 64-bit +# + +SPECS += x86_64 foc +SPECS += pci ps2 vesa + +# +# Linker options that are specific for x86 +# +LD_TEXT_ADDR ?= 0x01000000 + +# +# L4/sys headers +# +L4_INC_DIR = $(BUILD_BASE_DIR)/include/amd64 +L4F_INC_DIR = $(BUILD_BASE_DIR)/include/amd64/l4f + +# +# Compile for 64-bit +# +CC_OPT += -m64 + +# +# Also include less-specific configuration last +# +include $(call select_from_repositories,mk/spec-x86_64.mk) +include $(call select_from_repositories,mk/spec-foc.mk) + +INC_DIR += $(L4F_INC_DIR) $(L4_INC_DIR) diff --git a/base-foc/patches/README b/base-foc/patches/README new file mode 100644 index 000000000..543e94196 --- /dev/null +++ b/base-foc/patches/README @@ -0,0 +1,14 @@ +The patches in this directory are modifications of the Fiasco.OC kernel +required for using this kernel with Genode. + +:'foc_single_step_x86.patch': + + This patch enables the user land to use the CPU's single stepping mode on + x86_32 platforms. It is needed to enable the use of GDB monitor for + user-level debugging. + +:'fix_exception_ip.patch': + + On the occurrence of undefined-instruction exceptions on ARM, Fiasco.OC + reports a wrong program-counter value to the exception handler. The patch + fixes the problem. diff --git a/base-foc/patches/crtn_arm_binutils_2.21.1.patch b/base-foc/patches/crtn_arm_binutils_2.21.1.patch new file mode 100644 index 000000000..a32d341c7 --- /dev/null +++ b/base-foc/patches/crtn_arm_binutils_2.21.1.patch @@ -0,0 +1,21 @@ +Index: uclibc/lib/contrib/uclibc/libc/sysdeps/linux/arm/crtn.S +=================================================================== +--- l4/pkg/uclibc/lib/contrib/uclibc/libc/sysdeps/linux/arm/crtn.S (revision 36) ++++ l4/pkg/uclibc/lib/contrib/uclibc/libc/sysdeps/linux/arm/crtn.S (working copy) +@@ -16,6 +16,7 @@ + #else + .align 2 + .arm ++ .L1: + ldmdb fp, {r4, r5, r6, r7, r8, r9, sl, fp, sp, pc} + #endif + .size .L1, .-.L1 +@@ -32,6 +33,7 @@ + #else + .align 2 + .arm ++ .L2: + ldmdb fp, {r4, r5, r6, r7, r8, r9, sl, fp, sp, pc} + #endif + .size .L2,.-.L2 + diff --git a/base-foc/patches/fix_exception_ip.patch b/base-foc/patches/fix_exception_ip.patch new file mode 100644 index 000000000..2b866bda8 --- /dev/null +++ b/base-foc/patches/fix_exception_ip.patch @@ -0,0 +1,15 @@ +Index: kernel/fiasco/src/kern/arm/thread-arm.cpp +=================================================================== +--- kernel/fiasco/src/kern/arm/thread-arm.cpp (revision 38) ++++ kernel/fiasco/src/kern/arm/thread-arm.cpp (working copy) +@@ -258,6 +258,10 @@ + && handle_copro_fault[copro](opcode, ts)) + return; + } ++ ++ /* check for ARM default GDB breakpoint */ ++ if (!(ts->psr & Proc::Status_thumb) && opcode == 0xe7ffdefe) ++ ts->pc -= 4; + } + + undef_insn: diff --git a/base-foc/patches/foc_single_step_x86.patch b/base-foc/patches/foc_single_step_x86.patch new file mode 100644 index 000000000..5bc8f16f9 --- /dev/null +++ b/base-foc/patches/foc_single_step_x86.patch @@ -0,0 +1,241 @@ +Index: kernel/fiasco/src/Kconfig +=================================================================== +--- kernel/fiasco/src/Kconfig (revision 38) ++++ kernel/fiasco/src/Kconfig (working copy) +@@ -694,6 +694,14 @@ + prevent some P4 processors from being overheated. This option + requires a working timer IRQ to wakeup getchar periodically. + ++config USER_SINGLE_STEP ++ bool "Enable user level single stepping support" ++ depends on IA32 ++ default n ++ help ++ This option enables single stepping of user level applications outside of ++ JDB. ++ + choice + prompt "Warn levels" + default WARN_WARNING +Index: kernel/fiasco/src/kern/ia32/config-ia32.cpp +=================================================================== +--- kernel/fiasco/src/kern/ia32/config-ia32.cpp (revision 38) ++++ kernel/fiasco/src/kern/ia32/config-ia32.cpp (working copy) +@@ -98,6 +98,12 @@ + static const bool kinfo_timer_uses_rdtsc = false; + #endif + ++#ifdef CONFIG_USER_SINGLE_STEP ++ static const bool user_single_step = true; ++#else ++ static const bool user_single_step = false; ++#endif ++ + static const bool old_sigma0_adapter_hack = false; + + // the default uart to use for serial console +Index: kernel/fiasco/src/kern/ia32/32/entry-native.S +=================================================================== +--- kernel/fiasco/src/kern/ia32/32/entry-native.S (revision 38) ++++ kernel/fiasco/src/kern/ia32/32/entry-native.S (working copy) +@@ -46,6 +46,30 @@ + jmp slowtraps + .endm + ++#ifdef CONFIG_USER_SINGLE_STEP ++.macro HANDLE_USER_TRAP1 ++ /* Save EFLAGS, this may trap if user task had single stepping activated ++ * test for single stepping ++ */ ++ pushf ++ addl $4, %esp ++ testl $EFLAGS_TF, -4(%esp) ++.endm ++ ++.macro RESTORE_USER_TRAP1 ++ /* Restore single stepping if it has been set */ ++ je 1f ++ orl $EFLAGS_TF, (%esp) ++1: ++.endm ++#else ++.macro HANDLE_USER_TRAP1 ++.endm ++ ++.macro RESTORE_USER_TRAP1 ++.endm ++#endif ++ + .p2align 4 + .globl entry_vec01_debug + entry_vec01_debug: +@@ -55,6 +79,15 @@ + cmpl $VAL__MEM_LAYOUT__TCBS_END, %esp + jbe 2f + #endif ++ ++ /* test if trap was raised within kernel */ ++ testl $3, 4(%esp) ++ jne 1f ++ ++ /* turn of EFLAGS.TF */ ++ btrl $7, 8(%esp) ++ iret ++ + 1: pushl $0 + pushl $1 + pusha +@@ -214,11 +247,17 @@ + .p2align(4) + .global entry_sys_fast_ipc_c + entry_sys_fast_ipc_c: ++ ++ HANDLE_USER_TRAP1 ++ + pop %esp + pushl $(GDT_DATA_USER|SEL_PL_U) /* user ss */ + pushl %ebp // push user SP (get in ebp) + // Fake user eflags, set IOPL to 3 + pushl $(EFLAGS_IOPL_U | EFLAGS_IF) ++ ++ RESTORE_USER_TRAP1 ++ + cld + // Fake user cs. This cs value is never used with exception + // that the thread is ex_regs'd before we leave with sysexit. +Index: kernel/fiasco/src/kern/ia32/thread-ia32.cpp +=================================================================== +--- kernel/fiasco/src/kern/ia32/thread-ia32.cpp (revision 38) ++++ kernel/fiasco/src/kern/ia32/thread-ia32.cpp (working copy) +@@ -196,12 +196,19 @@ + Address ip; + int from_user = ts->cs() & 3; + ++ //if (ts->_trapno != 3) ++ // LOG_MSG_3VAL(this, "trap", ts->_trapno, from_user, ts->ip()); ++ + if (EXPECT_FALSE(ts->_trapno == 0xee)) //debug IPI + { + Ipi::eoi(Ipi::Debug); + goto generic_debug; + } + ++ if (Config::user_single_step && ts->_trapno == 1 && from_user) ++ if (send_exception(ts)) ++ goto success; ++ + if (from_user && _space.user_mode()) + { + if (ts->_trapno == 14 && Kmem::is_io_bitmap_page_fault(ts->_cr2)) +@@ -521,7 +528,8 @@ + // thread (not alien) and it's a debug trap, + // debug traps for aliens are always reflected as exception IPCs + if (!(state() & Thread_alien) +- && (ts->_trapno == 1 || ts->_trapno == 3)) ++ && ((ts->_trapno == 1 && !Config::user_single_step) ++ || ts->_trapno == 3)) + return 0; // we do not handle this + + if (ts->_trapno == 3) +@@ -574,6 +582,11 @@ + } + } + ++IMPLEMENT inline ++void ++Thread::user_single_step(bool) ++{} ++ + //---------------------------------------------------------------------------- + IMPLEMENTATION [ia32]: + +@@ -586,6 +599,16 @@ + _gs = _fs = Utcb_init::utcb_segment(); + } + ++IMPLEMENT inline ++void ++Thread::user_single_step(bool enable) ++{ ++ if (!Config::user_single_step) ++ return; ++ ++ regs()->flags(enable ? user_flags() | EFLAGS_TF : user_flags() & ~EFLAGS_TF); ++} ++ + //---------------------------------------------------------------------------- + IMPLEMENTATION [amd64]: + +Index: kernel/fiasco/src/kern/thread_object.cpp +=================================================================== +--- kernel/fiasco/src/kern/thread_object.cpp (revision 38) ++++ kernel/fiasco/src/kern/thread_object.cpp (working copy) +@@ -524,6 +524,8 @@ + if (o_ip) *o_ip = user_ip(); + if (o_flags) *o_flags = user_flags(); + ++ (ops & Exr_single_step) ? user_single_step(true) : user_single_step(false); ++ + // Changing the run state is only possible when the thread is not in + // an exception. + if (!(ops & Exr_cancel) && (state() & Thread_in_exception)) +Index: kernel/fiasco/src/kern/thread.cpp +=================================================================== +--- kernel/fiasco/src/kern/thread.cpp (revision 38) ++++ kernel/fiasco/src/kern/thread.cpp (working copy) +@@ -70,6 +70,7 @@ + { + Exr_cancel = 0x10000, + Exr_trigger_exception = 0x20000, ++ Exr_single_step = 0x40000, + }; + + enum Vcpu_ctl_flags +@@ -137,6 +138,8 @@ + + inline Mword user_flags() const; + ++ inline void user_single_step(bool); ++ + /** nesting level in debugger (always critical) if >1 */ + static Per_cpu nested_trap_recover; + static void handle_remote_requests_irq() asm ("handle_remote_cpu_requests"); +Index: kernel/fiasco/src/kern/arm/thread-arm.cpp +=================================================================== +--- kernel/fiasco/src/kern/arm/thread-arm.cpp (revision 38) ++++ kernel/fiasco/src/kern/arm/thread-arm.cpp (working copy) +@@ -361,7 +361,7 @@ + IMPLEMENT inline + Mword + Thread::user_flags() const +-{ return 0; } ++{ return state() & Thread_ready; } + + IMPLEMENT inline NEEDS[Thread::exception_triggered] + void +@@ -549,6 +549,10 @@ + return (v[insn >> 28] >> (psr >> 28)) & 1; + } + ++IMPLEMENT inline ++void Thread::user_single_step(bool) ++{} ++ + // ------------------------------------------------------------------------ + IMPLEMENTATION [arm && armv6plus]: + +Index: kernel/fiasco/src/kern/ppc32/thread-ppc32.cpp +=================================================================== +--- kernel/fiasco/src/kern/ppc32/thread-ppc32.cpp (revision 38) ++++ kernel/fiasco/src/kern/ppc32/thread-ppc32.cpp (working copy) +@@ -307,6 +307,10 @@ + } + } + ++IMPLEMENT inline ++void Thread::user_single_step(bool) ++{} ++ + PUBLIC inline NEEDS ["trap_state.h"] + int + Thread::send_exception_arch(Trap_state * /*ts*/) diff --git a/base-foc/patches/timer_arm.patch b/base-foc/patches/timer_arm.patch new file mode 100644 index 000000000..d922d48b7 --- /dev/null +++ b/base-foc/patches/timer_arm.patch @@ -0,0 +1,20 @@ +Index: kernel/fiasco/src/kern/arm/bsp/realview/timer-arm-mptimer-realview.cpp +=================================================================== +--- kernel/fiasco/src/kern/arm/bsp/realview/timer-arm-mptimer-realview.cpp (revision 38) ++++ kernel/fiasco/src/kern/arm/bsp/realview/timer-arm-mptimer-realview.cpp (working copy) +@@ -4,7 +4,7 @@ + EXTENSION class Timer + { + private: +- enum { Interval = 104999, /* assumed 210MHz */}; ++ enum { Interval = 209999, /* assumed 210MHz */}; + }; + + // -------------------------------------------------------------------------- +@@ -13,5 +13,5 @@ + EXTENSION class Timer + { + private: +- enum { Interval = 49999, }; ++ enum { Interval = 99999, /* assumed 100MHz */}; + }; diff --git a/base-foc/patches/vexpress_detection.patch b/base-foc/patches/vexpress_detection.patch new file mode 100644 index 000000000..02c3af74d --- /dev/null +++ b/base-foc/patches/vexpress_detection.patch @@ -0,0 +1,13 @@ +Index: kernel/fiasco/src/kern/arm/bsp/realview/board_check-arm-realview.cpp +=================================================================== +--- kernel/fiasco/src/kern/arm/bsp/realview/board_check-arm-realview.cpp (revision 38) ++++ kernel/fiasco/src/kern/arm/bsp/realview/board_check-arm-realview.cpp (working copy) +@@ -46,7 +46,7 @@ + IMPLEMENTATION [arm && realview && realview_vexpress]: + + Board_check::id_pair Board_check::ids[] FIASCO_INITDATA = { +- { 0xffffff00, 0x1190f500 }, ++ { 0xcfffff00, 0x0190f500 }, + }; + + // ------------------------------------------------------------------------ diff --git a/base-foc/run/env b/base-foc/run/env new file mode 100644 index 000000000..f8bc4dbb7 --- /dev/null +++ b/base-foc/run/env @@ -0,0 +1,203 @@ +# +# \brief Fiasco.OC-specific test-environment supplements +# \author Stefan Kalkowski +# \date 2010-11-22 +# +# This file is meant to be used as '--include' argument for 'tool/run'. +# + +## +# Return the location of the Fiasco.OC user directory +# +proc l4_dir { } { + global _l4_dir + + if {![info exists _l4_dir]} { + if {[file exists etc/foc.conf]} { + set _l4_dir [exec sed -n "/^L4_BUILD_DIR/s/^.*=\\s*//p" etc/foc.conf] + if {[file exists $_l4_dir]} { return $_l4_dir } + } + + set _l4_dir "[pwd]/l4" + if {![file exists $_l4_dir]} { + puts -nonewline stderr "Error: Could neither find the L4 build directory " + puts -nonewline stderr "within '/l4' nor at a location " + puts -nonewline stderr "specified via 'L4_BUILD_DIR = ' " + puts stderr "in /etc/foc.conf'." + exit 1 + } + } + return $_l4_dir +} + +## +# Return whether the l4-buid-directory is provided from the outside +# +proc l4_dir_external { } { + if {[l4_dir] == "[pwd]/l4"} { return 0 } + return 1 +} + +## +# Return the location of the Fiasco.OC kernel directory +# +proc fiasco { } { + global _fiasco + + if {![info exists _fiasco]} { + if {[file exists etc/foc.conf]} { + set _fiasco [exec sed -n "/^KERNEL/s/^.*=\\s*//p" etc/foc.conf] + if {[file exists $_fiasco]} { return $_fiasco } + } + + # try to fall back to version hosted with the Genode build directory + set _fiasco "[pwd]/kernel/fiasco.oc/fiasco" + } + return $_fiasco +} + +## +# Return whether fiasco kernel is provided from the outside +# +proc fiasco_external { } { + if {[fiasco] == "[pwd]/kernel/fiasco.oc/fiasco"} { return 0 } + return 1 +} + +################################## +## Test framework API functions ## +################################## + +proc create_boot_directory { } { + exec rm -rf [run_dir] + exec mkdir -p [run_dir]/genode + + if {[have_spec x86]} { + exec mkdir -p [run_dir]/fiasco + exec mkdir -p [run_dir]/boot/grub + } +} + + +proc copy_and_strip_binaries {binaries} { + + # + # Collect contents of the boot image + # + foreach binary $binaries { + exec cp bin/$binary [run_dir]/genode + catch { + exec [cross_dev_prefix]strip [run_dir]/genode/$binary } + } + + # + # Generate config file for bootstrap + # +} + + +proc bin_dir { } { + if {[have_spec x86_32]} { return "[l4_dir]/bin/x86_586" } + if {[have_spec x86_64]} { return "[l4_dir]/bin/amd64_K8" } + if {[have_spec arm_v7a]} { return "[l4_dir]/bin/arm_armv7a" } + + puts stderr "Error: Cannot determine bin directory" + exit 1 +} + + +proc build_boot_image_x86 {binaries} { + + copy_and_strip_binaries $binaries + + set foc_targets { } + if {![fiasco_external] && ![file exists kernel]} { lappend foc_targets kernel } + if {![l4_dir_external]} { + if {![file exists bootstrap]} { lappend foc_targets bootstrap } + if {![file exists sigma0]} { lappend foc_targets sigma0 } + } + if {[llength $foc_targets] > 0} { build $foc_targets } + + # assert existence of the L4 build directory + l4_dir + + puts "using fiasco kernel [fiasco]" + exec cp [fiasco] [run_dir]/fiasco + puts "using sigma0/bootstrap at [l4_dir]" + exec cp [bin_dir]/l4f/sigma0 [run_dir]/fiasco + exec cp [bin_dir]/bootstrap [run_dir]/fiasco + + install_iso_bootloader_to_run_dir + + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on Fiasco.OC" + puts $fh " kernel /fiasco/bootstrap -modaddr=0x01100000" + puts $fh " module /fiasco/fiasco -serial_esc" + puts $fh " module /fiasco/sigma0" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + puts $fh " vbeset 0x117 506070" + close $fh + + create_iso_image_from_run_dir +} + + +proc build_boot_image_arm {binaries} { + + copy_and_strip_binaries $binaries + + build "kernel sigma0 bootstrap" + + # + # Generate bootstrap config + # + set fh [open "[run_dir]/modules.list" "WRONLY CREAT TRUNC"] + + puts $fh "modaddr 0x01100000\n" + puts $fh "entry genode" + puts $fh "kernel [fiasco] -serial_esc" + puts $fh "roottask genode/core" + puts $fh "module genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh "module genode/$binary" } } + close $fh + + set gen_img_cmd "cd [l4_dir]/source/pkg/bootstrap/server/src && " + append gen_img_cmd "make O=[l4_dir] ENTRY=genode " + append gen_img_cmd "BOOTSTRAP_DO_UIMAGE= BOOTSTRAP_DO_RAW_IMAGE= " + append gen_img_cmd "BOOTSTRAP_MODULES_LIST=[pwd]/[run_dir]/modules.list " + append gen_img_cmd "BOOTSTRAP_SEARCH_PATH=[pwd]/[run_dir]:[file dirname [fiasco]]:[l4_dir] " + append gen_img_cmd "SYSTEM_TARGET=[cross_dev_prefix]" + + set pid [eval "spawn sh -c \"$gen_img_cmd\""] + expect { eof { } } + if {[lindex [wait $pid] end] != 0} { + puts "Error: Single-image creation failed" + exit -4 + } + + exec cp [bin_dir]/bootstrap.elf [run_dir]/image.elf +} + + +proc build_boot_image {binaries} { + if {[have_spec x86]} { return [build_boot_image_x86 $binaries] } + if {[have_spec arm]} { return [build_boot_image_arm $binaries] } +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + spawn_qemu $wait_for_re $timeout_value } diff --git a/base-foc/src/base/console/core_console.h b/base-foc/src/base/console/core_console.h new file mode 100644 index 000000000..e5e50de5a --- /dev/null +++ b/base-foc/src/base/console/core_console.h @@ -0,0 +1,30 @@ +/* + * \brief Console backend using the Fiasco kernel debugger + * \author Norman Feske + * \date 2006-04-08 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +/* Genode includes */ +#include + +namespace Genode { + + class Core_console : public Console + { + protected: + + void _out_char(char c) { Fiasco::outchar(c); } + }; +} diff --git a/base-foc/src/base/env/cap_sel_alloc.cc b/base-foc/src/base/env/cap_sel_alloc.cc new file mode 100644 index 000000000..c4ccbae9a --- /dev/null +++ b/base-foc/src/base/env/cap_sel_alloc.cc @@ -0,0 +1,107 @@ +/* + * \brief Capability-selector allocator + * \author Stefan Kalkowski + * \date 2010-12-06 + * + * This is a Fiasco.OC-specific addition to the process enviroment. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* Fiasco.OC includes */ +namespace Fiasco { +#include +#include +} + +using namespace Genode; + +/** + * First available capability selector for custom use + * + * Must be initialized by the startup code + */ +static unsigned long __first_free_cap_selector = + Fiasco::Fiasco_capability::USER_BASE_CAP; + +/** + * Low-level lock to protect the allocator + * + * We cannot use a normal Genode lock because this lock is used by code + * executed prior the initialization of Genode. + */ +class Alloc_lock +{ + private: + + int _state; + + public: + + enum State { LOCKED, UNLOCKED }; + + /** + * Constructor + */ + Alloc_lock() : _state(UNLOCKED) {} + + void lock() + { + while (!Genode::cmpxchg(&_state, UNLOCKED, LOCKED)) + Fiasco::l4_ipc_sleep(Fiasco::l4_ipc_timeout(0, 0, 500, 0)); + } + + void unlock() { _state = UNLOCKED; } +}; + + +/** + * Return lock used to protect capability selector allocations + */ +static Alloc_lock *alloc_lock() +{ + static Alloc_lock alloc_lock_inst; + return &alloc_lock_inst; +} + + +addr_t Capability_allocator::alloc(size_t num_caps) +{ + alloc_lock()->lock(); + int ret_base = _cap_idx; + _cap_idx += num_caps * Fiasco::L4_CAP_SIZE; + alloc_lock()->unlock(); + return ret_base; +} + + +void Capability_allocator::free(addr_t cap, size_t num_caps_log2) +{ +// PWRN("Not yet implemented!"); +} + + +Capability_allocator::Capability_allocator() +: _cap_idx(__first_free_cap_selector) +{ + /* initialize lock */ + alloc_lock(); +} + + +Capability_allocator* Capability_allocator::allocator() +{ + static Capability_allocator inst; + return &inst; +} diff --git a/base-foc/src/base/ipc/arm/pager.cc b/base-foc/src/base/ipc/arm/pager.cc new file mode 100644 index 000000000..71dc997f0 --- /dev/null +++ b/base-foc/src/base/ipc/arm/pager.cc @@ -0,0 +1,27 @@ +/* + * \brief Fiasco.OC pager framework + * \author Stefan Kalkowski + * \date 2011-09-08 + * + * ARM specific parts, when handling cpu-exceptions. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + + +void Genode::Ipc_pager::copy_regs(Thread_state *state) +{ + state->ip = _regs.pc; + state->sp = _regs.sp; + Genode::memcpy(&state->r, &_regs.r, sizeof(state->r)); + state->lr = _regs.ulr; + state->cpsr = _regs.cpsr; +} diff --git a/base-foc/src/base/ipc/arm/pager_exception.cc b/base-foc/src/base/ipc/arm/pager_exception.cc new file mode 100644 index 000000000..248adfc71 --- /dev/null +++ b/base-foc/src/base/ipc/arm/pager_exception.cc @@ -0,0 +1,29 @@ +/* + * \brief ARM-specific pager support for Fiasco.OC + * \author Stefan Kalkowski + * \date 2011-08-24 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +namespace Fiasco { +#include +} + +enum Exceptions { EX_REGS = 0x500000 }; + + +void Genode::Ipc_pager::_parse_exception() +{ + if (Fiasco::l4_utcb_exc()->err == EX_REGS) + _type = PAUSE; + else + _type = EXCEPTION; +} diff --git a/base-foc/src/base/ipc/ipc.cc b/base-foc/src/base/ipc/ipc.cc new file mode 100644 index 000000000..731e29da2 --- /dev/null +++ b/base-foc/src/base/ipc/ipc.cc @@ -0,0 +1,317 @@ +/* + * \brief Implementation of the IPC API for Fiasco.OC + * \author Stefan Kalkowski + * \date 2009-12-03 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* + * l4_msgtag_t (size == 1 mword) format: + * + * -------------------------------------------------------------- + * | label | 4 Bit flags | 6 Bit items | 6 Bit word count | + * -------------------------------------------------------------- + */ + + +/* Genode includes */ +#include +#include +#include +#include + +/* base-foc/src/base/lock */ +#include /* for 'thread_get_my_native_id()' */ + +/* Fiasco.OC includes */ +namespace Fiasco { +#include +#include +#include +#include +#include +} + +using namespace Genode; +using namespace Fiasco; + + +/*************** + ** Utilities ** + ***************/ + +enum Debug { + DEBUG_MSG = 0, + HALT_ON_ERROR = 0 +}; + + +static bool ipc_error(l4_msgtag_t tag, bool print) +{ + int ipc_error = l4_ipc_error(tag, l4_utcb()); + if (ipc_error) { + if (print) { + outstring("Ipc error: "); + outhex32(ipc_error); + outstring(" occurred!\n"); + } + if (HALT_ON_ERROR) + enter_kdebug("Ipc error"); + return true; + } + return false; +} + + +/** + * Copy message registers from UTCB to destination message buffer + */ +static void copy_utcb_to_msgbuf(l4_msgtag_t tag, Msgbuf_base *rcv_msg) +{ + unsigned num_msg_words = l4_msgtag_words(tag); + unsigned num_cap_sel = l4_msgtag_items(tag); + if (num_msg_words == 0 && num_cap_sel == 0) + return; + + /* look up and validate destination message buffer to receive the payload */ + l4_mword_t *msg_buf = (l4_mword_t *)rcv_msg->buf; + if (num_msg_words*sizeof(l4_mword_t) > rcv_msg->size()) { + if (DEBUG_MSG) + outstring("receive message buffer too small"); + num_msg_words = rcv_msg->size()/sizeof(l4_mword_t); + } + + /* read message payload into destination message buffer */ + l4_mword_t *src = (l4_mword_t *)l4_utcb_mr(); + l4_mword_t *dst = (l4_mword_t *)&msg_buf[0]; + for (unsigned i = 0; i < num_msg_words; i++) + *dst++ = *src++; + + rcv_msg->rcv_reset(); +} + + +/** + * Copy message registers from message buffer to UTCB and create message tag. + */ +static l4_msgtag_t copy_msgbuf_to_utcb(Msgbuf_base *snd_msg, unsigned offset, + Native_capability dst) +{ + l4_mword_t *msg_buf = (l4_mword_t *)snd_msg->buf; + unsigned num_msg_words = offset/sizeof(l4_mword_t); + unsigned num_cap_sel = snd_msg->snd_cap_sel_cnt(); + + if (num_msg_words + 2 * num_cap_sel > L4_UTCB_GENERIC_DATA_SIZE) { + if (DEBUG_MSG) + outstring("receive message buffer too small"); + throw Ipc_error(); + } + + /* first copy target label to message buffer */ + msg_buf[0] = dst.local_name(); + + /* store message into UTCB message registers */ + for (unsigned i = 0; i < num_msg_words; i++) + l4_utcb_mr()->mr[i] = msg_buf[i]; + + /* setup flexpages of capabilities to send */ + for (unsigned i = 0; i < num_cap_sel; i++) { + unsigned idx = num_msg_words + 2*i; + l4_utcb_mr()->mr[idx] = L4_ITEM_MAP/* | L4_ITEM_CONT*/; + l4_utcb_mr()->mr[idx + 1] = l4_obj_fpage(snd_msg->snd_cap_sel(i), + 0, L4_FPAGE_RWX).raw; + } + + /* we have consumed capability selectors, reset message buffer */ + snd_msg->snd_reset(); + + return l4_msgtag(0, num_msg_words, num_cap_sel, 0); +} + + +/***************** + ** Ipc_ostream ** + *****************/ + +void Ipc_ostream::_send() +{ + l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst); + tag = l4_ipc_send(_dst.dst(), l4_utcb(), tag, L4_IPC_NEVER); + if (ipc_error(tag, DEBUG_MSG)) + throw Ipc_error(); + + _write_offset = sizeof(l4_mword_t); +} + + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) +: + Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()), + _snd_msg(snd_msg), _dst(dst) +{ + _write_offset = sizeof(l4_mword_t); +} + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() +{ + l4_umword_t label = 0; + addr_t rcv_cap_sel = _rcv_msg->rcv_cap_sel_base(); + for (int i = 0; i < Msgbuf_base::MAX_CAP_ARGS; i++) { + l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP; + rcv_cap_sel += L4_CAP_SIZE; + } + l4_utcb_br()->bdr = 0; + + l4_msgtag_t tag; + do { + tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER); + } while (ipc_error(tag, DEBUG_MSG)); + + /* copy message from the UTCBs message registers to the receive buffer */ + copy_utcb_to_msgbuf(tag, _rcv_msg); + + /* reset unmarshaller */ + _read_offset = sizeof(l4_mword_t); +} + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) +: + Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), + Native_capability(thread_get_my_native_id(), + Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE]), + _rcv_msg(rcv_msg) +{ + _read_offset = sizeof(l4_mword_t); +} + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() +{ + /* copy call message to the UTCBs message registers */ + l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst); + + addr_t rcv_cap_sel = _rcv_msg->rcv_cap_sel_base(); + for (int i = 0; i < Msgbuf_base::MAX_CAP_ARGS; i++) { + l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP; + rcv_cap_sel += L4_CAP_SIZE; + } + + tag = l4_ipc_call(_dst.dst(), l4_utcb(), tag, L4_IPC_NEVER); + if (l4_ipc_error(tag, l4_utcb()) == L4_IPC_RECANCELED) + throw Genode::Blocking_canceled(); + if (ipc_error(tag, DEBUG_MSG)) + throw Genode::Ipc_error(); + + /* copy request message from the UTCBs message registers */ + copy_utcb_to_msgbuf(tag, _rcv_msg); + + _write_offset = _read_offset = sizeof(umword_t); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, + Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) { } + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_prepare_next_reply_wait() +{ + /* now we have a request to reply */ + _reply_needed = true; + + /* leave space for return value at the beginning of the msgbuf */ + _write_offset = 2*sizeof(umword_t); + + /* receive buffer offset */ + _read_offset = sizeof(umword_t); +} + + +void Ipc_server::_wait() +{ + /* wait for new server request */ + try { Ipc_istream::_wait(); } catch (Blocking_canceled) { } + + /* we only have an unknown implicit reply capability */ + /* _dst = ???; */ + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() +{ + l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst); + tag = l4_ipc_send(L4_SYSF_REPLY, l4_utcb(), tag, L4_IPC_SEND_TIMEOUT_0); + if (ipc_error(tag, DEBUG_MSG)) + throw Ipc_error(); +} + + +void Ipc_server::_reply_wait() +{ + if (_reply_needed) { + l4_umword_t label; + addr_t rcv_cap_sel = _rcv_msg->rcv_cap_sel_base(); + for (int i = 0; i < Msgbuf_base::MAX_CAP_ARGS; i++) { + l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP; + rcv_cap_sel += L4_CAP_SIZE; + } + + l4_utcb_br()->bdr &= ~L4_BDR_OFFSET_MASK; + + l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst); + tag = l4_ipc_reply_and_wait(l4_utcb(), tag, &label, L4_IPC_SEND_TIMEOUT_0); + if (ipc_error(tag, false)) { + /* + * The error conditions could be a message cut (which + * we want to ignore on the server side) or a reply failure + * (for example, if the caller went dead during the call. + * In both cases, we do not reflect the error condition to + * the user but want to wait for the next proper incoming + * message. So let's just wait now. + */ + _wait(); + } else { + + /* copy request message from the UTCBs message registers */ + copy_utcb_to_msgbuf(tag, _rcv_msg); + } + } else + _wait(); + + /* reply capability is implicit in fiasco.oc and unknown to us */ + /* _dst = ???; */ + _prepare_next_reply_wait(); +} + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), + Ipc_ostream(Native_capability(), snd_msg), + _reply_needed(false) +{ } diff --git a/base-foc/src/base/ipc/pager.cc b/base-foc/src/base/ipc/pager.cc new file mode 100644 index 000000000..65e4b69a8 --- /dev/null +++ b/base-foc/src/base/ipc/pager.cc @@ -0,0 +1,110 @@ +/* + * \brief Pager support for Fiasco.OC + * \author Stefan Kalkowski + * \date 2011-01-11 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +namespace Fiasco { +#include +} + +using namespace Genode; +using namespace Fiasco; + +void Ipc_pager::_parse(unsigned long label) { + _badge = label & ~0x3; + _parse_msg_type(); + if (_type == PAGEFAULT || _type == EXCEPTION) + _parse_pagefault(); + if (_type == PAUSE || _type == EXCEPTION) + memcpy(&_regs, l4_utcb_exc(), sizeof(l4_exc_regs_t)); +} + + +void Ipc_pager::_parse_pagefault() +{ + if (_tag.is_exception()) { + _pf_addr = l4_utcb_exc_pfa(l4_utcb_exc()); + _pf_ip = l4_utcb_exc_pc(l4_utcb_exc()); + } else { + _pf_addr = l4_utcb_mr()->mr[0]; + _pf_ip = l4_utcb_mr()->mr[1]; + } +} + + +void Ipc_pager::_parse_msg_type() +{ + if (_tag.is_exception() && !l4_utcb_exc_is_pf(l4_utcb_exc())) { + _parse_exception(); + return; + } + + if (_tag.is_page_fault()) + _type = PAGEFAULT; + else { + _type = WAKE_UP; + _pf_ip = l4_utcb_mr()->mr[1]; + } +} + + +void Ipc_pager::wait_for_fault() +{ + l4_umword_t label; + + do { + _tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER); + int err = l4_ipc_error(_tag, l4_utcb()); + if (!err) { + _parse(label); + return; + } + PERR("Ipc error %d in pagefault from %lx", err, label & ~0x3); + } while (true); +} + + +void Ipc_pager::reply_and_wait_for_fault() +{ + l4_umword_t label; + l4_msgtag_t snd_tag = l4_msgtag(0, 0, 1, 0); + + l4_umword_t grant = _reply_mapping.grant() ? L4_MAP_ITEM_GRANT : 0; + l4_utcb_mr()->mr[0] = _reply_mapping.dst_addr() | L4_ITEM_MAP | grant; + l4_utcb_mr()->mr[1] = _reply_mapping.fpage().raw; + + _tag = l4_ipc_send_and_wait(_last, l4_utcb(), snd_tag, + &label, L4_IPC_SEND_TIMEOUT_0); + int err = l4_ipc_error(_tag, l4_utcb()); + if (err) { + PERR("Ipc error %d in pagefault from %lx", err, label & ~0x3); + wait_for_fault(); + } else + _parse(label); +} + + +void Ipc_pager::acknowledge_wakeup() +{ + l4_cap_idx_t dst = _last.valid() ? _last : L4_SYSF_REPLY; + + /* answer wakeup call from one of core's region-manager sessions */ + l4_ipc_send(dst, l4_utcb(), l4_msgtag(0, 0, 0, 0), L4_IPC_SEND_TIMEOUT_0); +} + + +Ipc_pager::Ipc_pager() +: Native_capability(Thread_base::myself()->tid(), 0), _badge(0) { } + diff --git a/base-foc/src/base/ipc/x86/pager_exception.cc b/base-foc/src/base/ipc/x86/pager_exception.cc new file mode 100644 index 000000000..2473e656f --- /dev/null +++ b/base-foc/src/base/ipc/x86/pager_exception.cc @@ -0,0 +1,29 @@ +/* + * \brief x86-specific pager support for Fiasco.OC + * \author Stefan Kalkowski + * \date 2011-08-24 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +namespace Fiasco { +#include +} + +enum Exceptions { EX_REGS = 0xff }; + + +void Genode::Ipc_pager::_parse_exception() +{ + if (Fiasco::l4_utcb_exc()->trapno == EX_REGS) + _type = PAUSE; + else + _type = EXCEPTION; +} diff --git a/base-foc/src/base/ipc/x86_32/pager.cc b/base-foc/src/base/ipc/x86_32/pager.cc new file mode 100644 index 000000000..cfed3982b --- /dev/null +++ b/base-foc/src/base/ipc/x86_32/pager.cc @@ -0,0 +1,35 @@ +/* + * \brief Fiasco.OC pager framework + * \author Stefan Kalkowski + * \date 2011-09-08 + * + * X86_32 specific parts, when handling cpu-exceptions. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + + +void Genode::Ipc_pager::copy_regs(Thread_state *state) +{ + state->ip = _regs.ip; + state->sp = _regs.sp; + state->edi = _regs.edi; + state->esi = _regs.esi; + state->ebp = _regs.ebp; + state->ebx = _regs.ebx; + state->edx = _regs.edx; + state->ecx = _regs.ecx; + state->eax = _regs.eax; + state->gs = _regs.gs; + state->fs = _regs.fs; + state->eflags = _regs.flags; + state->trapno = _regs.trapno; +} diff --git a/base-foc/src/base/ipc/x86_64/pager.cc b/base-foc/src/base/ipc/x86_64/pager.cc new file mode 100644 index 000000000..6e0379754 --- /dev/null +++ b/base-foc/src/base/ipc/x86_64/pager.cc @@ -0,0 +1,42 @@ +/* + * \brief Fiasco.OC pager framework + * \author Stefan Kalkowski + * \date 2011-09-08 + * + * X86_64 specific parts, when handling cpu-exceptions. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + + +void Genode::Ipc_pager::copy_regs(Thread_state *state) +{ + state->ip = _regs.ip; + state->sp = _regs.sp; + state->r8 = _regs.r8; + state->r9 = _regs.r9; + state->r10 = _regs.r10; + state->r11 = _regs.r11; + state->r12 = _regs.r12; + state->r13 = _regs.r13; + state->r14 = _regs.r14; + state->r15 = _regs.r15; + state->rax = _regs.rax; + state->rbx = _regs.rbx; + state->rcx = _regs.rcx; + state->rdx = _regs.rdx; + state->rdi = _regs.rdi; + state->rsi = _regs.rsi; + state->rbp = _regs.rbp; + state->ss = _regs.ss; + state->eflags = _regs.flags; + state->trapno = _regs.trapno; +} diff --git a/base-foc/src/base/lock/lock_helper.h b/base-foc/src/base/lock/lock_helper.h new file mode 100644 index 000000000..06bbd166d --- /dev/null +++ b/base-foc/src/base/lock/lock_helper.h @@ -0,0 +1,108 @@ +/* + * \brief Fiasco.OC-specific helper functions for the Lock implementation + * \author Stefan Kalkowski + * \author Norman Feske + * \date 2011-02-22 + * + * This file serves as adapter between the generic lock implementation + * in 'lock.cc' and the underlying kernel. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* Fiasco.OC includes */ +namespace Fiasco { +#include +#include +#include +} + +/** + * Resolve 'Thread_base::myself' when not linking the thread library + * + * This weak symbol is primarily used by test cases. Most other Genode programs + * use the thread library. If the thread library is not used, 'myself' can only + * be called by the main thread, for which 'myself' is defined as zero. + */ +Genode::Thread_base * __attribute__((weak)) Genode::Thread_base::myself() { return 0; } + +/** + * Yield CPU time + */ +static inline void thread_yield() { Fiasco::l4_thread_yield(); } + + +/** + * Custom ExchangeRegisters wrapper for waking up a thread + * + * When waking up an lock applicant, we need to make sure that the thread was + * stopped beforehand. Therefore, we evaluate the previous thread state as + * returned by the 'L4_ExchangeRegisters' call. + * + * \return true if the thread was in blocking state + */ +static inline bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + using namespace Fiasco; + + Genode::Native_thread_id irq = tid + Fiasco_capability::THREAD_IRQ_CAP; + l4_irq_trigger(irq); + return true; +} + + +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + using namespace Fiasco; + + Genode::Thread_base *myself = Genode::Thread_base::myself(); + return myself ? myself->tid() : Fiasco_capability::MAIN_THREAD_CAP; +} + + +static inline Genode::Native_thread_id thread_invalid_id() +{ + return Genode::Native_thread(); +} + + +/** + * Check if a native thread ID is initialized + * + * \return true if ID is initialized + */ +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return tid.valid(); +} + + +/** + * Yield CPU time to the specified thread + */ +static inline void thread_switch_to(Genode::Native_thread_id tid) +{ + Fiasco::l4_thread_switch(tid); +} + + +/** + * Unconditionally block the calling thread + */ +static inline void thread_stop_myself() +{ + using namespace Fiasco; + + Genode::Native_thread_id irq = thread_get_my_native_id() + + Fiasco_capability::THREAD_IRQ_CAP; + l4_irq_receive(irq, L4_IPC_NEVER); +} diff --git a/base-foc/src/base/pager/pager.cc b/base-foc/src/base/pager/pager.cc new file mode 100644 index 000000000..fc4f7a12c --- /dev/null +++ b/base-foc/src/base/pager/pager.cc @@ -0,0 +1,183 @@ +/* + * \brief Fiasco pager framework + * \author Norman Feske + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-07-14 + * + * FIXME Isn't this file generic? + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* Fiasco.OC includes */ +namespace Fiasco { +#include +#include +} + +using namespace Genode; + + +/********************** + ** Pager activation ** + **********************/ + +void Pager_activation_base::entry() +{ + Ipc_pager pager; + _cap = Native_capability(Thread_base::_thread_cap); + _cap_valid.unlock(); + + bool reply_pending = false; + while (1) { + + if (reply_pending) + pager.reply_and_wait_for_fault(); + else + pager.wait_for_fault(); + + reply_pending = false; + + if (!_ep) { + PWRN("Pager entrypoint not yet defined"); + continue; + } + + switch (pager.msg_type()) { + + case Ipc_pager::PAGEFAULT: + case Ipc_pager::EXCEPTION: + { + /* lookup referenced object */ + Pager_object *obj = _ep->obj_by_id(pager.badge()); + + if (pager.is_exception()) { + Lock::Guard guard(obj->state.lock); + pager.copy_regs(&obj->state); + obj->state.exceptions++; + obj->state.in_exception = true; + obj->submit_exception_signal(); + continue; + } + + /* handle request */ + if (obj->pager(pager)) { + /* could not resolv - leave thread in pagefault */ + PDBG("Could not resolve pf=%p ip=%p", + (void*)pager.fault_addr(), (void*)pager.fault_ip()); + } else { + pager.set_reply_dst(Native_capability(obj->badge(),0)); + reply_pending = true; + continue; + } + break; + } + + case Ipc_pager::WAKE_UP: + { + /* + * We got a request from one of cores region-manager sessions + * to answer the pending page fault of a resolved region-manager + * client, or to resume a previously paused thread. Hence, we + * have to send a reply to the specified thread and answer the + * call. + */ + Pager_object *obj = _ep->obj_by_id(pager.badge()); + if (!obj) { + PWRN("Got illegal wake-up message from %lx", pager.badge()); + continue; + } + + /* send reply to the caller */ + pager.set_reply_dst(Native_capability()); + pager.acknowledge_wakeup(); + + /* revert exception flag */ + { + Lock::Guard guard(obj->state.lock); + obj->state.in_exception = false; + } + + /* send wake up message to requested thread */ + pager.set_reply_dst(Native_capability(obj->badge(),0)); + pager.acknowledge_wakeup(); + break; + } + + /* + * Handle exceptions that are artificially generated by the pause + * function of the CPU service. + */ + case Ipc_pager::PAUSE: + { + Pager_object *obj = _ep->obj_by_id(pager.badge()); + + Lock::Guard guard(obj->state.lock); + pager.copy_regs(&obj->state); + + obj->state.exceptions++; + obj->state.in_exception = true; + + /* + * It might occur, that the thread raises an exception, + * after it already got resumed by the cpu_session, in + * that case we unblock it immediately. + */ + if (!obj->state.paused) { + pager.set_reply_dst(Native_capability(obj->badge(),0)); + reply_pending = true; + } + break; + } + + default: + PERR("Got unknown message type %x!", pager.msg_type()); + } + }; +} + + +/********************** + ** Pager entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *cap_session, + Pager_activation_base *a) +: _activation(a), _cap_session(cap_session) +{ + _activation->ep(this); +} + + +void Pager_entrypoint::dissolve(Pager_object *obj) { remove(obj); } + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + using namespace Fiasco; + + /* return invalid capability if no activation is present */ + if (!_activation) return Pager_capability(); + + Native_capability c = _activation->cap(); + Native_capability cap(_cap_session->alloc(c)); + + /* add server object to object pool */ + obj->cap(cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return reinterpret_cap_cast(cap); +} diff --git a/base-foc/src/base/server/server.cc b/base-foc/src/base/server/server.cc new file mode 100644 index 000000000..020bd4c6a --- /dev/null +++ b/base-foc/src/base/server/server.cc @@ -0,0 +1,39 @@ +/* + * \brief Default version of platform-specific part of server framework + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2006-05-12 + * + * This version is suitable for platforms similar to L4. Each platform + * for which this implementation is not suited contains a platform- + * specific version in its respective 'base-' repository. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +using namespace Genode; + + +/*********************** + ** Server entrypoint ** + ***********************/ + +Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj) +{ + Untyped_capability new_obj_cap = _cap_session->alloc(_cap); + + /* add server object to object pool */ + obj->cap(new_obj_cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return new_obj_cap; +} diff --git a/base-foc/src/base/thread/thread.cc b/base-foc/src/base/thread/thread.cc new file mode 100644 index 000000000..a9ab1a7d4 --- /dev/null +++ b/base-foc/src/base/thread/thread.cc @@ -0,0 +1,200 @@ +/* + * \brief Implementation of the Thread API + * \author Norman Feske + * \date 2010-01-11 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +namespace Fiasco { +#include +} + +using namespace Genode; + + +/** + * Return the managed dataspace holding the thread context area + * + * This function is provided by the process environment. + */ +namespace Genode { + Rm_session *env_context_area_rm_session(); + Ram_session *env_context_area_ram_session(); +} + + +/****************************** + ** Thread-context allocator ** + ******************************/ + +Thread_base::Context *Thread_base::Context_allocator::base_to_context(addr_t base) +{ + addr_t result = base + CONTEXT_VIRTUAL_SIZE - sizeof(Context); + return reinterpret_cast(result); +} + + +addr_t Thread_base::Context_allocator::addr_to_base(void *addr) +{ + return ((addr_t)addr) & CONTEXT_VIRTUAL_BASE_MASK; +} + + +bool Thread_base::Context_allocator::_is_in_use(addr_t base) +{ + List_element *le = _threads.first(); + for (; le; le = le->next()) + if (base_to_context(base) == le->object()->_context) + return true; + + return false; +} + + +Thread_base::Context *Thread_base::Context_allocator::alloc(Thread_base *thread_base) +{ + Lock::Guard _lock_guard(_threads_lock); + + /* + * Find slot in context area for the new context + */ + addr_t base = CONTEXT_AREA_VIRTUAL_BASE; + for (; _is_in_use(base); base += CONTEXT_VIRTUAL_SIZE) { + + /* check upper bound of context area */ + if (base >= CONTEXT_AREA_VIRTUAL_BASE + CONTEXT_AREA_VIRTUAL_SIZE) + return 0; + } + + _threads.insert(&thread_base->_list_element); + + return base_to_context(base); +} + + +void Thread_base::Context_allocator::free(Thread_base *thread_base) +{ + Lock::Guard _lock_guard(_threads_lock); + + _threads.remove(&thread_base->_list_element); +} + + +/***************** + ** Thread base ** + *****************/ + +Thread_base::Context_allocator *Thread_base::_context_allocator() +{ + static Context_allocator context_allocator_inst; + return &context_allocator_inst; +} + + +Thread_base::Context *Thread_base::_alloc_context(size_t stack_size) +{ + /* + * Synchronize context list when creating new threads from multiple threads + * + * XXX: remove interim fix + */ + static Lock alloc_lock; + Lock::Guard _lock_guard(alloc_lock); + + /* allocate thread context */ + Context *context = _context_allocator()->alloc(this); + if (!context) + throw Context_alloc_failed(); + + /* determine size of dataspace to allocate for context members and stack */ + enum { PAGE_SIZE_LOG2 = 12 }; + size_t ds_size = align_addr(stack_size, PAGE_SIZE_LOG2); + + if (stack_size >= CONTEXT_VIRTUAL_SIZE - sizeof(Native_utcb) - (1 << PAGE_SIZE_LOG2)) + throw Stack_too_large(); + + /* + * Calculate base address of the stack + * + * The stack is always located at the top of the context. + */ + addr_t ds_addr = Context_allocator::addr_to_base(context) + CONTEXT_VIRTUAL_SIZE + - ds_size; + + /* add padding for UTCB if defined for the platform */ + if (sizeof(Native_utcb) >= (1 << PAGE_SIZE_LOG2)) + ds_addr -= sizeof(Native_utcb); + + /* allocate and attach backing store for the stack */ + Ram_dataspace_capability ds_cap; + try { + ds_cap = env_context_area_ram_session()->alloc(ds_size); + addr_t attach_addr = ds_addr - CONTEXT_AREA_VIRTUAL_BASE; + env_context_area_rm_session()->attach_at(ds_cap, attach_addr, ds_size); + + } catch (Ram_session::Alloc_failed) { + throw Stack_alloc_failed(); + } + + /* + * Now the thread context is backed by memory, so it is safe to access its + * members. + */ + + context->thread_base = this; + context->stack_base = ds_addr; + context->ds_cap = ds_cap; + return context; +} + + +void Thread_base::_free_context() +{ + addr_t ds_addr = _context->stack_base - CONTEXT_AREA_VIRTUAL_BASE; + Ram_dataspace_capability ds_cap = _context->ds_cap; + Genode::env_context_area_rm_session()->detach((void *)ds_addr); + Genode::env_context_area_ram_session()->free(ds_cap); + _context_allocator()->free(this); +} + + +void Thread_base::name(char *dst, size_t dst_len) +{ + snprintf(dst, min(dst_len, (size_t)Context::NAME_LEN), _context->name); +} + + +Thread_base *Thread_base::myself() { + using namespace Fiasco; + + return reinterpret_cast(l4_utcb_tcr()->user[UTCB_TCR_THREAD_OBJ]); +} + + +Thread_base::Thread_base(const char *name, size_t stack_size) +: + _list_element(this), + _context(_alloc_context(stack_size)) +{ + strncpy(_context->name, name, sizeof(_context->name)); + _init_platform_thread(); +} + + +Thread_base::~Thread_base() +{ + _deinit_platform_thread(); + _free_context(); +} diff --git a/base-foc/src/base/thread/thread_bootstrap.cc b/base-foc/src/base/thread/thread_bootstrap.cc new file mode 100644 index 000000000..a664e3b37 --- /dev/null +++ b/base-foc/src/base/thread/thread_bootstrap.cc @@ -0,0 +1,51 @@ +/* + * \brief Fiasco.OC specific thread bootstrap code + * \author Stefan Kalkowski + * \date 2011-01-20 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include + +namespace Fiasco { +#include +} + +void Genode::Thread_base::_thread_bootstrap() +{ + using namespace Genode; + using namespace Fiasco; + + /* first, receive my own gate-capability and badge from starter thread */ + addr_t thread_base = 0; + unsigned long my_badge = 0; + Msgbuf<128> snd_msg, rcv_msg; + Ipc_server srv(&snd_msg, &rcv_msg); + srv >> IPC_WAIT >> thread_base >> my_badge << IPC_REPLY; + + /* store both values in user-defined section of the UTCB */ + l4_utcb_tcr()->user[UTCB_TCR_BADGE] = my_badge; + l4_utcb_tcr()->user[UTCB_TCR_THREAD_OBJ] = thread_base; +} + + +void Genode::Thread_base::_thread_start() +{ + using namespace Genode; + + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + sleep_forever(); +} + + +void Genode::Thread_base::_init_platform_thread() { } diff --git a/base-foc/src/base/thread/thread_start.cc b/base-foc/src/base/thread/thread_start.cc new file mode 100644 index 000000000..d5c578cde --- /dev/null +++ b/base-foc/src/base/thread/thread_start.cc @@ -0,0 +1,67 @@ +/* + * \brief Fiasco-specific implementation of the non-core startup Thread API + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +using namespace Genode; + + +void Thread_base::_deinit_platform_thread() +{ + env()->cpu_session()->kill_thread(_thread_cap); +} + + +void Thread_base::start() +{ + /* create thread at core */ + char buf[48]; + name(buf, sizeof(buf)); + _thread_cap = env()->cpu_session()->create_thread(buf); + + /* assign thread to protection domain */ + env()->pd_session()->bind_thread(_thread_cap); + + /* create new pager object and assign it to the new thread */ + Pager_capability pager_cap = env()->rm_session()->add_client(_thread_cap); + env()->cpu_session()->set_pager(_thread_cap, pager_cap); + + /* register initial IP and SP at core */ + addr_t thread_sp = (addr_t)&_context->stack[-4]; + thread_sp &= ~0xf; /* align initial stack to 16 byte boundary */ + env()->cpu_session()->start(_thread_cap, (addr_t)_thread_start, thread_sp); + + /* get gate-capability and badge of new thread */ + Thread_state state; + env()->cpu_session()->state(_thread_cap, &state); + _tid = state.cap.dst(); + + /* + * send newly constructed thread, pointer to its Thread_base object, + * and its badge + */ + Msgbuf<128> snd_msg, rcv_msg; + Ipc_client cli(state.cap, &snd_msg, &rcv_msg); + cli << (addr_t)this << state.cap.local_name() << IPC_CALL; +} + + +void Thread_base::cancel_blocking() +{ + env()->cpu_session()->cancel_blocking(_thread_cap); +} diff --git a/base-foc/src/bootstrap/target.mk b/base-foc/src/bootstrap/target.mk new file mode 100644 index 000000000..de8e348be --- /dev/null +++ b/base-foc/src/bootstrap/target.mk @@ -0,0 +1,5 @@ +TARGET = bootstrap +PKGS = drivers-frst/of drivers-frst/uart bootstrap +LIBS = l4re_support + +include $(REP_DIR)/mk/l4_pkg.mk diff --git a/base-foc/src/core/arm/platform_arm.cc b/base-foc/src/core/arm/platform_arm.cc new file mode 100644 index 000000000..bb5f8dc08 --- /dev/null +++ b/base-foc/src/core/arm/platform_arm.cc @@ -0,0 +1,16 @@ +/* + * \brief Platform support specific to ARM + * \author Norman Feske + * \date 2011-05-02 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +void Genode::Platform::_setup_io_port_alloc() { } diff --git a/base-foc/src/core/arm/platform_thread.cc b/base-foc/src/core/arm/platform_thread.cc new file mode 100644 index 000000000..5959c5b94 --- /dev/null +++ b/base-foc/src/core/arm/platform_thread.cc @@ -0,0 +1,25 @@ +/* + * \brief Fiasco.OC thread facility (arm specifics) + * \author Stefan Kalkowski + * \date 2011-09-08 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + + +bool Genode::Platform_thread::_in_syscall(Fiasco::l4_umword_t flags) +{ + return flags == 0; +} diff --git a/base-foc/src/core/arm/target.mk b/base-foc/src/core/arm/target.mk new file mode 100644 index 000000000..89dee1de3 --- /dev/null +++ b/base-foc/src/core/arm/target.mk @@ -0,0 +1,7 @@ +include $(PRG_DIR)/../target.inc + +REQUIRES += arm +SRC_CC += arm/platform_arm.cc arm/platform_thread.cc + +vpath io_port_session_component.cc $(GEN_CORE_DIR)/arm + diff --git a/base-foc/src/core/cap_session_component.cc b/base-foc/src/core/cap_session_component.cc new file mode 100644 index 000000000..2991411a7 --- /dev/null +++ b/base-foc/src/core/cap_session_component.cc @@ -0,0 +1,230 @@ +/* + * \brief Fiasco.oc platform-specific capability allocation + * \author Stefan Kalkowski + * \date 2011-01-13 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +namespace Fiasco { +#include +#include +#include +#include +} + +using namespace Genode; + + +/***************************** + ** Cap_session_component ** + *****************************/ + +Native_capability Cap_session_component::alloc(Cap_session_component *session, + Native_capability ep) +{ + Native_capability cap; + + if (!ep.valid()) { + PWRN("Invalid cap!"); + return Native_capability(); + } + + /* + * maybe someone tries to fool us, proof whether cap exists in cap tree. + * + * Actually we should proof whether both capability selectors + * point to the same object, but this isn't possible in fiasco.oc. + */ + Capability_node *n = Capability_tree::tree()->find_by_badge(ep.local_name()); + if (!n) { + PWRN("Unknown capability!"); + return cap; + } + + + try { + using namespace Fiasco; + + /* + * Allocate new badge, and ipc-gate and set badge as gate-label + */ + unsigned long badge = Badge_allocator::allocator()->alloc(); + Native_thread gate = Capability_allocator::allocator()->alloc(); + l4_msgtag_t tag = l4_factory_create_gate(L4_BASE_FACTORY_CAP, + gate, + n->pt()->native_thread(), + badge); + if (l4_msgtag_has_error(tag)) { + PERR("l4_factory_create_gate failed!"); + Capability_allocator::allocator()->free(gate); + Badge_allocator::allocator()->free(badge); + return cap; + } else + /* set debugger-name of ipc-gate to thread's name */ + Fiasco::l4_debugger_set_object_name(gate, n->pt()->name()); + + /* + * Create new node in capability tree. + * + * TODO: don't use core_mem_alloc, but a session-specific allocator + */ + Capability_node *new_node = new (platform()->core_mem_alloc()) + Capability_node(badge, session, n->pt(), gate); + Capability_tree::tree()->insert(new_node); + cap = Native_capability(gate, badge); + + } catch (Badge_allocator::Out_of_badges) {} + + return cap; +} + + +Native_capability Cap_session_component::alloc(Native_capability ep) +{ + return Cap_session_component::alloc(this, ep); +} + + +void Cap_session_component::free(Native_capability cap) +{ + using namespace Fiasco; + + Capability_node *n = Capability_tree::tree()->find_by_badge(cap.local_name()); + if (!n) + return; + + /* + * check whether this cap_session has created the capability to delete. + * + * Actually we should proof whether both capability selectors + * point to the same object, but this isn't possible in fiasco.oc. + */ + if (n->cap_session() != this) + return; + + Capability_tree::tree()->remove(n); + Badge_allocator::allocator()->free(n->badge()); + l4_msgtag_t tag = l4_task_unmap(L4_BASE_TASK_CAP, + l4_obj_fpage(cap.dst(), 0, 0), + L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ); + if (l4_msgtag_has_error(tag)) + PERR("destruction of ipc-gate %lx failed!", (unsigned long) cap.dst()); + + /* free explicilty allocated cap-selector */ + Capability_allocator::allocator()->free(n->gate()); + + destroy(platform_specific()->core_mem_alloc(), n); +} + + +/*********************** + ** Badge Allocator ** + ***********************/ + +Badge_allocator::Badge_allocator() +: _id_alloc(platform_specific()->core_mem_alloc()) +{ + _id_alloc.add_range(BADGE_OFFSET, BADGE_RANGE); +} + + +unsigned long Badge_allocator::alloc() +{ + Lock::Guard lock_guard(_lock); + + void *badge; + if (_id_alloc.alloc(BADGE_OFFSET, &badge)) + return (unsigned long) badge; + throw Out_of_badges(); +} + + +void Badge_allocator::free(unsigned long badge) +{ + Lock::Guard lock_guard(_lock); + + if (badge < BADGE_RANGE) + _id_alloc.free((void*)(badge & BADGE_MASK), BADGE_OFFSET); +} + + +Badge_allocator* Badge_allocator::allocator() +{ + static Badge_allocator alloc; + return &alloc; +} + + +/*********************** + ** Capability_node ** + ***********************/ + +Capability_node::Capability_node(unsigned long badge, + Cap_session_component *cap_session, + Platform_thread *pt, + Native_thread gate) +: _badge(badge), _cap_session(cap_session), _pt(pt), _gate(gate) {} + + +bool Capability_node::higher(Capability_node *n) +{ + return n->_badge > _badge; +} + + +Capability_node* Capability_node::find_by_badge(unsigned long badge) +{ + if (_badge == badge) return this; + + Capability_node *n = Avl_node::child(badge > _badge); + return n ? n->find_by_badge(badge) : 0; +} + + +/*********************** + ** Capability_tree ** + ***********************/ + +void Capability_tree::insert(Avl_node *node) +{ + Lock::Guard lock_guard(_lock); + + Avl_tree::insert(node); +} + + +void Capability_tree::remove(Avl_node *node) +{ + Lock::Guard lock_guard(_lock); + + Avl_tree::remove(node); +} + + +Capability_node* Capability_tree::find_by_badge(unsigned long badge) +{ + Lock::Guard lock_guard(_lock); + + return first()->find_by_badge(badge); +} + + +Capability_tree* Capability_tree::tree() +{ + static Capability_tree _tree; + return &_tree; +} diff --git a/base-foc/src/core/cpu_session_extension.cc b/base-foc/src/core/cpu_session_extension.cc new file mode 100644 index 000000000..466aa1310 --- /dev/null +++ b/base-foc/src/core/cpu_session_extension.cc @@ -0,0 +1,87 @@ +/** + * \brief Core implementation of the CPU session interface extension + * \author Stefan Kalkowski + * \date 2011-04-04 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* Core includes */ +#include + +/* Fiasco.OC includes */ +namespace Fiasco { +#include +#include +} + + +void Genode::Cpu_session_component::enable_vcpu(Genode::Thread_capability thread_cap, + Genode::addr_t vcpu_state) +{ + using namespace Genode; + using namespace Fiasco; + + Lock::Guard lock_guard(_thread_list_lock); + + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return; + + Native_thread tid = thread->platform_thread()->native_thread(); + + l4_msgtag_t tag = l4_thread_vcpu_control(tid, vcpu_state); + if (l4_msgtag_has_error(tag)) + PWRN("l4_thread_vcpu_control failed"); +} + + +Genode::Native_capability +Genode::Cpu_session_component::native_cap(Genode::Thread_capability cap) +{ + using namespace Genode; + + Lock::Guard lock_guard(_thread_list_lock); + + Cpu_thread_component *thread = _lookup_thread(cap); + if (!thread) return Native_capability(); + + return Native_capability(thread->platform_thread()->native_thread(), -1); +} + + +Genode::Native_capability Genode::Cpu_session_component::alloc_irq() +{ + using namespace Fiasco; + + Fiasco_capability irq_cap(Genode::Capability_allocator::allocator()->alloc()); + l4_msgtag_t res = l4_factory_create_irq(L4_BASE_FACTORY_CAP, irq_cap); + if (l4_error(res)) + PWRN("Allocation of irq object failed!"); + return Genode::Native_capability(irq_cap, -1); +} + + +void Genode::Cpu_session_component::single_step(Genode::Thread_capability thread_cap, bool enable) +{ + using namespace Genode; + + Lock::Guard lock_guard(_thread_list_lock); + + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return; + + Native_thread tid = thread->platform_thread()->native_thread(); + + enum { THREAD_SINGLE_STEP = 0x40000 }; + int flags = enable ? THREAD_SINGLE_STEP : 0; + + Fiasco::l4_thread_ex_regs(tid, ~0UL, ~0UL, flags); +} diff --git a/base-foc/src/core/include/cap_session_component.h b/base-foc/src/core/include/cap_session_component.h new file mode 100644 index 000000000..995cf9d37 --- /dev/null +++ b/base-foc/src/core/include/cap_session_component.h @@ -0,0 +1,118 @@ +/* + * \brief Capability allocation service + * \author Stefan Kalkowski + * \date 2011-01-13 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +namespace Genode { + + class Cap_session_component : public Rpc_object + { + private: + + static long _unique_id_cnt; /* TODO: remove this from generic core code */ + + public: + + Native_capability alloc(Native_capability ep); + + void free(Native_capability cap); + + static Native_capability alloc(Cap_session_component *session, + Native_capability ep); + }; + + + class Badge_allocator + { + private: + + enum { + BADGE_RANGE = ~0UL, + BADGE_MASK = ~3UL, + BADGE_NUM_MAX = BADGE_MASK >> 2, + BADGE_OFFSET = 1 << 2 + }; + + Synchronized_range_allocator _id_alloc; + Lock _lock; + + Badge_allocator(); + + public: + + class Out_of_badges : Exception {}; + + unsigned long alloc(); + void free(unsigned long badge); + + static Badge_allocator* allocator(); + }; + + + class Platform_thread; + class Capability_node : public Avl_node + { + private: + + unsigned long _badge; + Cap_session_component *_cap_session; + Platform_thread *_pt; + Native_thread _gate; + + public: + + Capability_node(unsigned long badge, + Cap_session_component *cap_session, + Platform_thread *pt, + Native_thread gate); + + bool higher(Capability_node *n); + + Capability_node *find_by_badge(unsigned long badge); + + unsigned long badge() { return _badge; } + Cap_session_component *cap_session() { return _cap_session; } + Platform_thread *pt() { return _pt; } + Native_thread gate() { return _gate; } + }; + + + class Capability_tree : public Avl_tree + { + private: + + Lock _lock; + + Capability_tree() {} + + public: + + void insert(Avl_node *node); + + void remove(Avl_node *node); + + Capability_node *find_by_badge(unsigned long badge); + + static Capability_tree* tree(); + }; +} + +#endif /* _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ */ diff --git a/base-foc/src/core/include/cpu_session_component.h b/base-foc/src/core/include/cpu_session_component.h new file mode 100644 index 000000000..7ed3cfff4 --- /dev/null +++ b/base-foc/src/core/include/cpu_session_component.h @@ -0,0 +1,155 @@ +/* + * \brief Core-specific instance of the CPU session/thread interfaces + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + /** + * RPC interface of CPU thread + * + * We make 'Cpu_thread' a RPC object only to be able to lookup CPU threads + * from thread capabilities supplied as arguments to CPU-session functions. + * A CPU thread does not provide an actual RPC interface. + */ + struct Cpu_thread + { + GENODE_RPC_INTERFACE(); + }; + + + class Cpu_thread_component : public Rpc_object, + public List::Element + { + private: + + Platform_thread _platform_thread; + + bool _bound; /* pd binding flag */ + + public: + + Cpu_thread_component(const char *name, unsigned priority) + : _platform_thread(name, priority), _bound(false) { } + + + /************************ + ** Accessor functions ** + ************************/ + + inline Platform_thread * platform_thread() { return &_platform_thread; } + inline bool bound() const { return _bound; } + inline void bound(bool b) { _bound = b; } + }; + + + class Cpu_session_component : public Rpc_object + { + private: + + /** + * Allocator used for managing the CPU threads associated with the + * CPU session + */ + typedef Tslab Cpu_thread_allocator; + + Rpc_entrypoint *_thread_ep; + Pager_entrypoint *_pager_ep; + Allocator_guard _md_alloc; /* guarded meta-data allocator */ + Cpu_thread_allocator _slab; /* meta-data allocator */ + Lock _slab_lock; /* protect slab access */ + List _thread_list; + Lock _thread_list_lock; /* protect thread list */ + unsigned _priority; /* priority of threads + created with this + session */ + + /** + * Lookup thread in CPU session by its capability + * + * \retval NULL thread capability is invalid or + * does not belong to the CPU session + */ + Cpu_thread_component *_lookup_thread(Thread_capability thread) { + return dynamic_cast + (_thread_ep->obj_by_cap(thread)); } + + /** + * Raw thread-killing functionality + * + * This function is called from the 'kill_thread' function and + * the destructor. Each these functions grab the list lock + * by themselves and call this function to perform the actual + * killing. + */ + void _unsynchronized_kill_thread(Cpu_thread_component *thread); + + public: + + /** + * Constructor + */ + Cpu_session_component(Rpc_entrypoint *thread_ep, + Pager_entrypoint *pager_ep, + Allocator *md_alloc, const char *args); + + /** + * Destructor + */ + ~Cpu_session_component(); + + + /*************************** + ** CPU session interface ** + ***************************/ + + Thread_capability create_thread(Name const &); + void kill_thread(Thread_capability); + Thread_capability first(); + Thread_capability next(Thread_capability); + int set_pager(Thread_capability, Pager_capability); + int start(Thread_capability, addr_t, addr_t); + void pause(Thread_capability thread_cap); + void resume(Thread_capability thread_cap); + void single_step(Thread_capability thread_cap, bool enable); + void cancel_blocking(Thread_capability); + int name(Thread_capability, char *, size_t); + int state(Thread_capability, Thread_state *); + void exception_handler(Thread_capability, Signal_context_capability); + + + /*********************************** + ** Fiasco.OC specific extensions ** + ***********************************/ + + void enable_vcpu(Thread_capability, addr_t); + Native_capability native_cap(Thread_capability); + Native_capability alloc_irq(); + }; +} + +#endif /* _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ */ diff --git a/base-foc/src/core/include/irq_session_component.h b/base-foc/src/core/include/irq_session_component.h new file mode 100644 index 000000000..728d6eab9 --- /dev/null +++ b/base-foc/src/core/include/irq_session_component.h @@ -0,0 +1,113 @@ +/* + * \brief IRQ session interface for Fiasco.OC + * \author Stefan Kalkowski + * \date 2011-01-28 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ + +#include +#include +#include +#include + +#include + +namespace Genode { + + class Irq_session_component : public Rpc_object, + public List::Element + { + private: + + class Interrupt : public Avl_node + { + private: + + Native_thread _cap; + Semaphore _sem; + + public: + + unsigned number; + + Interrupt(); + + bool higher(Interrupt *n); + Interrupt* find_by_num(unsigned num); + + Native_thread capability() { return _cap; } + Semaphore* semaphore() { return &_sem; } + }; + + + class Interrupt_handler : public Thread<4096> + { + private: + + Interrupt_handler() { start(); } + + void entry(); + + public: + + static Native_thread handler_cap(); + }; + + + Interrupt _irq; + Range_allocator *_irq_alloc; + + /* + * Each IRQ session uses a dedicated server activation + */ + enum { STACK_SIZE = 2048 }; + Rpc_entrypoint _ep; + + Irq_session_capability _irq_cap; + + static Avl_tree* _irqs(); + + public: + + /** + * Constructor + * + * \param cap_session capability session to use + * \param irq_alloc platform-dependent IRQ allocator + * \param args session construction arguments + */ + Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args); + + /** + * Destructor + */ + ~Irq_session_component(); + + /** + * Return capability to this session + * + * If an initialization error occurs, returned capability is invalid. + */ + Irq_session_capability cap() const { return _irq_cap; } + + + /*************************** + ** Irq session interface ** + ***************************/ + + void wait_for_irq(); + }; +} + +#endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */ diff --git a/base-foc/src/core/include/map_local.h b/base-foc/src/core/include/map_local.h new file mode 100644 index 000000000..73cee97fd --- /dev/null +++ b/base-foc/src/core/include/map_local.h @@ -0,0 +1,66 @@ +/* + * \brief Core-local mapping + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2010-02-15 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__MAP_LOCAL_H_ +#define _CORE__INCLUDE__MAP_LOCAL_H_ + +/* core includes */ +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +namespace Genode { + + /** + * Map page locally within core + * + * On Fiasco, all mapping originate from virtual addresses. At startup, + * core obtains the whole memory sigma0 in a one-to-one fashion. Hence, + * core-local addresses normally correspond to physical addresses. + * + * \param from_addr core-virtual source address + * \param to_addr core-virtual destination address + * \param num_pages number of pages to remap + */ + inline bool map_local(addr_t from_addr, addr_t to_addr, size_t num_pages) + { + addr_t offset = 0; + size_t page_size = get_page_size(); + size_t page_size_log2 = get_page_size_log2(); + for (unsigned i = 0; i < num_pages; i++, offset += page_size) { + + using namespace Fiasco; + + l4_fpage_t snd_fpage = l4_fpage(from_addr + offset, + page_size_log2, L4_FPAGE_RW); + + if (l4_msgtag_has_error(l4_task_map(L4_BASE_TASK_CAP, + L4_BASE_TASK_CAP, + snd_fpage, + to_addr + offset))) { + PWRN("could not locally remap 0x%lx to 0x%lx", from_addr, to_addr); + return false; + } + } + + return true; + } +} + +#endif /* _CORE__INCLUDE__MAP_LOCAL_H_ */ + diff --git a/base-foc/src/core/include/pd_session_component.h b/base-foc/src/core/include/pd_session_component.h new file mode 100644 index 000000000..2f0490b1f --- /dev/null +++ b/base-foc/src/core/include/pd_session_component.h @@ -0,0 +1,57 @@ +/* + * \brief Core-specific instance of the PD session interface + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Pd_session_component : public Rpc_object + { + private: + + Platform_pd _pd; + Parent_capability _parent; + Rpc_entrypoint *_thread_ep; + + public: + + Pd_session_component(Rpc_entrypoint *thread_ep, const char *args) + : _thread_ep(thread_ep) { } + + + /************************** + ** PD session interface ** + **************************/ + + int bind_thread(Thread_capability); + int assign_parent(Parent_capability); + + + /********************************** + ** Fiasco.OC specific functions ** + **********************************/ + + Native_capability task_cap(); + }; +} + +#endif /* _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ */ diff --git a/base-foc/src/core/include/platform.h b/base-foc/src/core/include/platform.h new file mode 100644 index 000000000..7052fb68a --- /dev/null +++ b/base-foc/src/core/include/platform.h @@ -0,0 +1,157 @@ +/* + * \brief Fiasco platform + * \author Christian Helmuth + * \author Norman Feske + * \date 2007-09-10 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_H_ +#define _CORE__INCLUDE__PLATFORM_H_ + +#include +#include + +#include "platform_generic.h" +#include "platform_thread.h" +#include "platform_pd.h" +#include "multiboot.h" + + +namespace Genode { + + class Platform : public Platform_generic + { + private: + + /* + * Shortcut for the type of allocator instances for physical resources + */ + typedef Synchronized_range_allocator Phys_allocator; + + Platform_pd *_core_pd; /* core protection domain object */ + Phys_allocator _ram_alloc; /* RAM allocator */ + Phys_allocator _io_mem_alloc; /* MMIO allocator */ + Phys_allocator _io_port_alloc; /* I/O port allocator */ + Phys_allocator _irq_alloc; /* IRQ allocator */ + Phys_allocator _region_alloc; /* virtual memory allocator for core */ + Multiboot_info _mb_info; /* multiboot information */ + Rom_fs _rom_fs; /* ROM file system */ + Rom_module _kip_rom; /* ROM module for Fiasco KIP */ + + addr_t _vm_start; /* begin of virtual memory */ + size_t _vm_size; /* size of virtual memory */ + + /* + * We do not export any boot module loaded before FIRST_ROM. + */ + enum { FIRST_ROM = 3 }; + + /** + * Setup base resources + * + * - Map and provide KIP as ROM module + * - Initializes region allocator + * - Initializes multiboot info structure + */ + void _setup_basics(); + + /** + * Setup RAM, IO_MEM, and region allocators + */ + void _setup_mem_alloc(); + + /** + * Setup I/O port space allocator + */ + void _setup_io_port_alloc(); + + /** + * Setup IRQ allocator + */ + void _setup_irq_alloc(); + + /** + * Parse multi-boot information and update ROM database + */ + void _setup_rom(); + + /** + * Setup pager for core-internal threads + */ + void _setup_core_pager(); + + public: + + /** + * Pager object representing the pager of core namely sigma0 + */ + struct Sigma0 : public Pager_object + { + /** + * Constructor + */ + Sigma0(); + + int pager(Ipc_pager &ps) { /* never called */ return -1; } + }; + + /** + * Return singleton instance of Sigma0 pager object + */ + static Sigma0 *sigma0(); + + /** + * Core pager thread that handles core-internal page-faults + */ + struct Core_pager : public Platform_thread, public Pager_object + { + /** + * Constructor + */ + Core_pager(Platform_pd *core_pd); + + int pager(Ipc_pager &ps) { /* never called */ return -1; } + }; + + /** + * Return singleton instance of core pager object + */ + Core_pager *core_pager(); + + /** + * Constructor + */ + Platform(); + + /** + * Accessor for core pd object + */ + Platform_pd *core_pd() { return _core_pd; } + + + /******************************** + ** Generic platform interface ** + ********************************/ + + Allocator *core_mem_alloc() { return &_ram_alloc; } + Range_allocator *ram_alloc() { return &_ram_alloc; } + Range_allocator *io_mem_alloc() { return &_io_mem_alloc; } + Range_allocator *io_port_alloc() { return &_io_port_alloc; } + Range_allocator *irq_alloc() { return &_irq_alloc; } + Range_allocator *region_alloc() { return &_region_alloc; } + addr_t vm_start() const { return _vm_start; } + size_t vm_size() const { return _vm_size; } + Rom_fs *rom_fs() { return &_rom_fs; } + + void wait_for_exit(); + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-foc/src/core/include/platform_pd.h b/base-foc/src/core/include/platform_pd.h new file mode 100644 index 000000000..5ae2713b8 --- /dev/null +++ b/base-foc/src/core/include/platform_pd.h @@ -0,0 +1,116 @@ +/* + * \brief L4/Fiasco protection domain facility + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-04-11 + * + * Protection domains are L4 tasks under Fiasco.OC and serve as base + * container for the platform. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_PD_H_ +#define _CORE__INCLUDE__PLATFORM_PD_H_ + +#include +#include +#include +#include +#include + +namespace Fiasco { +#include +} + +namespace Genode { + + class Platform_thread; + class Platform_pd + { + private: + + enum { + THREAD_MAX = (1 << 6), + UTCB_AREA_SIZE = (THREAD_MAX * Fiasco::L4_UTCB_OFFSET), + UTCB_AREA_START = (Thread_base::CONTEXT_AREA_VIRTUAL_BASE + + THREAD_MAX * Thread_base::CONTEXT_VIRTUAL_SIZE) + }; + + Native_task _l4_task_cap; /* L4 task capability slot */ + Native_capability _parent; + bool _parent_cap_mapped; + bool _task_cap_mapped; + Platform_thread *_threads[THREAD_MAX]; + + /** + * Protection-domain creation + * + * The syscall parameter propagates if any L4 kernel function + * should be used. We need the special case for the Core startup. + */ + void _create_pd(bool syscall); + + /** + * Protection domain destruction + * + * No special case for Core here - we just never call it. + */ + void _destroy_pd(); + + public: + + class Threads_exhausted : Exception {}; + + + /** + * Constructor + */ + Platform_pd(bool create = true, + Native_task task_cap = Native_task()); + + /** + * Destructor + */ + ~Platform_pd(); + + /** + * Bind thread to protection domain + * + * \return 0 on success or + * -1 if thread ID allocation failed. + * + * This function allocates the physical L4 thread ID. + */ + int bind_thread(Platform_thread *thread); + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread *thread); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent); + + void map_task_cap(); + void map_parent_cap(); + + /******************************* + ** Fiasco-specific Accessors ** + *******************************/ + + Native_task native_task() { return _l4_task_cap; } + Native_thread parent_cap() { return _parent.dst(); } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ diff --git a/base-foc/src/core/include/platform_thread.h b/base-foc/src/core/include/platform_thread.h new file mode 100644 index 000000000..45a01a179 --- /dev/null +++ b/base-foc/src/core/include/platform_thread.h @@ -0,0 +1,164 @@ +/* + * \brief Fiasco thread facility + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__PLATFORM_THREAD_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +namespace Genode { + + class Platform_pd; + class Platform_thread + { + private: + + friend class Platform_pd; + + bool _core_thread; + Native_capability _thread_cap; + Native_capability _gate_cap; + Native_capability _remote_gate_cap; + Native_thread _remote_pager_cap; + Native_thread _irq_cap; + Native_thread _remote_irq_cap; + Capability_node _node; + Native_utcb _utcb; + char _name[32]; /* thread name that will be + registered at the kernel + debugger */ + Platform_pd *_platform_pd; /* protection domain thread + is bound to */ + Pager_object *_pager; + + void _create_thread(void); + void _finalize_construction(const char *name, unsigned prio); + bool _in_syscall(Fiasco::l4_umword_t flags); + + public: + + enum { DEFAULT_PRIORITY = 128 }; + + /** + * Constructor for non-core threads + */ + Platform_thread(const char *name, unsigned priority); + + /** + * Constructor for core main-thread + */ + Platform_thread(Native_thread cap, const char *name); + + /** + * Constructor for core threads + */ + Platform_thread(const char *name); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * This thread is about to be bound + * + * \param cap final capability index + * \param pd platform pd, thread is bound to + */ + void bind(/*Native_thread_id cap, */Platform_pd *pd); + + /** + * Unbind this thread + */ + void unbind(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Return/set pager + */ + Pager_object *pager() const { return _pager; } + void pager(Pager_object *pager); + + /** + * Return identification of thread when faulting + */ + unsigned long pager_object_badge() { + return (unsigned long) _thread_cap.dst(); } + + + /******************************* + ** Fiasco-specific Accessors ** + *******************************/ + + Native_thread native_thread() const { return _thread_cap.dst(); } + Native_capability thread_cap() const { return _thread_cap; } + Native_capability gate() const { return _remote_gate_cap; } + const char *name() const { return _name; } + bool core_thread() const { return _core_thread; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-foc/src/core/include/util.h b/base-foc/src/core/include/util.h new file mode 100644 index 000000000..3a8366012 --- /dev/null +++ b/base-foc/src/core/include/util.h @@ -0,0 +1,117 @@ +/* + * \brief Fiasco.OC utilities + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-04-11 + * + * Is very practical now, but please keep the errors of the l4util pkg in mind. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__UTIL_H_ +#define _CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +} + +namespace Genode { + + inline void log_event(const char *s) + { + Fiasco::fiasco_tbuf_log(s); + } + + inline void log_event(const char *s, unsigned v1, unsigned v2, unsigned v3) + { + Fiasco::fiasco_tbuf_log_3val(s, v1, v2, v3); + } + + inline void panic(const char *s) + { + using namespace Fiasco; + outstring(s); + enter_kdebug("> panic <"); + } + + inline void touch_ro(const void *addr, unsigned size) + { + using namespace Fiasco; + unsigned char const volatile *bptr; + unsigned char const *eptr; + + bptr = (unsigned char const volatile *)(((Genode::addr_t)addr) & L4_PAGEMASK); + eptr = (unsigned char const *)(((Genode::addr_t)addr + size - 1) & L4_PAGEMASK); + for ( ; bptr <= eptr; bptr += L4_PAGESIZE) + touch_read(bptr); + } + + inline void touch_rw(const void *addr, unsigned size) + { + using namespace Fiasco; + unsigned char volatile *bptr; + unsigned char const *eptr; + + bptr = (unsigned char volatile *)(((Genode::addr_t)addr) & L4_PAGEMASK); + eptr = (unsigned char const *)(((Genode::addr_t)addr + size - 1) & L4_PAGEMASK); + for (; bptr <= eptr; bptr += L4_PAGESIZE) + touch_read_write(bptr); + } + + inline addr_t trunc_page(addr_t addr) + { + using namespace Fiasco; + return l4_trunc_page(addr); + } + + inline addr_t round_page(addr_t addr) + { + using namespace Fiasco; + return l4_round_page(addr); + } + + inline addr_t round_superpage(addr_t addr) + { + using namespace Fiasco; + return (addr + L4_SUPERPAGESIZE-1) & L4_SUPERPAGEMASK; + } + + inline size_t get_page_size() { return L4_PAGESIZE; } + + inline size_t get_page_size_log2() { return L4_LOG2_PAGESIZE; } + + inline size_t get_super_page_size() { return L4_SUPERPAGESIZE; } + + inline size_t get_super_page_size_log2() { return L4_LOG2_SUPERPAGESIZE; } + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long badge) + { + printf("%s (%s pf_addr=%p pf_ip=%p from %lx)\n", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, badge); + } + + inline addr_t map_src_addr(addr_t core_local_addr, addr_t phys_addr) { + return core_local_addr; } + + inline size_t constrain_map_size_log2(size_t size_log2) { return size_log2; } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-foc/src/core/io_mem_session_support.cc b/base-foc/src/core/io_mem_session_support.cc new file mode 100644 index 000000000..f75670556 --- /dev/null +++ b/base-foc/src/core/io_mem_session_support.cc @@ -0,0 +1,92 @@ +/* + * \brief Fiasco.OC-specific implementation of the IO_MEM session interface + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-08-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +} + +using namespace Genode; + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ + platform()->region_alloc()->free(reinterpret_cast(base)); +} + + +static inline bool can_use_super_page(addr_t base, size_t size) +{ + return (base & (get_super_page_size() - 1)) == 0 + && (size >= get_super_page_size()); +} + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ + using namespace Fiasco; + + /* align large I/O dataspaces on a super-page boundary within core */ + size_t alignment = (size >= get_super_page_size()) ? get_super_page_size_log2() + : get_page_size_log2(); + + /* find appropriate region for mapping */ + void *local_base = 0; + if (!platform()->region_alloc()->alloc_aligned(size, &local_base, alignment)) + return 0; + + /* call sigma0 for I/O region */ + unsigned offset = 0; + while (size) { + /* FIXME what about caching demands? */ + /* FIXME what about read / write? */ + + l4_utcb_mr()->mr[0] = SIGMA0_REQ_FPAGE_IOMEM; + + size_t page_size_log2 = get_page_size_log2(); + if (can_use_super_page(base + offset, size)) + page_size_log2 = get_super_page_size_log2(); + l4_utcb_mr()->mr[1] = l4_fpage(base + offset, + page_size_log2, L4_FPAGE_RWX).raw; + + /* open receive window for mapping */ + l4_utcb_br()->bdr = 0; + l4_utcb_br()->br[0] = L4_ITEM_MAP; + l4_utcb_br()->br[1] = l4_fpage((addr_t)local_base + offset, + page_size_log2, L4_FPAGE_RWX).raw; + + l4_msgtag_t tag = l4_msgtag(L4_PROTO_SIGMA0, 2, 0, 0); + tag = l4_ipc_call(L4_BASE_PAGER_CAP, l4_utcb(), tag, L4_IPC_NEVER); + if (l4_ipc_error(tag, l4_utcb())) { + PERR("Ipc error %ld", l4_ipc_error(tag, l4_utcb())); + return 0; + } + + if (l4_msgtag_items(tag) < 1) { + PERR("Got no mapping!"); + return 0; + } + + offset += 1 << page_size_log2; + size -= 1 << page_size_log2; + } + + return (addr_t)local_base; +} diff --git a/base-foc/src/core/irq_session_component.cc b/base-foc/src/core/irq_session_component.cc new file mode 100644 index 000000000..a75e3942f --- /dev/null +++ b/base-foc/src/core/irq_session_component.cc @@ -0,0 +1,150 @@ +/* + * \brief Core implementation of IRQ sessions + * \author Christian Helmuth + * \date 2007-09-13 + * + * FIXME ram quota missing + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +#include +} + +using namespace Genode; + + +Irq_session_component::Interrupt* +Irq_session_component::Interrupt::find_by_num(unsigned num) +{ + if (number == num) return this; + + Interrupt *n = Avl_node::child(num > number); + return n ? n->find_by_num(num) : 0; +} + + +bool Irq_session_component::Interrupt::higher(Irq_session_component::Interrupt *n) +{ + return n->number > number; +} + + +Irq_session_component::Interrupt::Interrupt() +: _cap(Capability_allocator::allocator()->alloc()), _sem(), number(0) {} + + +Native_thread Irq_session_component::Interrupt_handler::handler_cap() +{ + static Interrupt_handler handler; + return handler._thread_cap.dst(); +} + + +void Irq_session_component::Interrupt_handler::entry() +{ + using namespace Fiasco; + + int err; + l4_msgtag_t tag; + l4_umword_t label; + + while (true) { + tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER); + if ((err = l4_ipc_error(tag, l4_utcb()))) + PERR("IRQ receive: %d\n", err); + else { + Interrupt *intr = _irqs()->first(); + if (intr) { + intr = intr->find_by_num(label); + if (intr) + intr->semaphore()->up(); + } + } + } +} + + +Avl_tree* Irq_session_component::_irqs() +{ + static Avl_tree irqs; + return &irqs; +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _ep(cap_session, STACK_SIZE, "irqctrl") +{ + using namespace Fiasco; + + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + if ((irq_number == -1) || _irq_alloc->alloc_addr(1, irq_number)) { + PERR("Unavailable IRQ %lx requested", irq_number); + throw Root::Invalid_args(); + } + + /* + * temorary hack for fiasco.oc using the local-apic, + * where old pic-line 0 maps to 2 + */ + if (irq_number == 0) + irq_number = 2; + + _irq.number = irq_number; + Irq_session_component::_irqs()->insert(&_irq); + + if (l4_error(l4_factory_create_irq(L4_BASE_FACTORY_CAP, _irq.capability()))) + PERR("l4_factory_create_irq failed!"); + + if (l4_error(l4_icu_bind(L4_BASE_ICU_CAP, irq_number, _irq.capability()))) + PERR("Binding IRQ%ld to the ICU failed", irq_number); + + if (l4_error(l4_irq_attach(_irq.capability(), irq_number, + Interrupt_handler::handler_cap()))) + PERR("Error attaching to IRQ %ld", irq_number); + + /* initialize capability */ + _irq_cap = _ep.manage(this); +} + + +void Irq_session_component::wait_for_irq() +{ + using namespace Fiasco; + + int err; + l4_msgtag_t tag = l4_irq_unmask(_irq.capability()); + if ((err = l4_ipc_error(tag, l4_utcb()))) + PERR("IRQ unmask: %d\n", err); + + _irq.semaphore()->down(); +} + + +Irq_session_component::~Irq_session_component() +{ + PERR("Implement me, immediately!"); +} diff --git a/base-foc/src/core/pd_session_extension.cc b/base-foc/src/core/pd_session_extension.cc new file mode 100644 index 000000000..fa08844a3 --- /dev/null +++ b/base-foc/src/core/pd_session_extension.cc @@ -0,0 +1,23 @@ +/* + * \brief Core implementation of the PD session extension + * \author Stefan Kalkowski + * \date 2011-04-14 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* Core includes */ +#include + + +Genode::Native_capability Genode::Pd_session_component::task_cap() { + return Native_capability(_pd.native_task(), + Badge_allocator::allocator()->alloc()); } diff --git a/base-foc/src/core/platform.cc b/base-foc/src/core/platform.cc new file mode 100644 index 000000000..c202db06a --- /dev/null +++ b/base-foc/src/core/platform.cc @@ -0,0 +1,500 @@ +/* + * \brief Fiasco platform interface implementation + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +#include +#include +#include + +static l4_kernel_info_t *kip; +} + +using namespace Genode; + + +static const bool verbose = true; +static const bool verbose_core_pf = false; +static const bool verbose_region_alloc = false; + + +/*********************************** + ** Core address space management ** + ***********************************/ + +static Synchronized_range_allocator &_core_address_ranges() +{ + static Synchronized_range_allocator _core_address_ranges(0); + return _core_address_ranges; +} + +enum { PAGER_STACK_ELEMENTS = 1024 }; +static unsigned long _core_pager_stack[PAGER_STACK_ELEMENTS]; + +/** + * Core pager "service loop" + */ +static void _core_pager_loop() +{ + using namespace Fiasco; + + bool send_reply = false; + l4_umword_t label; + l4_utcb_t *utcb = l4_utcb(); + l4_msgtag_t snd_tag = l4_msgtag(0, 0, 0, 0); + l4_msgtag_t tag; + + while (true) { + + if (send_reply) + tag = l4_ipc_reply_and_wait(utcb, snd_tag, &label, L4_IPC_NEVER); + else + tag = l4_ipc_wait(utcb, &label, L4_IPC_NEVER); + + if (!tag.is_page_fault()) { + PWRN("Received something different than a pagefault, ignoring ..."); + continue; + } + + /* read fault information */ + l4_umword_t pfa = l4_trunc_page(l4_utcb_mr()->mr[0]); + l4_umword_t ip = l4_utcb_mr()->mr[1]; + bool rw = l4_utcb_mr()->mr[0] & 2; //TODO enum + + if (pfa < (l4_umword_t)L4_PAGESIZE) { + + /* NULL pointer access */ + PERR("Possible null pointer %s at %lx IP %lx", + rw ? "WRITE" : "READ", pfa, ip); + /* do not unblock faulter */ + send_reply = false; + continue; + } else if (!_core_address_ranges().valid_addr(pfa)) { + + /* page-fault address is not in RAM */ + PERR("%s access outside of RAM at %lx IP %lx", + rw ? "WRITE" : "READ", pfa, ip); + /* do not unblock faulter */ + send_reply = false; + continue; + } else if (verbose_core_pf) + PDBG("pfa=%lx ip=%lx", pfa, ip); + + /* my pf handler is sigma0 - just touch the appropriate page */ + if (rw) + touch_rw((void *)pfa, 1); + else + touch_ro((void *)pfa, 1); + + send_reply = true; + } +} + + +Platform::Sigma0::Sigma0() : Pager_object(0) +{ + /* + * We use the Pager_object here in a slightly different manner, + * just to tunnel the pager cap to the Platform_thread::start method. + */ + cap(reinterpret_cap_cast(Native_capability(Fiasco::L4_BASE_PAGER_CAP, 0))); +} + + +Platform::Sigma0 *Platform::sigma0() +{ + static Sigma0 _sigma0; + return &_sigma0; +} + + +Platform::Core_pager::Core_pager(Platform_pd *core_pd) +: Platform_thread("core.pager"), Pager_object(0) +{ + Platform_thread::pager(sigma0()); + + core_pd->bind_thread(this); + cap(Native_capability(native_thread(), 0)); + + /* stack begins at the top end of the '_core_pager_stack' array */ + void *sp = (void *)&_core_pager_stack[PAGER_STACK_ELEMENTS - 1]; + start((void *)_core_pager_loop, sp); + + using namespace Fiasco; + + l4_thread_control_start(); + l4_thread_control_pager(native_thread()); + l4_thread_control_exc_handler(native_thread()); + l4_msgtag_t tag = l4_thread_control_commit(L4_BASE_THREAD_CAP); + if (l4_msgtag_has_error(tag)) + PWRN("l4_thread_control_commit failed!"); +} + + +Platform::Core_pager *Platform::core_pager() +{ + static Core_pager _core_pager(core_pd()); + return &_core_pager; +} + + +/*********************************** + ** Helper for L4 region handling ** + ***********************************/ + +struct Region +{ + addr_t start; + addr_t end; + + Region() : start(0), end(0) { } + Region(addr_t s, addr_t e) : start(s), end(e) { } +}; + + +/** + * Log region + */ +static inline void print_region(Region r) +{ + printf("[%08lx,%08lx) %08lx", r.start, r.end, r.end - r.start); +} + + +/** + * Add region to allocator + */ +static inline void add_region(Region r, Range_allocator &alloc) +{ + if (verbose_region_alloc) { + printf("%p add: ", &alloc); print_region(r); printf("\n"); + } + + /* adjust region */ + addr_t start = trunc_page(r.start); + addr_t end = round_page(r.end); + + alloc.add_range(start, end - start); +} + + +/** + * Remove region from allocator + */ +static inline void remove_region(Region r, Range_allocator &alloc) +{ + if (verbose_region_alloc) { + printf("%p remove: ", &alloc); print_region(r); printf("\n"); + } + + /* adjust region */ + addr_t start = trunc_page(r.start); + addr_t end = round_page(r.end); + + alloc.remove_range(start, end - start); +} + + +/** + * Request any RAM page from Sigma0 + */ +static inline int sigma0_req_region(addr_t *addr, unsigned log2size) +{ + using namespace Fiasco; + + l4_utcb_mr()->mr[0] = SIGMA0_REQ_FPAGE_ANY; + l4_utcb_mr()->mr[1] = l4_fpage(0, log2size, 0).raw; + + /* open receive window for mapping */ + l4_utcb_br()->bdr &= ~L4_BDR_OFFSET_MASK; + l4_utcb_br()->br[0] = L4_ITEM_MAP; + l4_utcb_br()->br[1] = l4_fpage(0, L4_WHOLE_ADDRESS_SPACE, L4_FPAGE_RWX).raw; + + l4_msgtag_t tag = l4_msgtag(L4_PROTO_SIGMA0, 2, 0, 0); + tag = l4_ipc_call(L4_BASE_PAGER_CAP, l4_utcb(), tag, L4_IPC_NEVER); + if (l4_ipc_error(tag, l4_utcb())) + return -1; + + if (l4_msgtag_items(tag) != 1) + return -2; + + *addr = l4_utcb_mr()->mr[0] & (~0UL << L4_PAGESHIFT); + + touch_rw((void *)addr, 1); + + return 0; +} + + +static Fiasco::l4_kernel_info_t *sigma0_map_kip() +{ + using namespace Fiasco; + + /* signal we want to map the KIP */ + l4_utcb_mr()->mr[0] = SIGMA0_REQ_KIP; + + /* open receive window for KIP one-to-one */ + l4_utcb_br()->bdr &= ~L4_BDR_OFFSET_MASK; + l4_utcb_br()->br[0] = L4_ITEM_MAP; + l4_utcb_br()->br[1] = l4_fpage(0, L4_WHOLE_ADDRESS_SPACE, L4_FPAGE_RX).raw; + + /* call sigma0 */ + l4_msgtag_t tag = l4_ipc_call(L4_BASE_PAGER_CAP, + l4_utcb(), + l4_msgtag(L4_PROTO_SIGMA0, 1, 0, 0), + L4_IPC_NEVER); + if (l4_ipc_error(tag, l4_utcb())) + return 0; + + l4_addr_t ret = l4_trunc_page(l4_utcb_mr()->mr[0]); + return (l4_kernel_info_t*) ret; +} + + +void Platform::_setup_mem_alloc() +{ + /* + * Completely map program image by touching all pages read-only to + * prevent sigma0 from handing out those page as anonymous memory. + */ + volatile const char *beg, *end; + beg = (const char *)(((Genode::addr_t)&_prog_img_beg) & L4_PAGEMASK); + end = (const char *)&_prog_img_end; + for ( ; beg < end; beg += L4_PAGESIZE) (void)(*beg); + + /* request pages of known page size starting with largest */ + size_t log2_sizes[] = { L4_LOG2_SUPERPAGESIZE, L4_LOG2_PAGESIZE }; + + for (unsigned i = 0; i < sizeof(log2_sizes)/sizeof(*log2_sizes); ++i) { + size_t log2_size = log2_sizes[i]; + size_t size = 1 << log2_size; + int err = 0; + addr_t addr = 0; + Region region; + + /* request any page of current size from sigma0 */ + do { + err = sigma0_req_region(&addr, log2_size); + if (!err) { + /* XXX do not allocate page0 */ + if (addr == 0) { + Fiasco::l4_task_unmap(Fiasco::L4_BASE_TASK_CAP, + Fiasco::l4_fpage(0, log2_size, 0), + Fiasco::L4_FP_ALL_SPACES); + continue; + } + + region.start = addr; region.end = addr + size; + add_region(region, _ram_alloc); + add_region(region, _core_address_ranges()); + remove_region(region, _io_mem_alloc); + remove_region(region, _region_alloc); + } + } while (!err); + } +} + + +void Platform::_setup_irq_alloc() { _irq_alloc.add_range(0, 0x100); } + + +void Platform::_setup_basics() +{ + using namespace Fiasco; + + kip = sigma0_map_kip(); + if (!kip) + panic("kip mapping failed"); + + if (kip->magic != L4_KERNEL_INFO_MAGIC) + panic("Sigma0 mapped something but not the KIP"); + + if (verbose) { + printf("\n"); + printf("KIP @ %p\n", kip); + printf(" magic: %08zx\n", (size_t)kip->magic); + printf(" version: %08zx\n", (size_t)kip->version); + printf(" sigma0 "); printf(" esp: %08lx eip: %08lx\n", kip->sigma0_esp, kip->sigma0_eip); + printf(" sigma1 "); printf(" esp: %08lx eip: %08lx\n", kip->sigma1_esp, kip->sigma1_eip); + printf(" root "); printf(" esp: %08lx eip: %08lx\n", kip->root_esp, kip->root_eip); + } + + /* add KIP as ROM module */ + _kip_rom = Rom_module((addr_t)kip, L4_PAGESIZE, "l4v2_kip"); + _rom_fs.insert(&_kip_rom); + + /* update multi-boot info pointer from KIP */ + void *mb_info_ptr = (void *)kip->user_ptr; + _mb_info = Multiboot_info(mb_info_ptr); + if (verbose) printf("MBI @ %p\n", mb_info_ptr); + + /* parse memory descriptors - look for virtual memory configuration */ + /* XXX we support only one VM region (here and also inside RM) */ + using Fiasco::L4::Kip::Mem_desc; + + _vm_start = 0; _vm_size = 0; + Mem_desc *desc = Mem_desc::first(kip); + + for (unsigned i = 0; i < Mem_desc::count(kip); ++i) + if (desc[i].is_virtual()) { + _vm_start = round_page(desc[i].start()); + _vm_size = trunc_page(desc[i].end() - _vm_start + 1); + + break; + } + if (_vm_size == 0) + panic("Virtual memory configuration not found"); + + /* configure applicable address space but never use page0 */ + _vm_size = _vm_start == 0 ? _vm_size - L4_PAGESIZE : _vm_size; + _vm_start = _vm_start == 0 ? L4_PAGESIZE : _vm_start; + _region_alloc.add_range(_vm_start, _vm_size); + + /* preserve context area in core's virtual address space */ + _region_alloc.remove_range(Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + Thread_base::CONTEXT_AREA_VIRTUAL_SIZE); + + /* preserve utcb- area in core's virtual address space */ + _region_alloc.remove_range((addr_t)l4_utcb(), L4_PAGESIZE); + + /* I/O memory could be the whole user address space */ + /* FIXME if the kernel helps to find out max address - use info here */ + _io_mem_alloc.add_range(0, ~0); + + /* remove KIP and MBI area from region and IO_MEM allocator */ + remove_region(Region((addr_t)kip, (addr_t)kip + L4_PAGESIZE), _region_alloc); + remove_region(Region((addr_t)kip, (addr_t)kip + L4_PAGESIZE), _io_mem_alloc); + remove_region(Region((addr_t)mb_info_ptr, (addr_t)mb_info_ptr + _mb_info.size()), _region_alloc); + remove_region(Region((addr_t)mb_info_ptr, (addr_t)mb_info_ptr + _mb_info.size()), _io_mem_alloc); + + /* remove core program image memory from region and IO_MEM allocator */ + addr_t img_start = (addr_t) &_prog_img_beg; + addr_t img_end = (addr_t) &_prog_img_end; + remove_region(Region(img_start, img_end), _region_alloc); + remove_region(Region(img_start, img_end), _io_mem_alloc); + + /* image is accessible by core */ + add_region(Region(img_start, img_end), _core_address_ranges()); +} + + +void Platform::_setup_rom() +{ + Rom_module rom; + + for (unsigned i = FIRST_ROM; i < _mb_info.num_modules(); i++) { + if (!(rom = _mb_info.get_module(i)).valid()) continue; + + Rom_module *new_rom = new(core_mem_alloc()) Rom_module(rom); + _rom_fs.insert(new_rom); + + if (verbose) + printf(" mod[%d] [%p,%p) %s\n", i, + (void *)new_rom->addr(), ((char *)new_rom->addr()) + new_rom->size(), + new_rom->name()); + + /* zero remainder of last ROM page */ + size_t count = L4_PAGESIZE - rom.size() % L4_PAGESIZE; + if (count != L4_PAGESIZE) + memset(reinterpret_cast(rom.addr() + rom.size()), 0, count); + + /* remove ROM area from region and IO_MEM allocator */ + remove_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _region_alloc); + remove_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _io_mem_alloc); + + /* add area to core-accessible ranges */ + add_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _core_address_ranges()); + } + + Rom_module *kip_rom = new(core_mem_alloc()) + Rom_module((addr_t)Fiasco::kip, L4_PAGESIZE, "kip"); + _rom_fs.insert(kip_rom); +} + + +Platform::Platform() : + _ram_alloc(0), _io_mem_alloc(core_mem_alloc()), + _io_port_alloc(core_mem_alloc()), _irq_alloc(core_mem_alloc()), + _region_alloc(core_mem_alloc()) +{ + /* + * We must be single-threaded at this stage and so this is safe. + */ + static bool initialized = 0; + if (initialized) panic("Platform constructed twice!"); + initialized = true; + + _setup_basics(); + _setup_mem_alloc(); + _setup_io_port_alloc(); + _setup_irq_alloc(); + _setup_rom(); + + if (verbose) { + printf(":ram_alloc: "); _ram_alloc.raw()->dump_addr_tree(); + printf(":region_alloc: "); _region_alloc.raw()->dump_addr_tree(); + printf(":io_mem: "); _io_mem_alloc.raw()->dump_addr_tree(); + printf(":io_port: "); _io_port_alloc.raw()->dump_addr_tree(); + printf(":irq: "); _irq_alloc.raw()->dump_addr_tree(); + printf(":rom_fs: "); _rom_fs.print_fs(); + printf(":core ranges: "); _core_address_ranges().raw()->dump_addr_tree(); + } + + /* setup pd object for core pd */ + _core_pd = new(core_mem_alloc()) Platform_pd(false, Fiasco::L4_BASE_TASK_CAP); + + /* + * We setup the thread object for thread0 in core pd using a special + * interface that allows us to specify the capability slot. + */ + Platform_thread *core_thread = new(core_mem_alloc()) + Platform_thread(Fiasco::L4_BASE_THREAD_CAP, "core.main"); + + core_thread->pager(sigma0()); + _core_pd->bind_thread(core_thread); +} + + +/******************************** + ** Generic platform interface ** + ********************************/ + +void Platform::wait_for_exit() +{ + /* + * On Fiasco, Core never exits. So let us sleep forever. + */ + sleep_forever(); +} + + +void Core_parent::exit(int exit_value) { } diff --git a/base-foc/src/core/platform_pd.cc b/base-foc/src/core/platform_pd.cc new file mode 100644 index 000000000..12ca769c9 --- /dev/null +++ b/base-foc/src/core/platform_pd.cc @@ -0,0 +1,174 @@ +/* + * \brief Fiasco protection domain facility + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +} + +using namespace Fiasco; +using namespace Genode; + + +static addr_t core_utcb_base() { + static addr_t base = (addr_t) l4_utcb(); + return base; +} + + +/**************************** + ** Private object members ** + ****************************/ + +void Platform_pd::_create_pd(bool syscall) +{ + if (!_l4_task_cap.valid()) + _l4_task_cap = Capability_allocator::allocator()->alloc(); + + if (syscall) { + if (!_l4_task_cap) + panic("no cap slot for pd creation available!"); + + l4_fpage_t utcb_area = l4_fpage(UTCB_AREA_START, + log2(UTCB_AREA_SIZE), 0); + l4_msgtag_t tag = l4_factory_create_task(L4_BASE_FACTORY_CAP, + _l4_task_cap, utcb_area); + + if (l4_msgtag_has_error(tag)) + panic("pd creation failed"); + } +} + + +void Platform_pd::_destroy_pd() +{ + l4_task_unmap(L4_BASE_TASK_CAP, + l4_obj_fpage(_l4_task_cap, 0, L4_FPAGE_RWX), + L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ); +} + + +/*************************** + ** Public object members ** + ***************************/ + +int Platform_pd::bind_thread(Platform_thread *thread) +{ + using namespace Fiasco; + + for (unsigned i = 0; i < THREAD_MAX; i++) { + if (_threads[i]) + continue; + + _threads[i] = thread; + if (thread->core_thread()) + thread->_utcb = (l4_utcb_t*) (core_utcb_base() + i * L4_UTCB_OFFSET); + else + thread->_utcb = + reinterpret_cast(UTCB_AREA_START + i * L4_UTCB_OFFSET); + Native_thread cap_offset = Fiasco_capability::THREADS_BASE_CAP + + i * Fiasco_capability::THREAD_CAP_SLOT; + thread->_remote_gate_cap = Native_capability(cap_offset + Fiasco_capability::THREAD_GATE_CAP, + thread->_gate_cap.local_name()); + thread->_remote_pager_cap = cap_offset + Fiasco_capability::THREAD_PAGER_CAP; + thread->_remote_irq_cap = cap_offset + Fiasco_capability::THREAD_IRQ_CAP; + + /* inform thread about binding */ + thread->bind(this); + return 0; + } + + PERR("thread alloc failed"); + return -1; +} + + +void Platform_pd::unbind_thread(Platform_thread *thread) +{ + /* inform thread about unbinding */ + thread->unbind(); + + for (unsigned i = 0; i < THREAD_MAX; i++) + if (_threads[i] == thread) { + _threads[i] = (Platform_thread*) 0; + return; + } +} + + +int Platform_pd::assign_parent(Native_capability parent) +{ + if (_parent.valid()) return -1; + _parent = parent; + return 0; +} + + +void Platform_pd::map_parent_cap() +{ + if (!_parent_cap_mapped) { + l4_msgtag_t tag = l4_task_map(_l4_task_cap, L4_BASE_TASK_CAP, + l4_obj_fpage(_parent.dst(), 0, L4_FPAGE_RWX), + Fiasco_capability::PARENT_CAP | L4_ITEM_MAP); + if (l4_msgtag_has_error(tag)) + PWRN("mapping parent cap failed"); + + _parent_cap_mapped = true; + } +} + + +void Platform_pd::map_task_cap() +{ + if (!_task_cap_mapped) { + l4_msgtag_t tag = l4_task_map(_l4_task_cap, L4_BASE_TASK_CAP, + l4_obj_fpage(_l4_task_cap, 0, L4_FPAGE_RWX), + Fiasco_capability::TASK_CAP | L4_ITEM_MAP); + if (l4_msgtag_has_error(tag)) + PWRN("mapping task cap failed"); + _task_cap_mapped = true; + } +} + + +Platform_pd::Platform_pd(bool create, Native_task task_cap) +: _l4_task_cap(task_cap), + _parent_cap_mapped(false), + _task_cap_mapped(false) +{ + for (unsigned i = 0; i < THREAD_MAX; i++) + _threads[i] = (Platform_thread*) 0; + + _create_pd(create); +} + + +Platform_pd::~Platform_pd() +{ + for (unsigned i = 0; i < THREAD_MAX; i++) { + if (_threads[i]) + _threads[i]->unbind(); + } + _destroy_pd(); +} diff --git a/base-foc/src/core/platform_thread.cc b/base-foc/src/core/platform_thread.cc new file mode 100644 index 000000000..2288447b6 --- /dev/null +++ b/base-foc/src/core/platform_thread.cc @@ -0,0 +1,307 @@ +/* + * \brief Fiasco thread facility + * \author Stefan Kalkowski + * \date 2011-01-04 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +#include +#include +#include +#include +#include +} + +using namespace Genode; +using namespace Fiasco; + + +int Platform_thread::start(void *ip, void *sp) +{ + if (_pager && _platform_pd) { + /* map pager cap */ + l4_msgtag_t tag = l4_task_map(_platform_pd->native_task(), L4_BASE_TASK_CAP, + l4_obj_fpage(_pager->cap().dst(), 0, L4_FPAGE_RWX), + _remote_pager_cap | L4_ITEM_MAP); + if (l4_msgtag_has_error(tag)) + PWRN("mapping pager cap failed"); + } + + /* reserve utcb area and associate thread with this task */ + l4_thread_control_start(); + l4_thread_control_pager(_remote_pager_cap); + l4_thread_control_exc_handler(_remote_pager_cap); + l4_thread_control_bind(_utcb, _platform_pd->native_task()); + + l4_msgtag_t tag = l4_thread_control_commit(_thread_cap.dst()); + if (l4_msgtag_has_error(tag)) { + PWRN("l4_thread_control_commit for %lx failed!", + (unsigned long) _thread_cap.dst()); + return -1; + } + + /* set ip and sp and run the thread */ + tag = l4_thread_ex_regs(_thread_cap.dst(), (l4_addr_t) ip, (l4_addr_t) sp, 0); + if (l4_msgtag_has_error(tag)) { + PWRN("l4_thread_ex_regs failed!"); + return -1; + } + + return 0; +} + + +void Platform_thread::pause() +{ + if (!_pager) + return; + + _pager->state.lock.lock(); + + if (_pager->state.paused == true) { + _pager->state.lock.unlock(); + return; + } + + unsigned exc = _pager->state.exceptions; + _pager->state.ip = ~0UL; + _pager->state.sp = ~0UL; + l4_umword_t flags = L4_THREAD_EX_REGS_TRIGGER_EXCEPTION; + + /* Mark thread to be stopped */ + _pager->state.paused = true; + + /* + * Force the thread to be paused to trigger an exception. + * The pager thread, which also acts as exception handler, will + * leave the thread in exception state until, it gets woken again + */ + l4_thread_ex_regs_ret(_thread_cap.dst(), &_pager->state.ip, + &_pager->state.sp, &flags); + bool in_syscall = _in_syscall(flags); + _pager->state.lock.unlock(); + + /** + * Check whether the thread was in ongoing ipc, if so it won't raise + * an exception before ipc is completed. + */ + if (!in_syscall) { + /* + * Wait until the pager thread got an exception from + * the requested thread, and stored its thread state + */ + while (exc == _pager->state.exceptions && !_pager->state.in_exception) + l4_thread_switch(_thread_cap.dst()); + } +} + + +void Platform_thread::resume() +{ + if (!_pager) + return; + + _pager->state.lock.lock(); + + /* Mark thread to be runable again */ + _pager->state.paused = false; + _pager->state.lock.unlock(); + + /* Send a message to the exception handler, to unblock the client */ + Msgbuf<16> snd, rcv; + Ipc_client ipc_client(_pager->cap(), &snd, &rcv); + ipc_client << _pager << IPC_CALL; +} + + +void Platform_thread::bind(Platform_pd *pd) +{ + l4_msgtag_t tag; + Native_task task = pd->native_task(); + + _platform_pd = pd; + + if (!_core_thread) { + /* map parent and task cap if it doesn't happen already */ + _platform_pd->map_task_cap(); + _platform_pd->map_parent_cap(); + } + + if (_gate_cap.valid()) { + /* map thread's gate cap */ + tag = l4_task_map(task, L4_BASE_TASK_CAP, + l4_obj_fpage(_gate_cap.dst(), 0, L4_FPAGE_RWX), + _remote_gate_cap.dst() | L4_ITEM_MAP); + if (l4_msgtag_has_error(tag)) + PWRN("mapping thread's gate cap failed"); + } + + /* map thread's irq cap */ + tag = l4_task_map(task, L4_BASE_TASK_CAP, + l4_obj_fpage(_irq_cap, 0, L4_FPAGE_RWX), + _remote_irq_cap | L4_ITEM_MAP); + if (l4_msgtag_has_error(tag)) + PWRN("mapping thread's irq cap failed"); +} + + +void Platform_thread::unbind() +{ + l4_thread_ex_regs(_thread_cap.dst(), 0, 0, 0); + l4_task_unmap(L4_BASE_TASK_CAP, + l4_obj_fpage(_gate_cap.dst(), 0, L4_FPAGE_RWX), + L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ); + l4_task_unmap(L4_BASE_TASK_CAP, + l4_obj_fpage(_thread_cap.dst(), 0, L4_FPAGE_RWX), + L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ); + _platform_pd = (Platform_pd*) 0; +} + + +void Platform_thread::pager(Pager_object *pager) +{ + _pager = pager; +} + + +int Platform_thread::state(Thread_state *state_dst) +{ + if (_pager) + *state_dst = _pager->state; + + state_dst->cap = _remote_gate_cap; + return 0; +} + + +void Platform_thread::cancel_blocking() +{ + l4_irq_trigger(_irq_cap); +} + + +void Platform_thread::_create_thread() +{ + l4_msgtag_t tag = l4_factory_create_thread(L4_BASE_FACTORY_CAP, + _thread_cap.dst()); + if (l4_msgtag_has_error(tag)) + PERR("cannot create more thread kernel-objects!"); +} + + +void Platform_thread::_finalize_construction(const char *name, unsigned prio) +{ + /* create irq for new thread */ + _irq_cap = Capability_allocator::allocator()->alloc(); + l4_msgtag_t tag = l4_factory_create_irq(L4_BASE_FACTORY_CAP, _irq_cap); + if (l4_msgtag_has_error(tag)) + PWRN("creating thread's irq failed"); + + /* attach thread to irq */ + tag = l4_irq_attach(_irq_cap, 0, _thread_cap.dst()); + if (l4_msgtag_has_error(tag)) + PWRN("attaching thread's irq failed"); + + /* set human readable name in kernel debugger */ + strncpy(_name, name, sizeof(_name)); + Fiasco::l4_debugger_set_object_name(_thread_cap.dst(), name); + + /* set priority of thread */ + prio = Cpu_session::scale_priority(DEFAULT_PRIORITY, prio); + l4_sched_param_t params = l4_sched_param(prio); + l4_scheduler_run_thread(L4_BASE_SCHEDULER_CAP, _thread_cap.dst(), ¶ms); +} + + +Platform_thread::Platform_thread(const char *name, + unsigned prio) +: _core_thread(false), + _thread_cap(Capability_allocator::allocator()->alloc(), + Badge_allocator::allocator()->alloc()), + _node(_thread_cap.local_name(), 0, this, _thread_cap.dst()), + _utcb(0), + _platform_pd(0), + _pager(0) +{ + /* register the thread capability */ + Capability_tree::tree()->insert(&_node); + + _create_thread(); + + /* create gate for new thread */ + _gate_cap = core_env()->cap_session()->alloc(_thread_cap); + + _finalize_construction(name, prio); +} + + +Platform_thread::Platform_thread(Native_thread cap, const char *name) +: _core_thread(true), + _thread_cap(cap, -1), + _node(_thread_cap.local_name(), 0, this, _thread_cap.dst()), + _utcb(0), + _platform_pd(0), + _pager(0) +{ + /* register the thread capability */ + Capability_tree::tree()->insert(&_node); + + _finalize_construction(name, 0); +} + + +Platform_thread::Platform_thread(const char *name) +: _core_thread(true), + _thread_cap(Capability_allocator::allocator()->alloc(), + Badge_allocator::allocator()->alloc()), + _node(_thread_cap.local_name(), 0, this, _thread_cap.dst()), + _utcb(0), + _platform_pd(0), + _pager(0) +{ + /* register the thread capability */ + Capability_tree::tree()->insert(&_node); + + _create_thread(); + + /* create gate for new thread */ + _gate_cap = Cap_session_component::alloc(0, _thread_cap); + + _finalize_construction(name, 0); +} + + +Platform_thread::~Platform_thread() +{ + /* + * We inform our protection domain about thread destruction, which will end up in + * Thread::unbind() + */ + if (_platform_pd) + _platform_pd->unbind_thread(this); + + /* remove the thread capability */ + Capability_tree::tree()->remove(&_node); + Badge_allocator::allocator()->free(_thread_cap.local_name()); + Capability_allocator::allocator()->free(_thread_cap.dst()); +} diff --git a/base-foc/src/core/ram_session_support.cc b/base-foc/src/core/ram_session_support.cc new file mode 100644 index 000000000..9fe8d2c55 --- /dev/null +++ b/base-foc/src/core/ram_session_support.cc @@ -0,0 +1,27 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Norman Feske + * \date 2006-07-03 + * + * On L4, each dataspace _is_ a shared memory object. + * Therefore, these functions are empty. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "ram_session_component.h" + +using namespace Genode; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { } + +void Ram_session_component::_clear_ds(Dataspace_component *ds) +{ + memset((void *)ds->phys_addr(), 0, ds->size()); +} diff --git a/base-foc/src/core/rm_session_support.cc b/base-foc/src/core/rm_session_support.cc new file mode 100644 index 000000000..2406690e6 --- /dev/null +++ b/base-foc/src/core/rm_session_support.cc @@ -0,0 +1,35 @@ +/* + * \brief Fiasco-specific part of RM-session implementation + * \author Stefan Kalkowski + * \date 2011-01-18 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include + +/* Fiasco includes */ +namespace Fiasco { +#include +} + +using namespace Genode; + + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + using namespace Fiasco; + + // TODO unmap it only from target space + addr_t addr = core_local_base; + for (; addr < core_local_base + size; addr += L4_PAGESIZE) + l4_task_unmap(L4_BASE_TASK_CAP, + l4_fpage(addr, L4_LOG2_PAGESIZE, L4_FPAGE_RW), + L4_FP_OTHER_SPACES); +} diff --git a/base-foc/src/core/signal_source_component.cc b/base-foc/src/core/signal_source_component.cc new file mode 100644 index 000000000..e1f5fc8e0 --- /dev/null +++ b/base-foc/src/core/signal_source_component.cc @@ -0,0 +1,77 @@ +/* + * \brief Implementation of the SIGNAL interface + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2009-08-11 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +namespace Fiasco { +#include +#include +} + +using namespace Genode; + + +/***************************** + ** Signal-source component ** + *****************************/ + +void Signal_source_component::submit(Signal_context_component *context, + Ipc_ostream *ostream, + int cnt) +{ + /* enqueue signal to context */ + context->increment_signal_cnt(cnt); + + if (!context->is_enqueued()) { + _signal_queue.enqueue(context); + + /* wake up client */ + Fiasco::l4_irq_trigger(_blocking_semaphore.dst()); + } +} + + +Signal_source::Signal Signal_source_component::wait_for_signal() +{ + if (_signal_queue.empty()) { + PWRN("unexpected call of wait_for_signal"); + return Signal(0, 0); + } + + /* dequeue and return pending signal */ + Signal_context_component *context = _signal_queue.dequeue(); + Signal result(context->imprint(), context->cnt()); + context->reset_signal_cnt(); + return result; +} + + +Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) +: _entrypoint(ep) +{ + using namespace Fiasco; + + Fiasco_capability irq = Capability_allocator::allocator()->alloc(); + l4_msgtag_t res = l4_factory_create_irq(L4_BASE_FACTORY_CAP, irq); + if (l4_error(res)) + PERR("Allocation of irq object failed!"); + + _blocking_semaphore = Native_capability(irq, -1); +} diff --git a/base-foc/src/core/target.inc b/base-foc/src/core/target.inc new file mode 100644 index 000000000..27530aff6 --- /dev/null +++ b/base-foc/src/core/target.inc @@ -0,0 +1,57 @@ +TARGET = core +REQUIRES = foc +LIBS = cxx ipc heap core_printf process pager lock raw_signal raw_server + +LD_TEXT_ADDR = 0x140000 + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = main.cc \ + multiboot_info.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_bootstrap.cc \ + thread_start.cc \ + platform_thread.cc \ + platform_pd.cc \ + platform.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + cap_sel_alloc.cc \ + dump_alloc.cc \ + context_area.cc \ + cap_session_component.cc \ + cpu_session_extension.cc \ + pd_session_extension.cc + +INC_DIR += $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include + +vpath main.cc $(GEN_CORE_DIR) +vpath multiboot_info.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath context_area.cc $(GEN_CORE_DIR) +vpath thread.cc $(REP_DIR)/src/base/thread +vpath thread_bootstrap.cc $(REP_DIR)/src/base/thread +vpath cap_sel_alloc.cc $(REP_DIR)/src/base/env +vpath %.cc $(REP_DIR)/src/core diff --git a/base-foc/src/core/thread_start.cc b/base-foc/src/core/thread_start.cc new file mode 100644 index 000000000..ec4e7632a --- /dev/null +++ b/base-foc/src/core/thread_start.cc @@ -0,0 +1,68 @@ +/* + * \brief Fiasco.OC-specific implementation of core's startup Thread API. + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2006-05-03 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include +#include + +namespace Fiasco { +#include +#include +} + +using namespace Genode; + + +void Thread_base::_deinit_platform_thread() +{ + PWRN("%s: not implemented yet!", __func__); +} + + +void Thread_base::start() +{ + using namespace Fiasco; + + /* create and start platform thread */ + Platform_thread *pt = + new(platform()->core_mem_alloc()) Platform_thread(_context->name); + + platform_specific()->core_pd()->bind_thread(pt); + _tid = pt->gate().dst(); + _thread_cap = reinterpret_cap_cast(pt->thread_cap()); + + pt->pager(platform_specific()->core_pager()); + pt->start((void *)_thread_start, _context->stack); + + /* + * send newly constructed thread, pointer to its Thread_base object, + * and its badge + */ + Msgbuf<128> snd_msg, rcv_msg; + Ipc_client cli(_thread_cap, &snd_msg, &rcv_msg); + cli << (addr_t)this << pt->gate().local_name() << IPC_CALL; +} + + +void Thread_base::cancel_blocking() +{ + /* + * Within core, we never need to unblock threads + */ +} diff --git a/base-foc/src/core/x86/platform_thread.cc b/base-foc/src/core/x86/platform_thread.cc new file mode 100644 index 000000000..da360874a --- /dev/null +++ b/base-foc/src/core/x86/platform_thread.cc @@ -0,0 +1,27 @@ +/* + * \brief Fiasco.OC thread facility (x86 specifics) + * \author Stefan Kalkowski + * \date 2011-09-08 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + + +bool Genode::Platform_thread::_in_syscall(Fiasco::l4_umword_t flags) +{ + using namespace Genode; + + return flags & X86::IOPL; +} diff --git a/base-foc/src/core/x86/platform_x86.cc b/base-foc/src/core/x86/platform_x86.cc new file mode 100644 index 000000000..a5829af40 --- /dev/null +++ b/base-foc/src/core/x86/platform_x86.cc @@ -0,0 +1,44 @@ +/* + * \brief Platform support specific to x86 + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +#include "platform.h" +#include "util.h" + +/* Fiasco.OC includes */ +namespace Fiasco { +#include +} + +void Genode::Platform::_setup_io_port_alloc() +{ + using namespace Fiasco; + + l4_fpage_t fpage = l4_iofpage(0, L4_WHOLE_IOADDRESS_SPACE); + l4_utcb_mr()->mr[0] = fpage.raw; + l4_utcb_br()->bdr &= ~L4_BDR_OFFSET_MASK; + l4_utcb_br()->br[0] = L4_ITEM_MAP; + l4_utcb_br()->br[1] = fpage.raw; + l4_msgtag_t tag = l4_ipc_call(L4_BASE_PAGER_CAP, l4_utcb(), + l4_msgtag(L4_PROTO_IO_PAGE_FAULT, 1, 0, 0), + L4_IPC_NEVER); + + if (l4_ipc_error(tag, l4_utcb())) + panic("Received no I/O ports from sigma0"); + + /* setup allocator */ + _io_port_alloc.add_range(0, 0x10000); +} diff --git a/base-foc/src/core/x86/target.mk b/base-foc/src/core/x86/target.mk new file mode 100644 index 000000000..4a5ca470b --- /dev/null +++ b/base-foc/src/core/x86/target.mk @@ -0,0 +1,8 @@ +include $(PRG_DIR)/../target.inc + +REQUIRES += x86 +SRC_CC += x86/platform_x86.cc \ + x86/platform_thread.cc + +vpath io_port_session_component.cc $(GEN_CORE_DIR)/x86 + diff --git a/base-foc/src/kernel/pbxa9/target.mk b/base-foc/src/kernel/pbxa9/target.mk new file mode 100644 index 000000000..7efed8180 --- /dev/null +++ b/base-foc/src/kernel/pbxa9/target.mk @@ -0,0 +1,4 @@ +REQUIRES = platform_pbxa9 +KERNEL_CONFIG = $(REP_DIR)/config/pbxa9.kernel + +-include $(PRG_DIR)/../target.inc diff --git a/base-foc/src/kernel/target.inc b/base-foc/src/kernel/target.inc new file mode 100644 index 000000000..62dd563b3 --- /dev/null +++ b/base-foc/src/kernel/target.inc @@ -0,0 +1,21 @@ +TARGET = fiasco.oc +REQUIRES += foc +FOC_BUILD_DIR = $(BUILD_BASE_DIR)/kernel/$(TARGET) +FIASCO = $(FOC_BUILD_DIR)/fiasco +FIASCO_SRC = $(REP_DIR)/contrib/kernel/fiasco +STARTUP_LIB = + +$(TARGET): $(FIASCO) + +$(FOC_BUILD_DIR): + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) SYSTEM_TARGET="$(CROSS_DEV_PREFIX)" \ + $(VERBOSE_DIR) -C $(FIASCO_SRC) BUILDDIR=$@ + $(VERBOSE)cp $(KERNEL_CONFIG) $@/globalconfig.out + +$(FIASCO): $(FOC_BUILD_DIR) + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) SYSTEM_TARGET="$(CROSS_DEV_PREFIX)" \ + $(VERBOSE_DIR) -C $(FOC_BUILD_DIR) + $(VERBOSE)ln -sf $@ $(BUILD_BASE_DIR)/bin/$(TARGET) + +clean cleanall: + $(VERBOSE)rm -rf $(FOC_BUILD_DIR) diff --git a/base-foc/src/kernel/vea9x4/target.mk b/base-foc/src/kernel/vea9x4/target.mk new file mode 100644 index 000000000..b4b1ffa60 --- /dev/null +++ b/base-foc/src/kernel/vea9x4/target.mk @@ -0,0 +1,4 @@ +REQUIRES = platform_vea9x4 +KERNEL_CONFIG = $(REP_DIR)/config/vea9x4.kernel + +-include $(PRG_DIR)/../target.inc diff --git a/base-foc/src/kernel/x86_32/target.mk b/base-foc/src/kernel/x86_32/target.mk new file mode 100644 index 000000000..7324e497e --- /dev/null +++ b/base-foc/src/kernel/x86_32/target.mk @@ -0,0 +1,4 @@ +REQUIRES = x86 32bit +KERNEL_CONFIG = $(REP_DIR)/config/x86_32.kernel + +-include $(PRG_DIR)/../target.inc diff --git a/base-foc/src/kernel/x86_64/target.mk b/base-foc/src/kernel/x86_64/target.mk new file mode 100644 index 000000000..f01766c42 --- /dev/null +++ b/base-foc/src/kernel/x86_64/target.mk @@ -0,0 +1,4 @@ +REQUIRES = x86 64bit +KERNEL_CONFIG = $(REP_DIR)/config/x86_64.kernel + +-include $(PRG_DIR)/../target.inc diff --git a/base-foc/src/platform/_main_helper.h b/base-foc/src/platform/_main_helper.h new file mode 100644 index 000000000..05d69d205 --- /dev/null +++ b/base-foc/src/platform/_main_helper.h @@ -0,0 +1,29 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Christian Prochaska + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM___MAIN_HELPER_H_ +#define _PLATFORM___MAIN_HELPER_H_ + +/* Genode includes */ +#include + +namespace Fiasco { +#include +} + + +static void main_thread_bootstrap() { + Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_THREAD_OBJ] = 0; } + + +#endif /* _PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-foc/src/platform/_main_parent_cap.h b/base-foc/src/platform/_main_parent_cap.h new file mode 100644 index 000000000..570449a9d --- /dev/null +++ b/base-foc/src/platform/_main_parent_cap.h @@ -0,0 +1,36 @@ +/* + * \brief Obtain parent capability + * \author Norman Feske + * \date 2010-01-26 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__MAIN_PARENT_CAP_H_ +#define _PLATFORM__MAIN_PARENT_CAP_H_ + +#include + +namespace Genode { + + /** + * Return constructed parent capability + */ + Parent_capability parent_cap() + { + Native_capability cap; + memcpy(&cap, (void *)&_parent_cap, sizeof(cap)); + + /* assemble parent capability from object ID and Fiasco cap */ + return reinterpret_cap_cast( + Native_capability(Fiasco::Fiasco_capability::PARENT_CAP, + cap.local_name())); + } +} + +#endif /* _PLATFORM__MAIN_PARENT_CAP_H_ */ diff --git a/base-foc/src/sigma0/target.mk b/base-foc/src/sigma0/target.mk new file mode 100644 index 000000000..81726b75a --- /dev/null +++ b/base-foc/src/sigma0/target.mk @@ -0,0 +1,5 @@ +TARGET = l4f/sigma0 +PKGS = sigma0 +LIBS = l4re_support + +include $(REP_DIR)/mk/l4_pkg.mk diff --git a/base-host/README b/base-host/README new file mode 100644 index 000000000..3e7487c66 --- /dev/null +++ b/base-host/README @@ -0,0 +1,7 @@ +This repository contains dummy implementations of platform-specific Genode APIs +to enable the compilation of Genode for the host platform. Because the +repository provides only dummy implementations, most of the generated binaries +will not work. However, the repository serves two important purposes. It +documents the platform- specific APIs that must be filled out when porting +Genode to another platform, and it is the build environment for unit tests +executed on the host platform. diff --git a/base-host/etc/specs.conf b/base-host/etc/specs.conf new file mode 100644 index 000000000..834b0f8f4 --- /dev/null +++ b/base-host/etc/specs.conf @@ -0,0 +1,13 @@ +# +# Description of build platform +# + +# +# If you want to build the host-specific Genode +# binaries, use this config option. +# +ifeq ($(shell uname -m),x86_64) +SPECS ?= host x86_64 +else +SPECS ?= host x86_32 +endif diff --git a/base-host/etc/tools.conf b/base-host/etc/tools.conf new file mode 100644 index 000000000..b051c01e2 --- /dev/null +++ b/base-host/etc/tools.conf @@ -0,0 +1,4 @@ +# +# Use the default host compiler instead of the Genode tool chain +# +CROSS_DEV_PREFIX = diff --git a/base-host/include/base/ipc_msgbuf.h b/base-host/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..ac3bc03c2 --- /dev/null +++ b/base-host/include/base/ipc_msgbuf.h @@ -0,0 +1,39 @@ +/* + * \brief Dummy IPC message buffer + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +namespace Genode { + + class Msgbuf_base + { + private: + + size_t _size; + + public: + + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; }; + }; + + template + class Msgbuf : public Msgbuf_base { }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-host/include/base/ipc_pager.h b/base-host/include/base/ipc_pager.h new file mode 100644 index 000000000..9c8a9760a --- /dev/null +++ b/base-host/include/base/ipc_pager.h @@ -0,0 +1,139 @@ +/* + * \brief Dummy pager support for Genode + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +#include +#include +#include + +namespace Genode { + + class Mapping + { + public: + + /** + * Constructor + */ + Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, unsigned l2size = 12, bool rw = true) { } + + /** + * Construct invalid mapping + */ + Mapping() { } + + /** + * Prepare map operation + */ + void prepare_map_operation() { } + }; + + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + protected: + + /** + * Wait for short-message (register) IPC -- pagefault + */ + void _wait() { } + + /** + * Send short flex page and + * wait for next short-message (register) IPC -- pagefault + */ + void _reply_and_wait() { } + + public: + + /** + * Constructor + */ + Ipc_pager() { } + + /** + * Wait for a new fault received as short message IPC + */ + void wait_for_fault() { } + + /** + * Reply current page-fault and wait for a new one + * + * Send short flex page and wait for next short-message (register) + * IPC -- fault + */ + void reply_and_wait_for_fault() { } + + /** + * Request instruction pointer of current page fault + */ + addr_t fault_ip() { return 0; } + + /** + * Request fault address of current page fault + */ + addr_t fault_addr() { return 0; } + + /** + * Set parameters for next reply + */ + void set_reply_mapping(Mapping m) { } + + /** + * Set destination for next reply + */ + void set_reply_dst(Native_capability pager_object) { } + + /** + * Answer call without sending a flex-page mapping + * + * This function is used to acknowledge local calls from one of + * core's region-manager sessions. + */ + void acknowledge_wakeup() { } + + /** + * Return thread ID of last faulter + */ + Native_thread_id last() const { return 0; } + + /** + * Return badge for faulting thread + */ + unsigned long badge() const { return 0; } + + /** + * Return true if last fault was a write fault + */ + bool is_write_fault() const { return false; } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + /* + * Reflection of exceptions is not supported on this platform. + */ + return false; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-host/include/base/native_types.h b/base-host/include/base/native_types.h new file mode 100644 index 000000000..57ecd778a --- /dev/null +++ b/base-host/include/base/native_types.h @@ -0,0 +1,45 @@ +/* + * \brief Dummy definitions for native types used for compiling unit tests + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +namespace Genode { + + typedef volatile int Native_lock; + typedef int Native_thread; + typedef Native_thread Native_thread_id; + typedef struct { } Native_utcb; + + class Native_capability + { + private: + + long _local_name; + + public: + + Native_capability() : _local_name(0) { } + Native_capability(Native_thread_id, long local_name) + : _local_name(local_name) { } + + bool valid() const { return _local_name != 0; } + int local_name() const { return _local_name; } + int dst() const { return 0; } + Native_thread_id tid() const { return 0; } + }; + + typedef int Native_connection_state; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-host/lib/mk/core_printf.mk b/base-host/lib/mk/core_printf.mk new file mode 100644 index 000000000..a0fa6a56b --- /dev/null +++ b/base-host/lib/mk/core_printf.mk @@ -0,0 +1 @@ +LIBS = printf_stdio console diff --git a/base-host/lib/mk/env.mk b/base-host/lib/mk/env.mk new file mode 100644 index 000000000..915a0ccbe --- /dev/null +++ b/base-host/lib/mk/env.mk @@ -0,0 +1,6 @@ +SRC_CC = env.cc parent.cc context_area.cc +LIBS = ipc heap lock log_console + +vpath env.cc $(BASE_DIR)/src/base/env +vpath context_area.cc $(BASE_DIR)/src/base/env +vpath parent.cc $(REP_DIR)/src/base/env diff --git a/base-host/lib/mk/ipc.mk b/base-host/lib/mk/ipc.mk new file mode 100644 index 000000000..524cb1f03 --- /dev/null +++ b/base-host/lib/mk/ipc.mk @@ -0,0 +1,3 @@ +SRC_CC = ipc.cc + +vpath ipc.cc $(REP_DIR)/src/base/ipc diff --git a/base-host/lib/mk/lock.mk b/base-host/lib/mk/lock.mk new file mode 100644 index 000000000..a79c1d9a1 --- /dev/null +++ b/base-host/lib/mk/lock.mk @@ -0,0 +1,4 @@ +SRC_CC = lock.cc +INC_DIR += $(REP_DIR)/src/base/lock + +vpath lock.cc $(BASE_DIR)/src/base/lock diff --git a/base-host/lib/mk/pager.mk b/base-host/lib/mk/pager.mk new file mode 100644 index 000000000..c22e66d22 --- /dev/null +++ b/base-host/lib/mk/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-host/lib/mk/printf_stdio.mk b/base-host/lib/mk/printf_stdio.mk new file mode 100644 index 000000000..8f910d8e9 --- /dev/null +++ b/base-host/lib/mk/printf_stdio.mk @@ -0,0 +1,3 @@ +SRC_CC = printf_stdio.cc + +vpath printf_stdio.cc $(REP_DIR)/src/lib/printf_stdio diff --git a/base-host/src/base/env/parent.cc b/base-host/src/base/env/parent.cc new file mode 100644 index 000000000..efc9378a0 --- /dev/null +++ b/base-host/src/base/env/parent.cc @@ -0,0 +1,24 @@ +/* + * \brief Access to pseudo parent capability + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +namespace Genode { + + /** + * Return parent capability + * + * This function is normally provided by the 'startup' library. + */ + Native_capability parent_cap() { return Native_capability(); } +} diff --git a/base-host/src/base/ipc/ipc.cc b/base-host/src/base/ipc/ipc.cc new file mode 100644 index 000000000..3055926c5 --- /dev/null +++ b/base-host/src/base/ipc/ipc.cc @@ -0,0 +1,77 @@ +/* + * \brief Dummy implementation of the IPC API + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +using namespace Genode; + + +/***************** + ** Ipc_ostream ** + *****************/ + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) +: + Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()), + _snd_msg(snd_msg), _dst(dst) +{ } + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() +{ } + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) : + Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), + _rcv_msg(rcv_msg) +{ } + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() { } + + +Ipc_client::Ipc_client(Native_capability const &srv, + Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) +{ } + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_wait() { } + + +void Ipc_server::_reply() { } + + +void Ipc_server::_reply_wait() { } + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(Native_capability(), snd_msg) +{ } diff --git a/base-host/src/base/lock/lock_helper.h b/base-host/src/base/lock/lock_helper.h new file mode 100644 index 000000000..c3197875a --- /dev/null +++ b/base-host/src/base/lock/lock_helper.h @@ -0,0 +1,52 @@ +/* + * \brief Dummy helper functions for the Lock implementation + * \author Norman Feske + * \date 2009-10-02 + * + * For documentation about the interface, please revisit the 'base-pistachio' + * implementation. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + + +static inline void thread_yield() { } + + +static bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + return true; +} + + +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + return -1; +} + + +static inline Genode::Native_thread_id thread_invalid_id() +{ + return -1; +} + + +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return false; +} + + +static inline void thread_switch_to(Genode::Native_thread_id tid) +{ } + + +static inline void thread_stop_myself() { while (true); } diff --git a/base-host/src/base/pager/pager.cc b/base-host/src/base/pager/pager.cc new file mode 100644 index 000000000..34c91e9f3 --- /dev/null +++ b/base-host/src/base/pager/pager.cc @@ -0,0 +1,57 @@ +/* + * \brief Dummy pager framework + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + + +/********************** + ** Pager activation ** + **********************/ + +void Pager_activation_base::entry() +{ + while (1); +} + + +/********************** + ** Pager entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *, Pager_activation_base *a) +: _activation(a) +{ _activation->ep(this); } + + +void Pager_entrypoint::dissolve(Pager_object *obj) +{ + remove(obj); +} + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + /* return invalid capability if no activation is present */ + if (!_activation) return Pager_capability(); + + Native_capability cap = Native_capability(_activation->cap().tid(), obj->badge()); + + /* add server object to object pool */ + obj->cap(cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return Pager_capability(cap); +} diff --git a/base-host/src/core/context_area.cc b/base-host/src/core/context_area.cc new file mode 100644 index 000000000..0cf455b06 --- /dev/null +++ b/base-host/src/core/context_area.cc @@ -0,0 +1,90 @@ +/* + * \brief Support code for the thread API + * \author Norman Feske + * \date 2010-01-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + + +/** + * Region-manager session for allocating thread contexts + */ +class Context_area_rm_session : public Genode::Rm_session +{ + public: + + /** + * Attach backing store to thread-context area + */ + Local_addr attach(Genode::Dataspace_capability ds_cap, + Genode::size_t size, Genode::off_t offset, + bool use_local_addr, Local_addr local_addr) + { + PWRN("not implemented"); + return local_addr; + } + + void detach(Local_addr local_addr) { + PWRN("context area detach from 0x%p - not implemented", (void *)local_addr); } + + Genode::Pager_capability add_client(Genode::Thread_capability) { + return Genode::Pager_capability(); } + + void fault_handler(Genode::Signal_context_capability) { } + + State state() { return State(); } + + Genode::Dataspace_capability dataspace() { + return Genode::Dataspace_capability(); } +}; + + +class Context_area_ram_session : public Genode::Ram_session +{ + public: + + Genode::Ram_dataspace_capability alloc(Genode::size_t size) { + return Genode::Ram_dataspace_capability(); } + + void free(Genode::Ram_dataspace_capability) { } + + int ref_account(Genode::Ram_session_capability) { return 0; } + + int transfer_quota(Genode::Ram_session_capability, Genode::size_t) { return 0; } + + Genode::size_t quota() { return 0; } + + Genode::size_t used() { return 0; } +}; + + +/** + * Return single instance of the context-area RM and RAM session + */ +namespace Genode { + + Rm_session *env_context_area_rm_session() + { + static Context_area_rm_session inst; + return &inst; + } + + Ram_session *env_context_area_ram_session() + { + static Context_area_ram_session inst; + return &inst; + } +} + diff --git a/base-host/src/core/core_rm_session.cc b/base-host/src/core/core_rm_session.cc new file mode 100644 index 000000000..0c00829f2 --- /dev/null +++ b/base-host/src/core/core_rm_session.cc @@ -0,0 +1,30 @@ +/* + * \brief Core-local RM session + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + +using namespace Genode; + + +Rm_session::Local_addr +Core_rm_session::attach(Dataspace_capability ds_cap, size_t size, + off_t offset, bool use_local_addr, + Rm_session::Local_addr local_addr) +{ + PWRN("not implemented"); + return 0; +} diff --git a/base-host/src/core/include/core_rm_session.h b/base-host/src/core/include/core_rm_session.h new file mode 100644 index 000000000..ac46b2640 --- /dev/null +++ b/base-host/src/core/include/core_rm_session.h @@ -0,0 +1,48 @@ +/* + * \brief Core-local region manager session + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_RM_SESSION_H_ +#define _CORE__INCLUDE__CORE_RM_SESSION_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +namespace Genode { + + class Core_rm_session : public Rm_session + { + public: + + Core_rm_session(Rpc_entrypoint *ds_ep) { } + + Local_addr attach(Dataspace_capability ds_cap, size_t size=0, + off_t offset=0, bool use_local_addr = false, + Local_addr local_addr = 0); + + void detach(Local_addr local_addr) { } + + Pager_capability add_client(Thread_capability thread) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability handler) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_RM_SESSION_H_ */ diff --git a/base-host/src/core/include/platform.h b/base-host/src/core/include/platform.h new file mode 100644 index 000000000..0b467602e --- /dev/null +++ b/base-host/src/core/include/platform.h @@ -0,0 +1,50 @@ +/* + * \brief Platform interface + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_H_ +#define _CORE__INCLUDE__PLATFORM_H_ + +/* core includes */ +#include + +namespace Genode { + + class Platform : public Platform_generic + { + public: + + /** + * Constructor + */ + Platform(); + + + /******************************** + ** Generic platform interface ** + ********************************/ + + Range_allocator *ram_alloc() { return 0; } + Range_allocator *io_mem_alloc() { return 0; } + Range_allocator *io_port_alloc() { return 0; } + Range_allocator *irq_alloc() { return 0; } + Range_allocator *region_alloc() { return 0; } + Allocator *core_mem_alloc() { return 0; } + addr_t vm_start() const { return 0; } + size_t vm_size() const { return 0; } + Rom_fs *rom_fs() { return 0; } + + void wait_for_exit(); + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-host/src/core/include/platform_pd.h b/base-host/src/core/include/platform_pd.h new file mode 100644 index 000000000..7c051aba3 --- /dev/null +++ b/base-host/src/core/include/platform_pd.h @@ -0,0 +1,59 @@ +/* + * \brief Protection-domain facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_PD_H_ +#define _CORE__INCLUDE__PLATFORM_PD_H_ + +#include + +namespace Genode { + + class Platform_thread; + class Platform_pd + { + public: + + /** + * Constructors + */ + Platform_pd(bool core); + Platform_pd(signed pd_id = -1, bool create = true); + + /** + * Destructor + */ + ~Platform_pd(); + + /** + * Bind thread to protection domain + * + * \return 0 on success or + * -1 if thread ID allocation failed. + */ + int bind_thread(Platform_thread *thread); + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread *thread); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent) { return 0; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ diff --git a/base-host/src/core/include/platform_thread.h b/base-host/src/core/include/platform_thread.h new file mode 100644 index 000000000..e62719001 --- /dev/null +++ b/base-host/src/core/include/platform_thread.h @@ -0,0 +1,106 @@ +/* + * \brief Thread facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__PLATFORM_THREAD_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Genode { + + class Platform_pd; + class Platform_thread + { + public: + + enum { THREAD_INVALID = -1 }; /* invalid thread number */ + + /** + * Constructor + */ + Platform_thread(const char *name = 0, unsigned priority = 0, + int thread_id = THREAD_INVALID); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * \param cpu_no target cpu + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp, unsigned int cpu_no = 0); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Set pager + */ + void pager(Pager_object *pager) { } + + /** + * Return identification of thread when faulting + */ + unsigned long pager_object_badge() const; + + /** + * Set the executing CPU for this thread. + */ + void set_cpu(unsigned int cpu_no); + + /** + * Get thread name + */ + const char *name() const { return "noname"; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-host/src/core/include/util.h b/base-host/src/core/include/util.h new file mode 100644 index 000000000..4f50f05f9 --- /dev/null +++ b/base-host/src/core/include/util.h @@ -0,0 +1,60 @@ +/* + * \brief Core-internal utilities + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__UTIL_H_ +#define _CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include + +namespace Genode { + + inline size_t get_page_size_log2() { return 12; } + inline size_t get_page_size() { return 1 << get_page_size_log2(); } + inline addr_t get_page_mask() { return ~(get_page_size() - 1); } + inline addr_t trunc_page(addr_t addr) { return addr & get_page_mask(); } + inline addr_t round_page(addr_t addr) { return trunc_page(addr + get_page_size() - 1); } + + /** + * Select source used for map operations + */ + inline addr_t map_src_addr(addr_t core_local, addr_t phys) { return phys; } + + /** + * Return highest supported flexpage size for the given mapping size + * + * This function is called by the page-fault handler to determine the + * mapping granularity to be used for a page-fault answer. If a kernel + * supports flexible page sizes, this function can just return the + * argument. If a kernel only supports a certain set of map sizes such + * as 4K and 4M, this function should select one of those smaller or + * equal to the argument. + */ + inline size_t constrain_map_size_log2(size_t size_log2) + { + return get_page_size_log2(); + } + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long faulter_badge) + { + printf("%s (%s pf_addr=%p pf_ip=%p from %02lx)", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, + faulter_badge); + } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-host/src/core/io_mem_session_support.cc b/base-host/src/core/io_mem_session_support.cc new file mode 100644 index 000000000..6b1f5715e --- /dev/null +++ b/base-host/src/core/io_mem_session_support.cc @@ -0,0 +1,27 @@ +/* + * \brief Implementation of the IO_MEM session interface + * \author Norman Feske + * \date 2009-03-29 + * + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include + + +using namespace Genode; + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ } + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ return 0; } diff --git a/base-host/src/core/io_port_session_component.cc b/base-host/src/core/io_port_session_component.cc new file mode 100644 index 000000000..c90a87b7e --- /dev/null +++ b/base-host/src/core/io_port_session_component.cc @@ -0,0 +1,58 @@ +/* + * \brief Implementation of the IO_PORT session interface + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "io_port_session_component.h" + +using namespace Genode; + + +/************** + ** Port API ** + **************/ + +unsigned char Io_port_session_component::inb(unsigned short address) { + return 0; } + + +unsigned short Io_port_session_component::inw(unsigned short address) { + return 0; } + + +unsigned Io_port_session_component::inl(unsigned short address) { + return 0; } + + +void Io_port_session_component::outb(unsigned short address, unsigned char value) +{ } + + +void Io_port_session_component::outw(unsigned short address, unsigned short value) +{ } + + +void Io_port_session_component::outl(unsigned short address, unsigned value) +{ } + + +/****************************** + ** Constructor / destructor ** + ******************************/ + +Io_port_session_component::Io_port_session_component(Range_allocator *io_port_alloc, + const char *args) +: _io_port_alloc(io_port_alloc) +{ } + + +Io_port_session_component::~Io_port_session_component() +{ } diff --git a/base-host/src/core/irq_session_component.cc b/base-host/src/core/irq_session_component.cc new file mode 100644 index 000000000..4e5226958 --- /dev/null +++ b/base-host/src/core/irq_session_component.cc @@ -0,0 +1,54 @@ +/* + * \brief Implementation of IRQ session component + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + + +using namespace Genode; + + +bool Irq_session_component::Irq_control_component::associate_to_irq(unsigned irq) +{ + PWRN("not implemented"); + return true; +} + + +void Irq_session_component::wait_for_irq() +{ + PWRN("not implemented"); +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _ep(cap_session, STACK_SIZE, "irqctrl"), + _irq_attached(false), + _control_client(Capability()) +{ + PWRN("not implemented"); +} + + +Irq_session_component::~Irq_session_component() +{ + PERR("not yet implemented"); +} + diff --git a/base-host/src/core/platform.cc b/base-host/src/core/platform.cc new file mode 100644 index 000000000..96beed570 --- /dev/null +++ b/base-host/src/core/platform.cc @@ -0,0 +1,41 @@ +/* + * \brief Platform interface implementation + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +using namespace Genode; + + +Platform::Platform() +{ + PWRN("not implemented"); +} + + +/******************************** + ** Generic platform interface ** + ********************************/ + +void Platform::wait_for_exit() +{ + sleep_forever(); +} + + +void Core_parent::exit(int exit_value) { } diff --git a/base-host/src/core/platform_pd.cc b/base-host/src/core/platform_pd.cc new file mode 100644 index 000000000..4d4487073 --- /dev/null +++ b/base-host/src/core/platform_pd.cc @@ -0,0 +1,55 @@ +/* + * \brief Protection-domain facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + +using namespace Genode; + + +/*************************** + ** Public object members ** + ***************************/ + +int Platform_pd::bind_thread(Platform_thread *thread) +{ + PWRN("not implemented"); + return -1; +} + + +void Platform_pd::unbind_thread(Platform_thread *thread) +{ + PWRN("not implemented"); +} + + +Platform_pd::Platform_pd(bool core) +{ + PWRN("not yet implemented"); +} + + +Platform_pd::Platform_pd(signed pd_id, bool create) +{ + PWRN("not yet implemented"); +} + + +Platform_pd::~Platform_pd() +{ + PWRN("not yet implemented"); +} diff --git a/base-host/src/core/platform_thread.cc b/base-host/src/core/platform_thread.cc new file mode 100644 index 000000000..6d9dfff82 --- /dev/null +++ b/base-host/src/core/platform_thread.cc @@ -0,0 +1,77 @@ +/* + * \brief Thread facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + +using namespace Genode; + + +void Platform_thread::set_cpu(unsigned int cpu_no) +{ + PERR("not yet implemented"); +} + + +int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no) +{ + PWRN("not implemented"); + return -1; +} + + +void Platform_thread::pause() +{ + PWRN("not implemented"); +} + + +void Platform_thread::resume() +{ + PWRN("not implemented"); +} + + +int Platform_thread::state(Thread_state *state_dst) +{ + PWRN("not implemented"); + return -1; +} + + +void Platform_thread::cancel_blocking() +{ + PWRN("not implemented"); +} + + +unsigned long Platform_thread::pager_object_badge() const +{ + PWRN("not implemented"); + return -1; +} + + +Platform_thread::Platform_thread(const char *name, unsigned, int thread_id) +{ + PWRN("not implemented"); +} + + +Platform_thread::~Platform_thread() +{ + PWRN("not implemented"); +} diff --git a/base-host/src/core/ram_session_support.cc b/base-host/src/core/ram_session_support.cc new file mode 100644 index 000000000..ae7dabee8 --- /dev/null +++ b/base-host/src/core/ram_session_support.cc @@ -0,0 +1,29 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + + +using namespace Genode; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { } + +void Ram_session_component::_clear_ds (Dataspace_component *ds) +{ + PWRN("not implemented"); +} diff --git a/base-host/src/core/rm_session_support.cc b/base-host/src/core/rm_session_support.cc new file mode 100644 index 000000000..4e8e309a3 --- /dev/null +++ b/base-host/src/core/rm_session_support.cc @@ -0,0 +1,26 @@ +/* + * \brief RM-session implementation + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + +using namespace Genode; + + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + PWRN("not implemented"); +} diff --git a/base-host/src/core/target.inc b/base-host/src/core/target.inc new file mode 100644 index 000000000..94d609385 --- /dev/null +++ b/base-host/src/core/target.inc @@ -0,0 +1,49 @@ +TARGET = core +LIBS = cxx ipc heap core_printf process pager lock \ + raw_signal raw_server + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = \ + main.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_host.cc \ + thread_bootstrap.cc \ + platform_thread.cc \ + platform_pd.cc \ + platform.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + core_rm_session.cc \ + context_area.cc + +INC_DIR = $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include + +vpath main.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath signal_source_component.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath %.cc $(REP_DIR)/src/core +vpath thread_bootstrap.cc $(BASE_DIR)/src/base/thread +vpath thread.cc $(BASE_DIR)/src/base/thread + diff --git a/base-host/src/core/target.mk b/base-host/src/core/target.mk new file mode 100644 index 000000000..310689bf0 --- /dev/null +++ b/base-host/src/core/target.mk @@ -0,0 +1 @@ +include $(PRG_DIR)/target.inc diff --git a/base-host/src/core/thread_host.cc b/base-host/src/core/thread_host.cc new file mode 100644 index 000000000..da171551c --- /dev/null +++ b/base-host/src/core/thread_host.cc @@ -0,0 +1,23 @@ +/* + * \brief Implementation of Thread API interface for core + * \author Norman Feske + * \date 2006-05-03 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +using namespace Genode; + +void Thread_base::_init_platform_thread() { } +void Thread_base::_deinit_platform_thread() { } +void Thread_base::start() { } +void Thread_base::cancel_blocking() { } diff --git a/base-host/src/lib/printf_stdio/printf_stdio.cc b/base-host/src/lib/printf_stdio/printf_stdio.cc new file mode 100644 index 000000000..a1981ee00 --- /dev/null +++ b/base-host/src/lib/printf_stdio/printf_stdio.cc @@ -0,0 +1,35 @@ +/* + * \brief Genode::printf back-end for stdio + * \author Norman Feske + * \date 2009-10-06 + * + * This library can be used by unit test executed on the host platform to + * direct output from the Genode framework to stdout. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + + +void Genode::printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + ::vprintf(format, list); + + va_end(list); +} + + +void Genode::vprintf(const char *format, va_list list) +{ + ::vprintf(format, list); +} diff --git a/base-linux/README b/base-linux/README new file mode 100644 index 000000000..5322ef84f --- /dev/null +++ b/base-linux/README @@ -0,0 +1 @@ +This repository contains the Linux-specific implementation of Genode. diff --git a/base-linux/etc/specs.conf b/base-linux/etc/specs.conf new file mode 100644 index 000000000..7574aad19 --- /dev/null +++ b/base-linux/etc/specs.conf @@ -0,0 +1,22 @@ +# +# Description of build platform +# + +# +# If you want to build the Linux-specific Genode +# binaries, use this config option. +# +ifeq ($(shell uname -m),x86_64) +SPECS ?= genode linux_x86_64 sdl +else +SPECS ?= genode linux_x86_32 sdl +endif + +# +# If you want to build for the host platform, +# use the following config option. +# You need to specify '32bit' additionally to 'host' +# to include the 32bit-specific Genode include path +# containing integer definitions. +# +#SPECS ?= host 32bit diff --git a/base-linux/include/base/ipc_msgbuf.h b/base-linux/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..c0f554830 --- /dev/null +++ b/base-linux/include/base/ipc_msgbuf.h @@ -0,0 +1,64 @@ +/* + * \brief Linux-specific layout of IPC message buffer + * \author Norman Feske + * \date 2006-06-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +namespace Genode { + + /** + * IPC message buffer layout + */ + class Msgbuf_base + { + protected: + + Genode::size_t _size; + char _msg_start[]; /* symbol marks start of message buffer data */ + + /* + * No member variables are allowed beyond this point! + */ + + public: + + char buf[]; + + /** + * Return size of message buffer + */ + inline Genode::size_t size() const { return _size; }; + + /** + * Return address of message buffer + */ + inline void *addr() { return &_msg_start[0]; }; + }; + + + /** + * Pump up IPC message buffer to specified buffer size + */ + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + }; +} + + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-linux/include/base/local_interface.h b/base-linux/include/base/local_interface.h new file mode 100644 index 000000000..e7f08096c --- /dev/null +++ b/base-linux/include/base/local_interface.h @@ -0,0 +1,89 @@ +/* + * \brief Support for process-local pseudo capabilities + * \author Norman Feske + * \date 2011-11-21 + * + * Pseudo capabilities have a zero 'tid' and a non-zero 'local_name'. The local + * name is a pointer to the local object implementing the interface. Pseudo + * capabilties are valid only as arguments for local services that are prepared + * for it. I.e., the locally implemented RM service accepts pseudo dataspace + * capabilities that refer to managed dataspaces. Or the Linux-specific + * 'Rm_session_client' takes a pseudo capability to target RM-session + * invokations to the local implementation. + * + * Please note that this header file is not part of the official Genode API. + * It exists on no other platform than Linux and is meant for Genode-internal + * use only. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__LOCAL_INTERFACE_H_ +#define _INCLUDE__BASE__LOCAL_INTERFACE_H_ + +#include +#include + +namespace Genode { + + /** + * Common base class of local interface implementations + */ + struct Local_interface + { + virtual ~Local_interface() { } + + /** + * Exception type + */ + class Non_local_capability { }; + + /** + * Convert pseudo capability to pointer to locally implemented session + * + * \param IF interface type + * \param cap pseudo capability + * + * \throw Non_local_capability if the argument does not refer to a + * locally implemented interface + */ + template + static IF *deref(Capability cap) + { + /* check if this is a pseudo capability */ + if (cap.tid() != 0 || !cap.local_name()) + throw Non_local_capability(); + + /* + * For a pseudo capability, the 'local_name' points to the local + * session object of the correct type. + */ + IF *interface = dynamic_cast((Local_interface *)cap.local_name()); + if (!interface) + throw Non_local_capability(); + + return interface; + } + + /** + * Construct pseudo capability to process-local interface implementation + * + * \param IF interface type + * \param interface pointer to local interface implementation + * \return pseudo capability + * + */ + template + static Capability capability(IF *interface) + { + return reinterpret_cap_cast(Native_capability(0, (long)interface)); + }; + }; +} + +#endif /* _INCLUDE__BASE__LOCAL_INTERFACE_H_ */ diff --git a/base-linux/include/base/native_types.h b/base-linux/include/base/native_types.h new file mode 100644 index 000000000..033eb53b9 --- /dev/null +++ b/base-linux/include/base/native_types.h @@ -0,0 +1,135 @@ +/* + * \brief Native types + * \author Norman Feske + * \date 2007-10-15 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +/* + * We cannot just include and here + * because this would impy the nested inclusion of a myriad + * of Linux types and would pollute the namespace for everyone + * who includes this header file. We want to cleanly separate + * Genode from POSIX. + */ + +namespace Genode { + + /** + * Native lock type + * + * We are using a sleeping spinlock as lock implementation on Linux. This + * is a temporary solution until we have implemented futex-based locking. + * In a previous version, we have relied on POSIX semaphores as provided by + * the glibc. However, relying on the glibc badly interferes with a custom + * libc implementation. The glibc semaphore implementation expects to find + * a valid pthread structure via the TLS pointer. We do not have such a + * structure because we create threads via the 'clone' system call rather + * than 'pthread_create'. Hence we have to keep the base framework clean + * from glibc usage altogether. + */ + typedef volatile int Native_lock; + + /** + * Thread ID used in lock implementation + * + * Unfortunately, both - PID and TID - are needed for lx_tgkill() in + * thread_check_stopped_and_restart(). + */ + struct Native_thread_id + { + unsigned int tid; /* Native thread ID type as returned by the + 'clone' system call */ + unsigned int pid; /* process ID (resp. thread-group ID) */ + + Native_thread_id() : tid(0), pid(0) { } + Native_thread_id(unsigned int tid, unsigned int pid) + : tid(tid), pid(pid) { } + }; + + /** + * Native thread contains more thread-local data than just the ID + * + * A thread needs two sockets as it may be a server that depends on another + * service during request processing. If the server socket would be used for + * the client call, the server thread may be unblocked by further requests + * from its clients. In other words, the additional client socket provides + * closed-receive semantics in calls. An even better solution is to use + * SCM_RIGHTS messages to send a client socket descriptor with the request. + */ + struct Native_thread : Native_thread_id + { + int client; /* socket used as IPC client */ + int server; /* socket used as IPC server */ + + Native_thread() : client(-1), server(-1) { } + }; + + inline bool operator == (Native_thread_id t1, Native_thread_id t2) { + return (t1.tid == t2.tid) && (t1.pid == t2.pid); } + + inline bool operator != (Native_thread_id t1, Native_thread_id t2) { + return (t1.tid != t2.tid) || (t1.pid != t2.pid); } + + /** + * Empty UTCB type expected by the thread library, unused on Linux + */ + typedef struct { } Native_utcb; + + /* + * On Linux, the local_name member of a capability is global + * to the whole system. Therefore, capabilities are to be + * created at a central place that prevents id clashes. + */ + class Native_capability + { + protected: + + long _tid; /* target thread */ + long _local_name; + + public: + + /** + * Default constructor + */ + Native_capability() : _tid(0), _local_name(0) { } + + long local_name() const { return _local_name; } + + bool valid() const { return _tid != 0; } + + + /**************************************************** + ** Functions to be used by the Linux backend only ** + ****************************************************/ + + /** + * Constructor + * + * This constructor can be called to create a Linux + * capability by hand. It must never be used from + * generic code! + */ + Native_capability(long tid, long local_name) + : _tid(tid), _local_name(local_name) { } + + /** + * Access raw capability data + */ + long tid() const { return _tid; } + }; + + typedef int Native_connection_state; /* socket descriptor */ +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-linux/include/base/pager.h b/base-linux/include/base/pager.h new file mode 100644 index 000000000..bd4b61f00 --- /dev/null +++ b/base-linux/include/base/pager.h @@ -0,0 +1,43 @@ +/* + * \brief Paging-server framework + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-04-28 + * + * Linux dummies + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__PAGER_H_ +#define _INCLUDE__BASE__PAGER_H_ + +#include +#include +#include + +namespace Genode { + + struct Pager_object + { + virtual ~Pager_object() { } + + void exception_handler(Signal_context_capability) { } + }; + + class Pager_activation_base { }; + struct Pager_entrypoint + { + Pager_entrypoint(Cap_session *, Pager_activation_base *) { } + + Pager_object *obj_by_cap(Pager_capability) { return 0; } + }; + template class Pager_activation : public Pager_activation_base { }; +} + +#endif /* _INCLUDE__BASE__PAGER_H_ */ diff --git a/base-linux/include/base/platform_env.h b/base-linux/include/base/platform_env.h new file mode 100644 index 000000000..d98332fc5 --- /dev/null +++ b/base-linux/include/base/platform_env.h @@ -0,0 +1,379 @@ +/* + * \brief Linux-specific environment + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__PLATFORM_ENV_H_ +#define _INCLUDE__BASE__PLATFORM_ENV_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + class Platform_env : public Env + { + private: + + /************************** + ** Local region manager ** + **************************/ + + class Region + { + private: + + addr_t _start; + off_t _offset; + Dataspace_capability _ds; + size_t _size; + + /** + * Return offset of first byte after the region + */ + addr_t _end() const { return _start + _size; } + + public: + + Region() : _start(0), _offset(0), _size(0) { } + + Region(addr_t start, off_t offset, Dataspace_capability ds, size_t size) + : _start(start), _offset(offset), _ds(ds), _size(size) { } + + bool used() const { return _size > 0; } + addr_t start() const { return _start; } + off_t offset() const { return _offset; } + size_t size() const { return _size; } + Dataspace_capability dataspace() const { return _ds; } + + bool intersects(Region const &r) const + { + return (r.start() < _end()) && (_start < r._end()); + } + }; + + + /** + * Meta data about dataspaces attached to an RM session + */ + class Region_map + { + public: + + enum { MAX_REGIONS = 4096 }; + + private: + + Region _map[MAX_REGIONS]; + + bool _id_valid(int id) const { + return (id >= 0 && id < MAX_REGIONS); } + + public: + + /** + * Add region to region map + * + * \return region ID, or + * -1 if out of metadata, or + * -2 if region conflicts existing region + */ + int add_region(Region const ®ion) + { + /* + * Check for region conflicts + */ + for (int i = 0; i < MAX_REGIONS; i++) { + if (_map[i].intersects(region)) + return -2; + } + + /* + * Allocate new region metadata + */ + int i; + for (i = 0; i < MAX_REGIONS; i++) + if (!_map[i].used()) break; + + if (i == MAX_REGIONS) { + PERR("maximum number of %d regions reached", + MAX_REGIONS); + return -1; + } + + _map[i] = region; + return i; + } + + Region region(int id) const + { + return _id_valid(id) ? _map[id] : Region(); + } + + Region lookup(addr_t start) + { + for (int i = 0; i < MAX_REGIONS; i++) + if (_map[i].start() == start) + return _map[i]; + return Region(); + } + + void remove_region(addr_t start) + { + for (int i = 0; i < MAX_REGIONS; i++) + if (_map[i].start() == start) + _map[i] = Region(); + } + }; + + + /* + * On Linux, we use a local region manager session + * that attaches dataspaces via mmap to the local + * address space. + */ + class Rm_session_mmap : public Local_interface, + public Rm_session, + public Dataspace + { + private: + + Lock _lock; /* protect '_rmap' */ + Region_map _rmap; + bool const _sub_rm; /* false if RM session is root */ + size_t const _size; + + /** + * Base offset of the RM session + * + * For a normal RM session (the one that comes with the + * 'env()', this value is zero. If the RM session is + * used as nested dataspace, '_base' contains the address + * where the managed dataspace is attached in the root RM + * session. + * + * Note that a managed dataspace cannot be attached more + * than once. Furthermore, managed dataspace cannot be + * attached to another managed dataspace. The nested + * dataspace emulation is solely implemented to support + * the common use case of managed dataspaces as mechanism + * to reserve parts of the local address space from being + * populated by the 'env()->rm_session()'. (i.e., for the + * context area, or for the placement of consecutive + * shared-library segments) + */ + addr_t _base; + + bool _is_attached() const { return _base > 0; } + + void _add_to_rmap(Region const &); + + public: + + Rm_session_mmap(bool sub_rm, size_t size = ~0) + : _sub_rm(sub_rm), _size(size), _base(0) { } + + ~Rm_session_mmap() + { + /* detach sub RM session when destructed */ + if (_sub_rm && _is_attached()) + env()->rm_session()->detach((void *)_base); + } + + + /************************************** + ** Region manager session interface ** + **************************************/ + + Local_addr attach(Dataspace_capability ds, size_t size, + off_t, bool, Local_addr); + + void detach(Local_addr local_addr); + + Pager_capability add_client(Thread_capability thread) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability handler) { } + + State state() { return State(); } + + + /************************* + ** Dataspace interface ** + *************************/ + + size_t size() { return _size; } + + addr_t phys_addr() { return 0; } + + bool writable() { return true; } + + /** + * Return pseudo dataspace capability of the RM session + * + * The capability returned by this function is only usable + * as argument to 'Rm_session_mmap::attach'. It is not a + * real capability. + */ + Dataspace_capability dataspace() + { + return Local_interface::capability(this); + } + }; + + + class Expanding_ram_session_client : public Ram_session_client + { + Ram_session_capability _cap; + + public: + + Expanding_ram_session_client(Ram_session_capability cap) + : Ram_session_client(cap), _cap(cap) { } + + Ram_dataspace_capability alloc(size_t size) { + bool try_again; + do { + try_again = false; + try { + return Ram_session_client::alloc(size); + + } catch (Ram_session::Out_of_metadata) { + + /* give up if the error occurred a second time */ + if (try_again) + break; + + PINF("upgrade quota donation for Env::RAM session"); + env()->parent()->upgrade(_cap, "ram_quota=8K"); + try_again = true; + } + } while (try_again); + + return Ram_dataspace_capability(); + } + }; + + + /** + * Local interceptor of parent requests + * + * On Linux, we need to intercept calls to the parent interface to + * implement the RM service locally. This particular service is + * used for creating managed dataspaces, which allow the + * reservation of parts of the local address space from being + * automatically managed by the 'env()->rm_session()'. + * + * All requests that do not refer to the RM service are passed + * through the real parent interface. + */ + class Local_parent : public Parent_client + { + public: + + /********************** + ** Parent interface ** + **********************/ + + Session_capability session(Service_name const &, + Session_args const &); + void close(Session_capability); + + /** + * Constructor + * + * \param parent_cap real parent capability used to + * promote requests to non-local + * services + */ + Local_parent(Parent_capability parent_cap); + }; + + + /************************************* + ** Linux-specific helper functions ** + *************************************/ + + /** + * Read Unix environment variable as long value + */ + static unsigned long _get_env_ulong(const char *key); + + + Parent_capability _parent_cap() + { + long tid = _get_env_ulong("parent_tid"); + long local_name = _get_env_ulong("parent_local_name"); + + /* produce typed capability manually */ + return reinterpret_cap_cast(Native_capability(tid, local_name)); + } + + + /******************************* + ** Platform-specific members ** + *******************************/ + + Local_parent _parent; + Ram_session_capability _ram_session_cap; + Expanding_ram_session_client _ram_session_client; + Cpu_session_client _cpu_session_client; + Rm_session_mmap _rm_session_mmap; + Heap _heap; + + public: + + /** + * Standard constructor + */ + Platform_env() + : + _parent(_parent_cap()), + _ram_session_cap(static_cap_cast(parent()->session("Env::ram_session", ""))), + _ram_session_client(_ram_session_cap), + _cpu_session_client(static_cap_cast(parent()->session("Env::cpu_session", ""))), + _rm_session_mmap(false), + _heap(&_ram_session_client, &_rm_session_mmap) + { } + + /** + * Destructor + */ + ~Platform_env() { parent()->exit(0); } + + + /******************* + ** Env interface ** + *******************/ + + Parent *parent() { return &_parent; } + Ram_session *ram_session() { return &_ram_session_client; } + Ram_session_capability ram_session_cap() { return _ram_session_cap; } + Rm_session *rm_session() { return &_rm_session_mmap; } + Heap *heap() { return &_heap; } + Cpu_session *cpu_session() { return &_cpu_session_client; } + Pd_session *pd_session() { return 0; } + }; +} + +#endif /* _INCLUDE__BASE__PLATFORM_ENV_H_ */ diff --git a/base-linux/include/linux_dataspace/client.h b/base-linux/include/linux_dataspace/client.h new file mode 100644 index 000000000..284bb27d0 --- /dev/null +++ b/base-linux/include/linux_dataspace/client.h @@ -0,0 +1,47 @@ +/* + * \brief Linux-specific dataspace client interface + * \author Norman Feske + * \date 2006-05-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__LINUX_DATASPACE__CLIENT_H_ +#define _INCLUDE__LINUX_DATASPACE__CLIENT_H_ + +#include +#include +#include +#include + +namespace Genode { + + struct Linux_dataspace_client : Rpc_client + { + explicit Linux_dataspace_client(Dataspace_capability ds) + : Rpc_client(static_cap_cast(ds)) { } + + + /********************************* + ** Generic dataspace interface ** + *********************************/ + + size_t size() { return call(); } + addr_t phys_addr() { return call(); } + bool writable() { return call(); } + + + /**************************************** + ** Linux-specific dataspace interface ** + ****************************************/ + + Filename fname() { return call(); } + }; +} + +#endif /* _INCLUDE__LINUX_DATASPACE__CLIENT_H_ */ diff --git a/base-linux/include/linux_dataspace/linux_dataspace.h b/base-linux/include/linux_dataspace/linux_dataspace.h new file mode 100644 index 000000000..432cadd0f --- /dev/null +++ b/base-linux/include/linux_dataspace/linux_dataspace.h @@ -0,0 +1,47 @@ +/* + * \brief Linux-specific dataspace interface + * \author Norman Feske + * \date 2006-07-05 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__LINUX_DATASPACE__LINUX_DATASPACE_H_ +#define _INCLUDE__LINUX_DATASPACE__LINUX_DATASPACE_H_ + +#include +#include +#include +#include + +namespace Genode { + + struct Linux_dataspace : Dataspace + { + enum { FNAME_LEN = 32 }; + + struct Filename { char buf[FNAME_LEN]; }; + + virtual ~Linux_dataspace() { } + + /** + * Request name of file that represents the dataspace on Linux + */ + virtual Filename fname() = 0; + + /********************* + ** RPC declaration ** + *********************/ + + + GENODE_RPC(Rpc_fname, Filename, fname); + GENODE_RPC_INTERFACE_INHERIT(Dataspace, Rpc_fname); + }; +} + +#endif /* _INCLUDE__LINUX_DATASPACE__LINUX_DATASPACE_H_ */ diff --git a/base-linux/include/rm_session/client.h b/base-linux/include/rm_session/client.h new file mode 100644 index 000000000..c06312ff3 --- /dev/null +++ b/base-linux/include/rm_session/client.h @@ -0,0 +1,59 @@ +/* + * \brief Pseudo RM-session client stub targeting the process-local RM service + * \author Norman Feske + * \date 2011-11-21 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RM_SESSION__CLIENT_H_ +#define _INCLUDE__RM_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Rm_session_client : Rm_session + { + Rm_session_capability const _cap; + + /** + * Return pointer to locally implemented RM session + * + * \throw Local_interface::Non_local_capability + */ + Rm_session *_local() const { return Local_interface::deref(_cap); } + + explicit Rm_session_client(Rm_session_capability session) + : _cap(session) { } + + Local_addr attach(Dataspace_capability ds, size_t size, off_t offset, + bool use_local_addr, Local_addr local_addr) + { + return _local()->attach(ds, size, offset, use_local_addr, local_addr); + } + + void detach(Local_addr local_addr) { + return _local()->detach(local_addr); } + + Pager_capability add_client(Thread_capability thread) { + return _local()->add_client(thread); } + + void fault_handler(Signal_context_capability handler) { + return _local()->fault_handler(handler); } + + State state() { + return _local()->state(); } + + Dataspace_capability dataspace() { + return _local()->dataspace(); } + }; +} + +#endif /* _INCLUDE__RM_SESSION__CLIENT_H_ */ diff --git a/base-linux/lib/import/import-lx_hybrid.mk b/base-linux/lib/import/import-lx_hybrid.mk new file mode 100644 index 000000000..4c61c5904 --- /dev/null +++ b/base-linux/lib/import/import-lx_hybrid.mk @@ -0,0 +1,92 @@ +# +# Make Linux headers of the host platform available to the program +# +include $(call select_from_repositories,lib/import/import-syscall.mk) + +# +# Manually supply all library search paths of the host compiler to our tool +# chain. +# +HOST_LIB_SEARCH_DIRS := $(shell cc -print-search-dirs | grep libraries |\ + sed "s/.*=//" | sed "s/:/ /g" |\ + sed "s/\/ / /g" | sed "s/\/\$$//") +# +# Add search path for 'limits.h' +# +INC_DIR += $(shell echo "int main() {return 0;}" |\ + LANG=C $(CXX) -x c++ -v -E - 2>&1 |\ + sed '/^\#include <\.\.\.> search starts here:/,/^End of search list/!d' |\ + grep "include-fixed") + +# +# Add search paths for normal libraries +# +CXX_LINK_OPT += $(addprefix -L,$(HOST_LIB_SEARCH_DIRS)) + +# +# Add search paths for shared-library lookup +# +# We add all locations of shared libraries present in the ld.cache to our +# library search path. +# +HOST_SO_SEARCH_DIRS := $(sort $(dir $(shell ldconfig -p | sed "s/^.* \//\//" | grep "^\/"))) +LINK_ARG_PREFIX := -Wl, +CXX_LINK_OPT += $(addprefix $(LINK_ARG_PREFIX)-rpath-link $(LINK_ARG_PREFIX),$(HOST_SO_SEARCH_DIRS)) + +# +# The '__libc_csu_init' function is normally provided by the C library. We +# override the libc's version in our 'lx_hybrid' library to have a hook for +# Genode-specific initializations. Unfortunately, this way, we get two symbols +# with the same name. So we have to tell the linker to be forgiving. The order +# of the libraries at the linker command line determines which symbol is used. +# Therefore it is important to have 'lx_hybrid.lib.so' listed before '-lc', +# which is always the case when supplying '-lc' via 'EXT_OBJECTS' (not +# 'CXX_LINK_OPT'). +# +CXX_LINK_OPT += -Wl,--allow-multiple-definition + +# +# Make exceptions work +# +CXX_LINK_OPT += -Wl,--eh-frame-hdr + +# +# Add all libraries and their dependencies specified at the 'LX_LIBS' +# variable to the linker command line +# +ifneq ($(LX_LIBS),) +EXT_OBJECTS = $(shell pkg-config --static --libs $(LX_LIBS)) +endif + +# +# Use the host's startup codes, linker script, and dynamic linker +# +EXT_OBJECTS += $(shell cc -print-file-name=crt1.o) +EXT_OBJECTS += $(shell cc -print-file-name=crti.o) +EXT_OBJECTS += $(shell cc -print-file-name=crtbegin.o) +EXT_OBJECTS += $(shell cc -print-file-name=crtend.o) +EXT_OBJECTS += $(shell cc -print-file-name=crtn.o) +EXT_OBJECTS += -lgcc -lgcc_s -lsupc++ -lc + +# +# Some header files installed on GNU/Linux test for the GNU compiler. For +# example, 'stdio.h' might complain with the following error otherwise: +# +# /usr/include/stdio.h:432:27: error: expected initializer before ‘throw’ +# /usr/include/stdio.h:488:6: error: expected initializer before ‘throw’ +# +# By manually defining '_GNU_SOURCE', the header files are processed as +# expected. +# +CC_OPT += -D_GNU_SOURCE + +USE_HOST_LD_SCRIPT = yes + +ifeq (x86_64,$(findstring x86_64,$(SPECS))) +CXX_LINK_OPT += -Wl,--dynamic-linker=/lib64/ld-linux-x86-64.so.2 +else +CXX_LINK_OPT += -Wl,--dynamic-linker=/lib/ld-linux.so.2 +endif + +# because we use the host compiler's libgcc, omit the Genode toolchain's version +LD_LIBGCC = diff --git a/base-linux/lib/import/import-syscall.mk b/base-linux/lib/import/import-syscall.mk new file mode 100644 index 000000000..6e66dc920 --- /dev/null +++ b/base-linux/lib/import/import-syscall.mk @@ -0,0 +1,6 @@ +INC_DIR += $(dir $(call select_from_repositories,src/platform/linux_syscalls.h)) +INC_DIR += /usr/include + +# needed for Ubuntu 11.04 +INC_DIR += /usr/include/i386-linux-gnu +INC_DIR += /usr/include/x86_64-linux-gnu diff --git a/base-linux/lib/mk/core_printf.mk b/base-linux/lib/mk/core_printf.mk new file mode 100644 index 000000000..32ba72675 --- /dev/null +++ b/base-linux/lib/mk/core_printf.mk @@ -0,0 +1,5 @@ +SRC_CC = core_printf.cc +LIBS = cxx console syscall +INC_DIR += $(REP_DIR)/src/base/console + +vpath core_printf.cc $(BASE_DIR)/src/base/console diff --git a/base-linux/lib/mk/env.mk b/base-linux/lib/mk/env.mk new file mode 100644 index 000000000..d07cccbae --- /dev/null +++ b/base-linux/lib/mk/env.mk @@ -0,0 +1,6 @@ +SRC_CC = env.cc rm_session_mmap.cc platform_env.cc debug.cc context_area.cc +LIBS = ipc heap log_console lock syscall + +vpath env.cc $(BASE_DIR)/src/base/env +vpath context_area.cc $(BASE_DIR)/src/base/env +vpath %.cc $(REP_DIR)/src/base/env diff --git a/base-linux/lib/mk/ipc.mk b/base-linux/lib/mk/ipc.mk new file mode 100644 index 000000000..f8b81bade --- /dev/null +++ b/base-linux/lib/mk/ipc.mk @@ -0,0 +1,5 @@ +REQUIRES = linux +SRC_CC = ipc.cc +LIBS = syscall rpath + +vpath ipc.cc $(REP_DIR)/src/base/ipc diff --git a/base-linux/lib/mk/lock.mk b/base-linux/lib/mk/lock.mk new file mode 100644 index 000000000..51b073080 --- /dev/null +++ b/base-linux/lib/mk/lock.mk @@ -0,0 +1,5 @@ +SRC_CC = lock.cc +LIBS = syscall +INC_DIR += $(REP_DIR)/src/base/lock + +vpath lock.cc $(BASE_DIR)/src/base/lock diff --git a/base-linux/lib/mk/lx_hybrid.mk b/base-linux/lib/mk/lx_hybrid.mk new file mode 100644 index 000000000..859754041 --- /dev/null +++ b/base-linux/lib/mk/lx_hybrid.mk @@ -0,0 +1,7 @@ +SRC_CC = lx_hybrid.cc new_delete.cc +LIBS += syscall env + +vpath new_delete.cc $(BASE_DIR)/src/base/cxx +vpath lx_hybrid.cc $(REP_DIR)/src/platform + +CUSTOM_CXX = g++ diff --git a/base-linux/lib/mk/process.mk b/base-linux/lib/mk/process.mk new file mode 100644 index 000000000..1e3d7795a --- /dev/null +++ b/base-linux/lib/mk/process.mk @@ -0,0 +1,13 @@ +SRC_CC = process.cc +LIBS = syscall + +# +# The Linux version of the process library does not use Genode's ELF loader for +# loading executables but the 'execve' system call. However, for supporting +# dynamically linked executables, we have to take the decision of whether to load +# the dynamic linker or a static executable based on information provided by +# the ELF program header. We use the ELF library to obtain this information. +# +LIBS += elf + +vpath process.cc $(REP_DIR)/src/base/process diff --git a/base-linux/lib/mk/rpath.mk b/base-linux/lib/mk/rpath.mk new file mode 100644 index 000000000..fc42a899a --- /dev/null +++ b/base-linux/lib/mk/rpath.mk @@ -0,0 +1,6 @@ +REQUIRES = linux +SRC_CC = linux_rpath.cc +LIBS = syscall + +vpath linux_rpath.cc $(REP_DIR)/src/platform + diff --git a/base-linux/lib/mk/thread.mk b/base-linux/lib/mk/thread.mk new file mode 100644 index 000000000..db22b1f6d --- /dev/null +++ b/base-linux/lib/mk/thread.mk @@ -0,0 +1,6 @@ +REQUIRES = linux +SRC_CC = thread.cc thread_linux.cc +LIBS = syscall + +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath thread_linux.cc $(REP_DIR)/src/base/thread diff --git a/base-linux/lib/mk/x86_32/startup.mk b/base-linux/lib/mk/x86_32/startup.mk new file mode 100644 index 000000000..765f02b6d --- /dev/null +++ b/base-linux/lib/mk/x86_32/startup.mk @@ -0,0 +1,8 @@ +REQUIRES = linux x86 +LIBS = cxx lock syscall +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(BASE_DIR)/src/platform + +vpath crt0.s $(REP_DIR)/src/platform/x86_32 +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-linux/lib/mk/x86_32/syscall.mk b/base-linux/lib/mk/x86_32/syscall.mk new file mode 100644 index 000000000..28371b0da --- /dev/null +++ b/base-linux/lib/mk/x86_32/syscall.mk @@ -0,0 +1,5 @@ +REQUIRES = linux x86 +SRC_S += lx_clone.S lx_syscall.S + +vpath lx_clone.S $(REP_DIR)/../base-linux/src/platform/x86_32 +vpath lx_syscall.S $(REP_DIR)/../base-linux/src/platform/x86_32 diff --git a/base-linux/lib/mk/x86_64/startup.mk b/base-linux/lib/mk/x86_64/startup.mk new file mode 100644 index 000000000..248c5902a --- /dev/null +++ b/base-linux/lib/mk/x86_64/startup.mk @@ -0,0 +1,8 @@ +REQUIRES = linux x86 +LIBS = cxx lock syscall +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(BASE_DIR)/src/platform + +vpath crt0.s $(REP_DIR)/src/platform/x86_64 +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-linux/lib/mk/x86_64/syscall.mk b/base-linux/lib/mk/x86_64/syscall.mk new file mode 100644 index 000000000..f2d614497 --- /dev/null +++ b/base-linux/lib/mk/x86_64/syscall.mk @@ -0,0 +1,7 @@ +REQUIRES = linux x86 +SRC_S += lx_clone.S lx_restore_rt.S lx_syscall.S + +vpath lx_restore_rt.S $(REP_DIR)/../base-linux/src/platform/x86_64 +vpath lx_clone.S $(REP_DIR)/../base-linux/src/platform/x86_64 +vpath lx_syscall.S $(REP_DIR)/../base-linux/src/platform/x86_64 + diff --git a/base-linux/mk/spec-linux.mk b/base-linux/mk/spec-linux.mk new file mode 100644 index 000000000..a09531af7 --- /dev/null +++ b/base-linux/mk/spec-linux.mk @@ -0,0 +1,18 @@ +# +# Specifics for the Linux-specific Genode components +# + +# +# Startup code to be used when building a program and linker script that is +# specific for Linux. We also reserve the thread-context area via a segment in +# the program under Linux to prevent clashes with vdso. +# +ifneq ($(USE_HOST_LD_SCRIPT),yes) +PRG_LIBS += startup +LD_TEXT_ADDR ?= 0x01000000 +LD_SCRIPT_STATIC = $(call select_from_repositories,src/platform/genode.ld) \ + $(call select_from_repositories,src/platform/context_area.nostdlib.ld) +else +LD_SCRIPT_STATIC = $(LD_SCRIPT_DEFAULT) \ + $(call select_from_repositories,src/platform/context_area.stdlib.ld) +endif diff --git a/base-linux/mk/spec-linux_x86_32.mk b/base-linux/mk/spec-linux_x86_32.mk new file mode 100644 index 000000000..6bcd64715 --- /dev/null +++ b/base-linux/mk/spec-linux_x86_32.mk @@ -0,0 +1,21 @@ +# +# Specifics for Linux on 32-bit x86 +# +SPECS += linux x86_32 + +REP_INC_DIR += src/platform/x86_32 + +# +# We need to manually add the default linker script on the command line in case +# of standard library use. Otherwise, we were not able to extend it by the +# context area section. +# +ifeq ($(USE_HOST_LD_SCRIPT),yes) +LD_SCRIPT_DEFAULT = ldscripts/elf_i386.xc +endif + +# +# Include less-specific configuration +# +include $(call select_from_repositories,mk/spec-x86_32.mk) +include $(call select_from_repositories,mk/spec-linux.mk) diff --git a/base-linux/mk/spec-linux_x86_64.mk b/base-linux/mk/spec-linux_x86_64.mk new file mode 100644 index 000000000..9e14b2fec --- /dev/null +++ b/base-linux/mk/spec-linux_x86_64.mk @@ -0,0 +1,22 @@ +# +# Specifics for Linux on 64-bit x86 +# +SPECS += linux x86_64 + +REP_INC_DIR += src/platform/x86_64 + +# +# We need to manually add the default linker script on the command line in case +# of standard library use. Otherwise, we were not able to extend it by the +# context area section. +# +ifeq ($(USE_HOST_LD_SCRIPT),yes) +LD_SCRIPT_DEFAULT = ldscripts/elf_x86_64.xc +endif + +# +# Include less-specific configuration +# +include $(call select_from_repositories,mk/spec-x86_64.mk) +include $(call select_from_repositories,mk/spec-linux.mk) + diff --git a/base-linux/run/env b/base-linux/run/env new file mode 100644 index 000000000..ab4ca95af --- /dev/null +++ b/base-linux/run/env @@ -0,0 +1,43 @@ +# +# \brief Environment for executing Genode on Linux +# \author Norman Feske +# \date 2010-08-16 +# +# For the documentation of the implemented API functions, +# please refer to the comments in 'tool/run'. +# + +proc create_boot_directory { } { + exec rm -rf [run_dir] + exec mkdir -p [run_dir] +} + + +proc install_config {config} { + set fh [open "[run_dir]/config" "WRONLY CREAT TRUNC"] + puts $fh $config + close $fh +} + + +proc build_boot_image {binaries} { + foreach binary $binaries { + exec ln -sf ../../../bin/$binary [run_dir] } +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + global output + set timeout $timeout_value + set orig_pwd [pwd] + cd [run_dir] + set pid [spawn ./core] + if {$wait_for_re == "forever"} { interact $pid } + expect { + -re $wait_for_re { } + timeout { puts stderr "Error: Test execution timed out"; exit -2 } + } + cd $orig_pwd + set output $expect_out(buffer) +} + diff --git a/base-linux/run/lx_hybrid_ctors.run b/base-linux/run/lx_hybrid_ctors.run new file mode 100644 index 000000000..7b52ad4d4 --- /dev/null +++ b/base-linux/run/lx_hybrid_ctors.run @@ -0,0 +1,77 @@ +# +# \brief Test if global static constructors in hybrid applications and +# host shared libs get called +# \author Christian Prochaska +# \date 2011-11-24 +# + +# +# Build +# + +build { + core init + test/lx_hybrid_ctors +} + +create_boot_directory + +# +# Generate config +# + +install_config { + + + + + + + + + + + + +} + +# +# Boot modules +# + +exec cp test/lx_hybrid_ctors/libtestlib.so bin/ + +# generic modules +set boot_modules { + core init + test-lx_hybrid_ctors + libtestlib.so +} + +build_boot_image $boot_modules + +# +# Execute test case +# + +# qemu config +append qemu_args "-nographic -m 64 " + +run_genode_until "child exited with exit value 0.*\n" 10 + +# +# Compare output +# + +grep_output {\[init -\> test-lx_hybrid_ctors\]} + +compare_output_to { + [init -> test-lx_hybrid_ctors] Global static constructor of host library called. + [init -> test-lx_hybrid_ctors] Global static constructor of Genode application called + [init -> test-lx_hybrid_ctors] --- lx_hybrid global static constructor test --- + [init -> test-lx_hybrid_ctors] --- returning from main --- +} + +exec rm bin/libtestlib.so + +# vi: set ft=tcl : diff --git a/base-linux/run/lx_hybrid_exception.run b/base-linux/run/lx_hybrid_exception.run new file mode 100644 index 000000000..32befb51c --- /dev/null +++ b/base-linux/run/lx_hybrid_exception.run @@ -0,0 +1,60 @@ +# +# \brief Test if the exception mechanism works in hybrid applications +# \author Christian Prochaska +# \date 2011-11-22 +# + +# +# Build +# + +build { + core init + test/lx_hybrid_exception +} + +create_boot_directory + +# +# Generate config +# + +install_config { + + + + + + + + + + + + +} + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init + test-lx_hybrid_exception +} + +build_boot_image $boot_modules + +# +# Execute test case +# + +# qemu config +append qemu_args "-nographic -m 64 " + +run_genode_until "child exited with exit value 0.*\n" 10 + +puts "Test succeeded" + +# vi: set ft=tcl : diff --git a/base-linux/src/base/console/core_console.h b/base-linux/src/base/console/core_console.h new file mode 100644 index 000000000..2fecced9a --- /dev/null +++ b/base-linux/src/base/console/core_console.h @@ -0,0 +1,31 @@ +/* + * \brief Printf backend using Linux stdout + * \author Norman Feske + * \date 2006-04-08 + * + * This console back-end should only be used by core. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* Linux syscall bindings */ +#include + +namespace Genode { + + class Core_console : public Console + { + protected: + + void _out_char(char c) { lx_write(1, &c, sizeof(c)); } + }; +} + diff --git a/base-linux/src/base/env/debug.cc b/base-linux/src/base/env/debug.cc new file mode 100644 index 000000000..d62b76976 --- /dev/null +++ b/base-linux/src/base/env/debug.cc @@ -0,0 +1,57 @@ +/* + * \brief Linux-specific debug utilities + * \author Norman Feske + * \date 2009-05-16 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* + * With the enabled 'DEBUG' flag, status information can be printed directly + * via a Linux system call by using the 'raw_write_str' function. This output + * bypasses the Genode 'LOG' mechanism, which is useful for debugging low-level + * code such as a libC back-end. + */ +#define DEBUG 1 + + +#if DEBUG +#include +#endif /* DEBUG */ + + +/** + * Write function targeting directly the Linux system call layer and bypassing + * any Genode code. + */ +extern "C" int raw_write_str(const char *str) +{ +#if DEBUG + unsigned len = 0; + for (; str[len] != 0; len++); + lx_syscall(SYS_write, (int)1, str, len); + return len; +#endif /* DEBUG */ +} + + +/** + * Debug function waiting until the user presses return + * + * This function is there to delay the execution of a back-end function such + * that we have time to attack the GNU debugger to the running process. Once + * attached, we can continue execution and use 'gdb' for debugging. In the + * normal mode of operation, this function is never used. + */ +extern "C" void wait_for_continue(void) +{ +#if DEBUG + char buf[16]; + lx_syscall(SYS_read, (int)0, buf, sizeof(buf)); +#endif /* DEBUG */ +} diff --git a/base-linux/src/base/env/platform_env.cc b/base-linux/src/base/env/platform_env.cc new file mode 100644 index 000000000..454e2cc92 --- /dev/null +++ b/base-linux/src/base/env/platform_env.cc @@ -0,0 +1,97 @@ +/* + * \brief Support for the Linux-specific environment + * \author Norman Feske + * \date 2008-12-12 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + +/******************************** + ** Platform_env::Local_parent ** + ********************************/ + +Session_capability +Platform_env::Local_parent::session(Service_name const &service_name, + Session_args const &args) +{ + if (strcmp(service_name.string(), + Rm_session::service_name()) == 0) + { + size_t size = + Arg_string::find_arg(args.string(),"size") + .ulong_value(~0); + + if (size == 0) + return Parent_client::session(service_name, args); + + Rm_session_mmap *rm = new (env()->heap()) + Rm_session_mmap(true, size); + + return Local_interface::capability(rm); + } + + return Parent_client::session(service_name, args); +} + + +void Platform_env::Local_parent::close(Session_capability session) +{ + /* + * Handle non-local capabilities + */ + if (session.valid()) { + Parent_client::close(session); + return; + } + + /* + * Detect capability to local RM session + */ + try { + Capability rm = + static_cap_cast(session); + + destroy(env()->heap(), Local_interface::deref(rm)); + + } catch (Local_interface::Non_local_capability) { } +} + + +Platform_env::Local_parent::Local_parent(Parent_capability parent_cap) +: Parent_client(parent_cap) { } + + +/****************** + ** Platform_env ** + ******************/ + +/** + * List of Unix environment variables, initialized by the startup code + */ +extern char **lx_environ; + + +/** + * Read environment variable as long value + */ +unsigned long Platform_env::_get_env_ulong(const char *key) +{ + for (char **curr = lx_environ; curr && *curr; curr++) { + + Arg arg = Arg_string::find_arg(*curr, key); + if (arg.valid()) + return arg.ulong_value(0); + } + + return 0; +} diff --git a/base-linux/src/base/env/rm_session_mmap.cc b/base-linux/src/base/env/rm_session_mmap.cc new file mode 100644 index 000000000..4e1c2439d --- /dev/null +++ b/base-linux/src/base/env/rm_session_mmap.cc @@ -0,0 +1,283 @@ +/* + * \brief Implementation of Linux-specific local region manager + * \author Norman Feske + * \date 2008-10-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include + +using namespace Genode; + + +static size_t dataspace_size(Dataspace_capability ds) +{ + if (ds.valid()) + return Dataspace_client(ds).size(); + + return Local_interface::deref(ds)->size(); +} + + +static bool is_sub_rm_session(Dataspace_capability ds) +{ + if (ds.valid()) + return false; + + try { + Local_interface::deref(ds); } + catch (Local_interface::Non_local_capability) { + return false; } + + return true; +} + + +static void *map_local(Dataspace_capability ds, size_t size, addr_t offset, + bool use_local_addr, addr_t local_addr) +{ + Linux_dataspace::Filename fname = Linux_dataspace_client(ds).fname(); + fname.buf[sizeof(fname.buf) - 1] = 0; + + bool writable = Dataspace_client(ds).writable(); + int fd = lx_open(fname.buf, (writable ? O_RDWR : O_RDONLY) | LX_O_CLOEXEC); + if (fd < 0) { + PERR("map_local: Could not open file \"%s\"", fname.buf); + throw Rm_session::Invalid_dataspace(); + } + + int flags = MAP_SHARED | (use_local_addr ? MAP_FIXED : 0); + int prot = PROT_READ | PROT_EXEC | (writable ? PROT_WRITE : 0); + void *addr = lx_mmap(use_local_addr ? (void*)local_addr : 0, size, + prot, flags, fd, offset); + + lx_close(fd); + + if (((long)addr < 0) && ((long)addr > -4095)) { + PERR("map_local: return value of mmap is %ld", (long)addr); + throw Rm_session::Region_conflict(); + } + + return addr; +} + + +void Platform_env::Rm_session_mmap::_add_to_rmap(Region const ®ion) +{ + if (_rmap.add_region(region) < 0) { + PERR("_add_to_rmap: could not add region to sub RM session"); + throw Region_conflict(); + } +} + + +Rm_session::Local_addr +Platform_env::Rm_session_mmap::attach(Dataspace_capability ds, + size_t size, off_t offset, + bool use_local_addr, + Rm_session::Local_addr local_addr) +{ + Lock::Guard lock_guard(_lock); + + /* only support attach_at for sub RM sessions */ + if (_sub_rm && !use_local_addr) { + PERR("Rm_session_mmap::attach: attaching w/o local addr not supported\n"); + throw Out_of_metadata(); + } + + if (offset < 0) { + PERR("Rm_session_mmap::attach: negative offset not supported\n"); + throw Region_conflict(); + } + + size_t const remaining_ds_size = dataspace_size(ds) > (addr_t)offset + ? dataspace_size(ds) - (addr_t)offset : 0; + + /* determine size of virtual address region */ + size_t const region_size = size ? min(remaining_ds_size, size) + : remaining_ds_size; + if (region_size == 0) + throw Region_conflict(); + + /* + * We have to distinguish the following cases + * + * 1 we are a root RM session and ds is a plain dataspace + * 2 we are a root RM session and ds is a sub RM session + * 2.1 ds is already attached (base != 0) + * 2.2 ds is not yet attached + * 3 we are a sub RM session and ds is a plain dataspace + * 3.1 we are attached to a root RM session + * 3.2 we are not yet attached + * 4 we are a sub RM session and ds is a sub RM session (not supported) + */ + + if (_sub_rm) { + + /* + * Case 4 + */ + if (is_sub_rm_session(ds)) { + PERR("Rm_session_mmap::attach: nesting sub RM sessions is not supported"); + throw Invalid_dataspace(); + } + + /* + * Check for the dataspace to not exceed the boundaries of the + * sub RM session + */ + if (region_size + (addr_t)local_addr > _size) { + PERR("Rm_session_mmap::attach: dataspace does not fit in sub RM session"); + throw Region_conflict(); + } + + _add_to_rmap(Region(local_addr, offset, ds, region_size)); + + /* + * Case 3.1 + * + * This RM session is a sub RM session. If the sub RM session is + * attached (_base > 0), add its attachement offset to the local base + * and map it. + */ + if (_is_attached()) + map_local(ds, region_size, offset, true, _base + (addr_t)local_addr); + + return (void *)local_addr; + + } else { + + if (is_sub_rm_session(ds)) { + + Dataspace *ds_if = Local_interface::deref(ds); + + Rm_session_mmap *rm = dynamic_cast(ds_if); + + if (!rm) + throw Invalid_dataspace(); + + /* + * Case 2.1 + * + * Detect if sub RM session is already attached + */ + if (rm->_base) { + PERR("Rm_session_mmap::attach: mapping a sub RM session twice is not supported"); + throw Out_of_metadata(); + } + + _add_to_rmap(Region(local_addr, offset, ds, region_size)); + + /* + * Allocate local address range that can hold the entire sub RM + * session. + */ + rm->_base = lx_vm_reserve(use_local_addr ? (addr_t)local_addr : 0, + region_size); + + /* + * Cases 2.2, 3.2 + * + * The sub rm session was not attached until now but it may have + * been populated with dataspaces. Go through all regions an map + * each of them. + */ + for (int i = 0; i < Region_map::MAX_REGIONS; i++) { + Region region = rm->_rmap.region(i); + if (!region.used()) + continue; + + map_local(region.dataspace(), region.size(), region.offset(), + true, rm->_base + region.start() + region.offset()); + } + + return rm->_base; + + } else { + + /* + * Case 1 + * + * Boring, a plain dataspace is attached to a root RM session. + */ + void *addr = map_local(ds, region_size, offset, use_local_addr, local_addr); + + _add_to_rmap(Region((addr_t)addr, offset, ds, region_size)); + + return addr; + } + } +} + + +void Platform_env::Rm_session_mmap::detach(Rm_session::Local_addr local_addr) +{ + Lock::Guard lock_guard(_lock); + + /* + * Cases + * + * 1 we are root RM + * 2 we are sub RM (region must be normal dataspace) + * 2.1 we are not attached + * 2.2 we are attached to a root RM + */ + + Region region = _rmap.lookup(local_addr); + if (!region.used()) + return; + + /* + * Remove meta data from region map + */ + _rmap.remove_region(local_addr); + + if (_sub_rm) { + + /* + * Case 2.1, 2.2 + * + * By removing a region from an attached sub RM session we mark the + * corresponding local address range as reserved. A plain 'munmap' + * would mark this range as free to use for the root RM session, which + * we need to prevent. + * + * If we are not attached, no local address-space manipulation is + * needed. + */ + if (_is_attached()) + lx_vm_reserve((addr_t)local_addr + _base, region.size()); + + } else { + + /* + * Case 1 + * + * We need no distiction between detaching normal dataspaces and + * sub RM session. In both cases, we simply mark the local address + * range as free. + */ + lx_munmap(local_addr, region.size()); + } + + /* + * If the detached dataspace is sub RM session, mark it as detached + */ + if (is_sub_rm_session(region.dataspace())) { + + Dataspace *ds_if = Local_interface::deref(region.dataspace()); + Rm_session_mmap *rm = dynamic_cast(ds_if); + if (rm) + rm->_base = 0; + } + +} diff --git a/base-linux/src/base/ipc/ipc.cc b/base-linux/src/base/ipc/ipc.cc new file mode 100644 index 000000000..eb8c5aa3d --- /dev/null +++ b/base-linux/src/base/ipc/ipc.cc @@ -0,0 +1,358 @@ +/* + * \brief Socket-based IPC implementation for Linux + * \author Norman Feske + * \author Christian Helmuth + * \date 2011-10-11 + * + * We create two sockets under lx_rpath() for each thread: client and server + * role. The naming is 'ep--'. The socket descriptors are + * cached in Thread_base::_tid. + * + * Currently two socket files are needed, as the client does not send the reply + * socket access-rights in a combined message with the payload. In the future, + * only server sockets must be bound in lx_rpath(). + * + * The current request message layout is: + * + * long server_local_name; + * long client_thread_id; + * int opcode; + * ...payload... + * + * Response messages look like this: + * + * long scratch_word; + * int exc_code; + * ...payload... + * + * All fields are naturally aligned, i.e., aligend on 4 or 8 byte boundaries on + * 32-bit resp. 64-bit systems. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* Linux includes */ +#include +#include +#include + +#include +#include + + +extern "C" void wait_for_continue(void); +extern "C" int raw_write_str(const char *str); + +#define PRAW(fmt, ...) \ + do { \ + char str[128]; \ + snprintf(str, sizeof(str), \ + ESC_ERR fmt ESC_END "\n", ##__VA_ARGS__); \ + raw_write_str(str); \ + } while (0) + + +using namespace Genode; + + +/** + * Utility: Create socket address for thread ID and role (client/server) + */ +static void lx_create_sockaddr(sockaddr_un *addr, long thread_id, char const *role) +{ + addr->sun_family = AF_UNIX; + Genode::snprintf(addr->sun_path, sizeof(addr->sun_path), "%s/ep-%ld-%s", + lx_rpath(), thread_id, role); +} + + +/** + * Utility: Create a socket descriptor and file for given thread and role + */ +static int create_socket(long thread_id, char const *role) +{ + int sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sd < 0) return -1; + + sockaddr_un addr; + lx_create_sockaddr(&addr, thread_id, role); + + /* make sure bind succeeds */ + lx_unlink(addr.sun_path); + + if (lx_bind(sd, (sockaddr *)&addr, sizeof(addr)) < 0) + return -2; + + return sd; +} + + +/** + * Utility: Unlink socket file and close descriptor + * + * XXX Currently, socket destruction is missing. The client socket could be + * used from multiple Ipc_client objects. A safe destruction would need + * reference counting. + */ +//static void destroy_socket(int sd, long thread_id, char const *role) +//{ +// sockaddr_un addr; +// lx_create_sockaddr(&addr, thread_id, role); +// +// lx_unlink(addr.sun_path); +// lx_close(sd); +//} + + +/** + * Get client-socket descriptor for main thread + */ +static int main_client_socket() +{ + static int sd = create_socket(lx_gettid(), "client"); + + return sd; +} + + +/** + * Utility: Get server socket for given thread + */ +static int server_socket(Thread_base *thread) +{ + /* + * Main thread uses Ipc_server for sleep_forever() only. + */ + if (!thread) + return lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + + if (thread->tid().server == -1) + thread->tid().server = create_socket(thread->tid().tid, "server"); + return thread->tid().server; +} + + +/** + * Utility: Get client socket for given thread + */ +static int client_socket(Thread_base *thread) +{ + if (!thread) return main_client_socket(); + + if (thread->tid().client == -1) + thread->tid().client = create_socket(thread->tid().tid, "client"); + return thread->tid().client; +} + + +/** + * Utility: Send message to thread via given socket descriptor + */ +static void send_to(int sd, long thread_id, char const *target_role, + void *msg, Genode::size_t msg_len) +{ + sockaddr_un addr; + lx_create_sockaddr(&addr, thread_id, target_role); + + int res = lx_sendto(sd, msg, msg_len, 0, (sockaddr *)&addr, sizeof(addr)); + if (res < 0) { + PRAW("Send error: %d with %s in %d", res, addr.sun_path, lx_gettid()); + wait_for_continue(); + throw Ipc_error(); + } +} + + +/** + * Utility: Receive message via given socket descriptor + */ +static void recv_from(int sd, void *buf, Genode::size_t buf_len) +{ + socklen_t fromlen; + int res = lx_recvfrom(sd, buf, buf_len, 0, 0, &fromlen); + if (res < 0) { + if ((-res) == EINTR) + throw Blocking_canceled(); + else { + PRAW("Recv error: %d in %d", res, lx_gettid()); + wait_for_continue(); + throw Ipc_error(); + } + } +} + + +/***************** + ** Ipc_ostream ** + *****************/ + +/* + * XXX This class will be removed soon. + */ + +void Ipc_ostream::_prepare_next_send() +{ + PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this); +} + + +void Ipc_ostream::_send() +{ + PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this); +} + + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg): + Ipc_marshaller(snd_msg->buf, snd_msg->size()), _snd_msg(snd_msg), _dst(dst) +{ } + + +/***************** + ** Ipc_istream ** + *****************/ + +/* + * XXX This class will be removed soon. + */ + +void Ipc_istream::_prepare_next_receive() +{ + PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this); +} + + +void Ipc_istream::_wait() +{ + PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this); +} + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) +: Ipc_unmarshaller(rcv_msg->buf, rcv_msg->size()), + Native_capability(lx_gettid(), 0), + _rcv_msg(rcv_msg), _rcv_cs(-1) +{ } + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_prepare_next_call() +{ + /* prepare next request in buffer */ + long local_name = _dst.local_name(); + long tid = Native_capability::tid(); + + _write_offset = 0; + _write_to_buf(local_name); + _write_to_buf(tid); + + /* prepare response buffer */ + _read_offset = sizeof(long); +} + + +void Ipc_client::_call() +{ + if (_dst.valid()) { + send_to(_rcv_cs, _dst.tid(), "server", + _snd_msg->buf, _write_offset); + + recv_from(_rcv_cs, _rcv_msg->buf, _rcv_msg->size()); + } + _prepare_next_call(); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, + Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) +{ + _rcv_cs = client_socket(Thread_base::myself()); + + _prepare_next_call(); +} + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_prepare_next_reply_wait() +{ + /* skip server-local name */ + _read_offset = sizeof(long); + + /* read client thread id from request buffer */ + long tid; + if (_reply_needed) { + _read_from_buf(tid); + _dst = Native_capability(tid, 0); /* only _tid member is used */ + } + + /* prepare next reply */ + _write_offset = 0; + long local_name = _dst.local_name(); + _write_to_buf(local_name); /* XXX unused, needed by de/marshaller */ + + /* leave space for exc code at the beginning of the msgbuf */ + _write_offset += align_natural(sizeof(int)); +} + + +void Ipc_server::_wait() +{ + /* wait for new server request */ + try { + recv_from(_rcv_cs, _rcv_msg->buf, _rcv_msg->size()); + } catch (Blocking_canceled) { } + + /* now we have a request to reply, determine reply destination */ + _reply_needed = true; + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() +{ + try { + send_to(_rcv_cs, _dst.tid(), "client", _snd_msg->buf, _write_offset); + } catch (Ipc_error) { } + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply_wait() +{ + /* when first called, there was no request yet */ + if (_reply_needed) + send_to(_rcv_cs, _dst.tid(), "client", _snd_msg->buf, _write_offset); + + _wait(); +} + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), + Ipc_ostream(Native_capability(), snd_msg), _reply_needed(false) +{ + _rcv_cs = server_socket(Thread_base::myself()); + + _prepare_next_reply_wait(); +} diff --git a/base-linux/src/base/lock/lock_helper.h b/base-linux/src/base/lock/lock_helper.h new file mode 100644 index 000000000..b6cca57f9 --- /dev/null +++ b/base-linux/src/base/lock/lock_helper.h @@ -0,0 +1,80 @@ +/* + * \brief Linux-specific helper functions for the Lock implementation + * \author Norman Feske + * \date 2009-07-20 + * + * This file serves as adapter between the generic lock implementation + * in 'lock.cc' and the underlying kernel. + * + * For documentation about the interface, please revisit the 'base-pistachio' + * implementation. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* Linux includes */ +#include + + +/** + * Resolve 'Thread_base::myself' when not linking the thread library + * + * This weak symbol is primarily used by test cases. Most other Genode programs + * use the thread library. If the thread library is not used, 'myself' can only + * be called by the main thread, for which 'myself' is defined as zero. + */ +Genode::Thread_base * __attribute__((weak)) Genode::Thread_base::myself() { return 0; } + + +static inline void thread_yield() +{ + struct timespec ts = { 0, 1000 }; + lx_nanosleep(&ts, 0); +} + + +static bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + lx_tgkill(tid.pid, tid.tid, LX_SIGUSR1); + return true; +} + + +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + return Genode::Native_thread_id(lx_gettid(), lx_getpid()); +} + + +static inline Genode::Native_thread_id thread_invalid_id() +{ + return Genode::Native_thread_id(); +} + + +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return (tid.pid != 0); +} + + +static inline void thread_switch_to(Genode::Native_thread_id tid) +{ + thread_yield(); +} + + +static inline void thread_stop_myself() +{ + struct timespec ts = { 1000, 0 }; + while (lx_nanosleep(&ts, 0) == 0); +} diff --git a/base-linux/src/base/process/process.cc b/base-linux/src/base/process/process.cc new file mode 100644 index 000000000..768c48f8c --- /dev/null +++ b/base-linux/src/base/process/process.cc @@ -0,0 +1,203 @@ +/* + * \brief Implementation of process creation for Linux + * \author Norman Feske + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* Framework-internal includes */ +#include + + +using namespace Genode; + +Dataspace_capability Process::_dynamic_linker_cap; + +/** + * Argument frame for passing 'execve' paremeters through 'clone' + */ +struct execve_args { + const char *filename; + char *const*argv; + char *const*envp; +}; + + +/** + * Startup code of the new child process + */ +static int _exec_child(struct execve_args *arg) +{ + return lx_execve(arg->filename, arg->argv, arg->envp); +} + + +/** + * List of Unix environment variables, initialized by the startup code + */ +extern char **lx_environ; + + +/** + * Read environment variable as string + * + * If no matching key exists, return an empty string. + */ +static const char *get_env(const char *key) +{ + Genode::size_t key_len = strlen(key); + for (char **curr = lx_environ; curr && *curr; curr++) + if ((Genode::strcmp(*curr, key, key_len) == 0) && (*curr)[key_len] == '=') + return (const char *)(*curr + key_len + 1); + + return ""; +} + +/** + * Check for dynamic ELF header + */ +static bool _check_dynamic_elf(Dataspace_capability elf_ds_cap) +{ + /* attach ELF locally */ + addr_t elf_addr; + + try { elf_addr = env()->rm_session()->attach(elf_ds_cap); } + catch (...) { return false; } + + /* + * If attach is called within core, it will return zero because + * Linux uses Core_rm_session. + */ + if (!elf_addr) return false; + + /* read program header and interpreter */ + Elf_binary elf((addr_t)elf_addr); + env()->rm_session()->detach((void *)elf_addr); + + return elf.is_dynamically_linked(); +} + + +const char *Process::_priv_pd_args(Parent_capability parent_cap, + Dataspace_capability elf_data_ds_cap, + const char *name, char *const argv[]) +{ + /* + * Serialize calls of this function because it uses the static 'envbuf' and + * 'stack' variables. + */ + static Lock _priv_pd_args_lock; + Lock::Guard _lock_guard(_priv_pd_args_lock); + + /* check for dynamic program header */ + if (_check_dynamic_elf(elf_data_ds_cap)) { + if (!_dynamic_linker_cap.valid()) { + PERR("Dynamically linked file found, but no dynamic linker binary present"); + return 0; + } + elf_data_ds_cap = _dynamic_linker_cap; + } + + /* pass parent capability as environment variable to the child */ + enum { ENV_STR_LEN = 256 }; + static char envbuf[5][ENV_STR_LEN]; + Genode::snprintf(envbuf[0], ENV_STR_LEN, "parent_tid=%ld", + parent_cap.tid()); + Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu", + parent_cap.local_name()); + Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s", + get_env("DISPLAY")); + Genode::snprintf(envbuf[3], ENV_STR_LEN, "HOME=%s", + get_env("HOME")); + Genode::snprintf(envbuf[4], ENV_STR_LEN, "LD_LIBRARY_PATH=%s", + get_env("LD_LIBRARY_PATH")); + + char *env[] = { &envbuf[0][0], &envbuf[1][0], &envbuf[2][0], + &envbuf[3][0], &envbuf[4][0], 0 }; + + /* determine name of binary to start */ + Linux_dataspace_client elf_data_ds(elf_data_ds_cap); + Linux_dataspace::Filename fname = elf_data_ds.fname(); + fname.buf[sizeof(fname.buf) - 1] = 0; + + /* prefix name of Linux program (helps killing some zombies) */ + char pname_buf[9 + Linux_dataspace::FNAME_LEN]; + snprintf(pname_buf, sizeof(pname_buf), "[Genode] %s", name); + + /* it may happen, that argv is null */ + char *argv_buf[2]; + if (!argv) { + argv_buf[0] = pname_buf; + argv_buf[1] = 0; + argv = argv_buf; + } else + ((char **)argv)[0] = pname_buf; + + /* + * We cannot create the new process via 'fork()' because all our used + * memory including stack memory is backed by dataspaces, which had been + * mapped with the 'MAP_SHARED' flag. Therefore, after being created, the + * new process starts using the stack with the same physical memory pages + * as used by parent process. This would ultimately lead to stack + * corruption. To prevent both processes from concurrently accessing the + * same stack, we pause the execution of the parent until the child calls + * 'execve'. From then on, the child has its private memory layout. The + * desired behaviour is normally provided by 'vfork' but we use the more + * modern 'clone' call for this purpose. + */ + enum { STACK_SIZE = 4096 }; + static char stack[STACK_SIZE]; /* initial stack used by the child until + calling 'execve' */ + + /* + * Argument frame as passed to 'clone'. Because, we can only pass a single + * pointer, all arguments are embedded within the 'execve_args' struct. + */ + struct execve_args arg = { + fname.buf, + argv, + env + }; + + pid_t pid = lx_create_process((int (*)(void *))_exec_child, + stack + STACK_SIZE - sizeof(umword_t), &arg); + + /* + * We create a pseudo pd session with the new pd's pid as argument + * to enable Core to kill the process when the pd session gets closed. + */ + snprintf(_priv_pd_argbuf, sizeof(_priv_pd_argbuf), "PID=%d", pid); + + return _priv_pd_argbuf; +} + + +Process::Process(Dataspace_capability elf_data_ds_cap, + Ram_session_capability ram_session_cap, + Cpu_session_capability cpu_session_cap, + Rm_session_capability rm_session_cap, + Parent_capability parent_cap, + const char *name, + char *const argv[]) +: + _pd(_priv_pd_args(parent_cap, elf_data_ds_cap, name, argv)), + _cpu_session_client(Cpu_session_capability()), + _rm_session_client(Rm_session_capability()) +{ } + + +Process::~Process() { } diff --git a/base-linux/src/base/thread/thread_linux.cc b/base-linux/src/base/thread/thread_linux.cc new file mode 100644 index 000000000..e6dec1152 --- /dev/null +++ b/base-linux/src/base/thread/thread_linux.cc @@ -0,0 +1,115 @@ +/* + * \brief Implementation of the Thread API via Linux threads + * \author Norman Feske + * \date 2006-06-13 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* Linux syscall bindings */ +#include + +using namespace Genode; + + +static void empty_signal_handler(int) { } + + +/** + * Signal handler for killing the thread + */ +static void thread_exit_signal_handler(int) { lx_exit(0); } + + +static void thread_start(void *) +{ + /* + * Set signal handler such that canceled system calls get not + * transparently retried after a signal gets received. + */ + lx_sigaction(LX_SIGUSR1, empty_signal_handler); + + Thread_base::myself()->entry(); + sleep_forever(); +} + + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() +{ + /* + * Kill thread until it is really really dead + * + * We use the 'tgkill' system call to kill the thread. This system call + * returns immediately and just flags the corresponding signal at the + * targeted thread context. However, the thread still lives until the + * signal flags are evaluated. When leaving this function, however, we want + * to be sure that the thread is no more executing any code such that we + * an safely free and unmap the thread's stack. So we call 'tgkill' in a + * loop until we get an error indicating that the thread does not exists + * anymore. + */ + for (;;) { + + /* destroy thread locally */ + int ret = lx_tgkill(_tid.pid, _tid.tid, LX_SIGCANCEL); + + if (ret < 0) break; + + /* if thread still exists, wait a bit and try to kill it again */ + struct timespec ts = { 0, 500 }; + lx_nanosleep(&ts, 0); + } + + /* inform core about the killed thread */ + env()->cpu_session()->kill_thread(_thread_cap); +} + + +void Thread_base::start() +{ + /* + * The first time we enter this code path, the 'start' function is + * called by the main thread as there cannot exist other threads + * without executing this function. When first called, we initialize + * the thread lib here. + */ + static bool threadlib_initialized = false; + if (!threadlib_initialized) { + lx_sigaction(LX_SIGCANCEL, thread_exit_signal_handler); + threadlib_initialized = true; + } + + /* align initial stack to 16 byte boundary */ + void *thread_sp = (void *)((addr_t)(_context->stack) & ~0xf); + _tid.tid = lx_create_thread(thread_start, thread_sp, this); + _tid.pid = lx_getpid(); + + /* + * Inform core about the new thread by calling create_thread and encoding + * the thread's PID in the thread-name argument. + */ + char name_and_pid[Cpu_session::THREAD_NAME_LEN + 2*16]; + snprintf(name_and_pid, sizeof(name_and_pid), "%s:0x%x:0x%x", + _context->name, _tid.tid, _tid.pid); + _thread_cap = env()->cpu_session()->create_thread(name_and_pid); +} + + +void Thread_base::cancel_blocking() +{ + env()->cpu_session()->cancel_blocking(_thread_cap); +} diff --git a/base-linux/src/core/context_area.cc b/base-linux/src/core/context_area.cc new file mode 100644 index 000000000..3c7a83d35 --- /dev/null +++ b/base-linux/src/core/context_area.cc @@ -0,0 +1,112 @@ +/* + * \brief Linux-specific support code for the thread API + * \author Norman Feske + * \date 2010-01-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* Linux includes */ +#include +#include + + +/** + * Region-manager session for allocating thread contexts + * + * This class corresponds to the managed dataspace that is normally + * used for organizing thread contexts with the thread context area. + * It "emulates" the sub address space by adjusting the local address + * argument to 'attach' with the offset of the thread context area. + */ +class Context_area_rm_session : public Genode::Rm_session +{ + public: + + /** + * Attach backing store to thread-context area + */ + Local_addr attach(Genode::Dataspace_capability ds_cap, + Genode::size_t size, Genode::off_t offset, + bool use_local_addr, Local_addr local_addr) + { + using namespace Genode; + + /* convert context-area-relative to absolute virtual address */ + addr_t addr = local_addr; + addr += Thread_base::CONTEXT_AREA_VIRTUAL_BASE; + + /* use anonymous mmap for allocating stack backing store */ + int flags = MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE; + int prot = PROT_READ | PROT_WRITE; + void *res = lx_mmap((void*)addr, size, prot, flags, -1, 0); + + if ((addr_t)res != addr) + throw Region_conflict(); + + return local_addr; + } + + void detach(Local_addr local_addr) { + PWRN("context area detach from 0x%p - not implemented", (void *)local_addr); } + + Genode::Pager_capability add_client(Genode::Thread_capability) { + return Genode::Pager_capability(); } + + void fault_handler(Genode::Signal_context_capability) { } + + State state() { return State(); } + + Genode::Dataspace_capability dataspace() { + return Genode::Dataspace_capability(); } +}; + + +class Context_area_ram_session : public Genode::Ram_session +{ + public: + + Genode::Ram_dataspace_capability alloc(Genode::size_t size) { + return Genode::Ram_dataspace_capability(); } + + void free(Genode::Ram_dataspace_capability) { } + + int ref_account(Genode::Ram_session_capability) { return 0; } + + int transfer_quota(Genode::Ram_session_capability, Genode::size_t) { return 0; } + + size_t quota() { return 0; } + + size_t used() { return 0; } +}; + + +/** + * Return single instance of the context-area RM and RAM session + */ +namespace Genode { + + Rm_session *env_context_area_rm_session() + { + static Context_area_rm_session inst; + return &inst; + } + + Ram_session *env_context_area_ram_session() + { + static Context_area_ram_session inst; + return &inst; + } +} + diff --git a/base-linux/src/core/include/cap_session_component.h b/base-linux/src/core/include/cap_session_component.h new file mode 100644 index 000000000..f63d1d145 --- /dev/null +++ b/base-linux/src/core/include/cap_session_component.h @@ -0,0 +1,47 @@ +/* + * \brief Capability allocation service + * \author Norman Feske + * \date 2006-06-26 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__CAP_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__LINUX__CAP_SESSION_COMPONENT_H_ + +#include +#include +#include + +namespace Genode { + + class Cap_session_component : public Rpc_object + { + private: + + static long _unique_id_cnt; + static Lock &_lock() + { + static Lock static_lock; + return static_lock; + } + + public: + + Native_capability alloc(Native_capability ep) + { + Lock::Guard lock_guard(_lock()); + + return Native_capability(ep.tid(), ++_unique_id_cnt); + } + + void free(Native_capability cap) { } + }; +} + +#endif /* _CORE__INCLUDE__LINUX__CAP_SESSION_COMPONENT_H_ */ diff --git a/base-linux/src/core/include/dataspace_component.h b/base-linux/src/core/include/dataspace_component.h new file mode 100644 index 000000000..fa1b05ce3 --- /dev/null +++ b/base-linux/src/core/include/dataspace_component.h @@ -0,0 +1,91 @@ +/* + * \brief Core-internal dataspace representation on Linux + * \author Norman Feske + * \date 2006-05-19 + * + * On Linux userland, we do not deal with physical memory. Instead, + * we create a file for each dataspace that is to be mmapped. + * Therefore, the allocator is not really used for allocating + * memory but only as a container for quota. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__DATASPACE_COMPONENT_H_ +#define _CORE__INCLUDE__LINUX__DATASPACE_COMPONENT_H_ + +#include +#include +#include +#include +#include + +namespace Genode { + + class Dataspace_component : public Rpc_object + { + private: + + size_t _size; /* size of dataspace in bytes */ + addr_t _addr; /* meaningless on linux */ + Filename _fname; /* filename for mmap */ + bool _writable; /* false if read-only */ + + public: + + /** + * Constructor + */ + Dataspace_component(size_t size, addr_t addr, bool writable) + : _size(size), _addr(addr), _writable(writable) { } + + /** + * Default constructor returns invalid dataspace + */ + Dataspace_component() : _size(0), _addr(0), _writable(false) { } + + /** + * This constructor is only provided for compatibility + * reasons and should not be used. + */ + Dataspace_component(size_t size, addr_t core_local_addr, + addr_t phys_addr, bool write_combined, + bool writable) + : _size(size), _addr(phys_addr) + { + PWRN("Should only be used for IOMEM and not within Linux."); + } + + /** + * Define/request corresponding filename of dataspace + * + * To use dataspaces as shared memory objects on Linux, we have to + * assign a file to each dataspace. This way, multiple Linux process + * can mmap this file. + */ + void fname(const char *fname) { strncpy(_fname.buf, fname, sizeof(_fname.buf)); } + + + /************************* + ** Dataspace interface ** + *************************/ + + size_t size() { return _size; } + addr_t phys_addr() { return _addr; } + bool writable() { return _writable; } + + + /**************************************** + ** Linux-specific dataspace interface ** + ****************************************/ + + Filename fname() { return _fname; } + }; +} + +#endif /* _CORE__INCLUDE__LINUX__DATASPACE_COMPONENT_H_ */ diff --git a/base-linux/src/core/include/io_mem_session_component.h b/base-linux/src/core/include/io_mem_session_component.h new file mode 100644 index 000000000..61344c6d6 --- /dev/null +++ b/base-linux/src/core/include/io_mem_session_component.h @@ -0,0 +1,64 @@ +/* + * \brief Core-specific instance of the IO_MEM session interface (Linux) + * \author Christian Helmuth + * \date 2007-09-14 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__IO_MEM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__LINUX__IO_MEM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Io_mem_session_component : public Rpc_object + { + public: + + /** + * Constructor + * + * \param io_mem_alloc MMIO region allocator + * \param ram_alloc RAM allocator that will be checked for + * region collisions + * \param ds_ep entry point to manage the dataspace + * corresponding the io_mem session + * \param args session construction arguments, in + * particular MMIO region base, size and + * caching demands + */ + Io_mem_session_component(Range_allocator *io_mem_alloc, + Range_allocator *ram_alloc, + Rpc_entrypoint *ds_ep, + const char *args); + + /** + * Destructor + */ + ~Io_mem_session_component() { } + + + /***************************** + ** Io-mem session interface ** + *****************************/ + + Io_mem_dataspace_capability dataspace() { + return Io_mem_dataspace_capability(); } + }; +} + +#endif /* _CORE__INCLUDE__LINUX__IO_MEM_SESSION_COMPONENT_H_ */ diff --git a/base-linux/src/core/include/irq_session_component.h b/base-linux/src/core/include/irq_session_component.h new file mode 100644 index 000000000..907b88d98 --- /dev/null +++ b/base-linux/src/core/include/irq_session_component.h @@ -0,0 +1,60 @@ +/* + * \brief Core-specific instance of the IRQ session interface for Linux + * \author Christian Helmuth + * \date 2007-09-13 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__IRQ_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__LINUX__IRQ_SESSION_COMPONENT_H_ + +#include +#include +#include +#include + +namespace Genode { + + class Irq_session_component : public List::Element + { + public: + + /** + * Constructor + * + * \param cap_session capability session to use + * \param irq_alloc platform-dependent IRQ allocator + * \param args session construction arguments + */ + Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) { } + + /** + * Destructor + */ + ~Irq_session_component() { } + + /** + * Return capability to this session + * + * Capability is always invalid under Linux. + */ + Session_capability cap() const { return Session_capability(); } + + + /*************************** + ** Irq session interface ** + ***************************/ + + void wait_for_irq() { } + }; +} + +#endif /* _CORE__INCLUDE__LINUX__IRQ_SESSION_COMPONENT_H_ */ diff --git a/base-linux/src/core/include/pd_session_component.h b/base-linux/src/core/include/pd_session_component.h new file mode 100644 index 000000000..bc4866b5e --- /dev/null +++ b/base-linux/src/core/include/pd_session_component.h @@ -0,0 +1,61 @@ +/* + * \brief CORE-specific instance of the PD session interface for Linux + * \author Norman Feske + * \date 2006-08-14 + * + * On Linux, we use a pd session only for keeping the information about the + * existence of protection domains to enable us to destruct all pds of a whole + * subtree. A pd is killed by CORE when closing the corresponding pd session. + * The PID of the process is passed to CORE as an argument of the session + * construction. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__PD_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__LINUX__PD_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include "platform.h" + +namespace Genode { + + class Pd_session_component : public Rpc_object + { + private: + + unsigned long _pid; + + public: + + Pd_session_component(Rpc_entrypoint *thread_ep, + const char *args); + + ~Pd_session_component(); + + + /****************************/ + /** Pd session interface **/ + /****************************/ + + /* + * This interface is not functional on Linux. + */ + + int bind_thread(Thread_capability thread); + int assign_parent(Parent_capability); + }; +} + +#endif /* _CORE__INCLUDE__LINUX__PD_SESSION_COMPONENT_H_ */ diff --git a/base-linux/src/core/include/platform.h b/base-linux/src/core/include/platform.h new file mode 100644 index 000000000..1d3379b1d --- /dev/null +++ b/base-linux/src/core/include/platform.h @@ -0,0 +1,62 @@ +/* + * \brief Linux platform + * \author Christian Helmuth + * \author Norman Feske + * \date 2007-09-10 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__PLATFORM_H_ +#define _CORE__INCLUDE__LINUX__PLATFORM_H_ + +#include +#include +#include + +#include +#include +#include + +namespace Genode { + + using namespace Genode; + + class Platform : public Platform_generic + { + private: + + Synchronized_range_allocator _ram_alloc; /* RAM allocator */ + + public: + + /** + * Constructor + */ + Platform(); + + + /******************************** + ** Generic platform interface ** + ********************************/ + + Range_allocator *core_mem_alloc() { return &_ram_alloc; } + Range_allocator *ram_alloc() { return &_ram_alloc; } + Range_allocator *io_mem_alloc() { return 0; } + Range_allocator *io_port_alloc() { return 0; } + Range_allocator *irq_alloc() { return 0; } + Range_allocator *region_alloc() { return 0; } + addr_t vm_start() const { return 0; } + size_t vm_size() const { return 0; } + Rom_fs *rom_fs() { return 0; } + + void wait_for_exit(); + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-linux/src/core/include/platform_pd.h b/base-linux/src/core/include/platform_pd.h new file mode 100644 index 000000000..cd9fa461a --- /dev/null +++ b/base-linux/src/core/include/platform_pd.h @@ -0,0 +1,25 @@ +/* + * \brief Linux protection domain facility + * \author Norman Feske + * \date 2006-06-13 + * + * Pretty dumb. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__PLATFORM_PD_H_ +#define _CORE__INCLUDE__LINUX__PLATFORM_PD_H_ + +namespace Genode { + + class Platform_pd + { }; +} + +#endif /* _CORE__INCLUDE__LINUX__PLATFORM_PD_H_ */ diff --git a/base-linux/src/core/include/platform_thread.h b/base-linux/src/core/include/platform_thread.h new file mode 100644 index 000000000..9fadda64d --- /dev/null +++ b/base-linux/src/core/include/platform_thread.h @@ -0,0 +1,65 @@ +/* + * \brief Linux thread facility + * \author Norman Feske + * \date 2006-06-13 + * + * Pretty dumb. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__LINUX__PLATFORM_THREAD_H_ + +#include +#include + +namespace Genode { + + class Platform_thread + { + private: + + unsigned long _tid; + unsigned long _pid; + char _name[32]; + + public: + + /** + * Constructor + */ + Platform_thread(const char *name, unsigned priority); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Dummy implementation of platform-thread interface + */ + Pager_object *pager() { return 0; } + void pager(Pager_object *) { } + int start(void *ip, void *sp) { return 0; } + int state(Thread_state *state_dst) { return 0; } + const char *name() { return _name; } + }; +} + +#endif /* _CORE__INCLUDE__LINUX__PLATFORM_THREAD_H_ */ diff --git a/base-linux/src/core/include/rm_session_component.h b/base-linux/src/core/include/rm_session_component.h new file mode 100644 index 000000000..aa2aa535c --- /dev/null +++ b/base-linux/src/core/include/rm_session_component.h @@ -0,0 +1,64 @@ +/* + * \brief Core-specific instance of the RM session interface + * \author Christian Helmuth + * \date 2006-07-17 + * + * Dummies for Linux platform + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LINUX__RM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__LINUX__RM_SESSION_COMPONENT_H_ + +/* Genode */ +#include +#include +#include +#include + +namespace Genode { + + struct Rm_client; + + class Rm_session_component : public Rpc_object + { + public: + + Rm_session_component(Rpc_entrypoint *ds_ep, + Rpc_entrypoint *thread_ep, + Allocator *md_alloc, + size_t ram_quota, + Pager_entrypoint *pager_ep, + addr_t vm_start, + size_t vm_size) { } + + void upgrade_ram_quota(size_t ram_quota) { } + + Local_addr attach(Dataspace_capability, size_t, off_t, bool, Local_addr) { + return (addr_t)0; } + + void detach(Local_addr) { } + + Pager_capability add_client(Thread_capability) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } + + void dissolve(Rm_client *cl) { } + }; + + struct Rm_member { Rm_session_component *member_rm_session() { return 0; } }; + struct Rm_client : Pager_object, Rm_member { }; +} + +#endif /* _CORE__INCLUDE__LINUX__RM_SESSION_COMPONENT_H_ */ diff --git a/base-linux/src/core/io_mem_session_component.cc b/base-linux/src/core/io_mem_session_component.cc new file mode 100644 index 000000000..cead86767 --- /dev/null +++ b/base-linux/src/core/io_mem_session_component.cc @@ -0,0 +1,27 @@ +/* + * \brief Linux-specific IO_MEM service + * \author Christian Helmuth + * \date 2006-09-01 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +#include + +using namespace Genode; + + +Io_mem_session_component::Io_mem_session_component(Range_allocator *io_mem_alloc, + Range_allocator *ram_alloc, + Rpc_entrypoint *ds_ep, + const char *args) +{ + PWRN("no io_mem support on Linux (args=\"%s\")", args); +} diff --git a/base-linux/src/core/io_port_session_component.cc b/base-linux/src/core/io_port_session_component.cc new file mode 100644 index 000000000..d5ef72473 --- /dev/null +++ b/base-linux/src/core/io_port_session_component.cc @@ -0,0 +1,59 @@ +/* + * \brief Linux-specific IO_PORT service + * \author Christian Helmuth + * \date 2007-04-18 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +#include "io_port_session_component.h" + +using namespace Genode; + + +unsigned char Io_port_session_component::inb(unsigned short address) { + return 0; } + + +unsigned short Io_port_session_component::inw(unsigned short address) { + return 0; } + + +unsigned Io_port_session_component::inl(unsigned short address) { + return 0; } + + +void Io_port_session_component::outb(unsigned short address, unsigned char value) { +} + + +void Io_port_session_component::outw(unsigned short address, unsigned short value) { +} + + +void Io_port_session_component::outl(unsigned short address, unsigned value) { +} + + +Io_port_session_component::Io_port_session_component(Range_allocator *io_port_alloc, + const char *args) +: _io_port_alloc(io_port_alloc) +{ + PWRN("no IO_PORT support under Linux (args=\"%s\")", args); + _size = 0; + throw Root::Invalid_args(); +} + + +Io_port_session_component::~Io_port_session_component() +{ + PERR("Implement me, immediately!"); +} diff --git a/base-linux/src/core/pd_session_component.cc b/base-linux/src/core/pd_session_component.cc new file mode 100644 index 000000000..5f809b68b --- /dev/null +++ b/base-linux/src/core/pd_session_component.cc @@ -0,0 +1,51 @@ +/** + * \brief Core implementation of the PD session interface + * \author Christian Helmuth + * \date 2006-07-17 + * + * FIXME arg_string and quota missing + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode */ +#include + +/* Core */ +#include + +/* Linux includes */ +#include + +using namespace Genode; + + +Pd_session_component::Pd_session_component(Rpc_entrypoint *thread_ep, + const char *args) +{ + _pid = Arg_string::find_arg(args, "PID").long_value(0); +} + + +Pd_session_component::~Pd_session_component() +{ + if (_pid) + lx_kill(_pid, 9); +} + + +int Pd_session_component::bind_thread(Thread_capability) +{ + return -1; +} + + +int Pd_session_component::assign_parent(Parent_capability) +{ + return -1; +} diff --git a/base-linux/src/core/platform.cc b/base-linux/src/core/platform.cc new file mode 100644 index 000000000..2946a870f --- /dev/null +++ b/base-linux/src/core/platform.cc @@ -0,0 +1,62 @@ +/* + * \brief Linux platform interface implementation + * \author Norman Feske + * \date 2006-06-13 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include "platform.h" +#include "core_env.h" + +/* Linux includes */ +#include +#include + + +using namespace Genode; + + +static char _some_mem[80*1024*1024]; +static Lock _wait_for_exit_lock(Lock::LOCKED); /* exit() sync */ + + +static void signal_handler(int signum) +{ + _wait_for_exit_lock.unlock(); +} + + +Platform::Platform() +: _ram_alloc(0) +{ + /* catch control-c */ + lx_sigaction(2, signal_handler); + + /* create resource directory under /tmp */ + lx_mkdir(lx_rpath(), S_IRWXU); + + _ram_alloc.add_range((addr_t)_some_mem, sizeof(_some_mem)); +} + + +void Platform::wait_for_exit() +{ + /* block until exit condition is satisfied */ + try { _wait_for_exit_lock.lock(); } + catch (Blocking_canceled) { }; +} + +void Core_parent::exit(int exit_value) +{ + lx_exit_group(exit_value); +} diff --git a/base-linux/src/core/platform_thread.cc b/base-linux/src/core/platform_thread.cc new file mode 100644 index 000000000..7f7761903 --- /dev/null +++ b/base-linux/src/core/platform_thread.cc @@ -0,0 +1,86 @@ +/* + * \brief Linux-specific platform thread implementation + * \author Norman Feske + * \date 2007-10-15 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include "platform_thread.h" + +/* Linux syscall helper */ +#include + +using namespace Genode; + + +typedef Token Tid_token; + + +Platform_thread::Platform_thread(const char *name, unsigned) +{ + /* search for thread-id portion of thread name */ + Tid_token tok(name); + while (tok.type() != Tid_token::END && tok[0] != ':') + tok = tok.next(); + + /* tok points at the colon separator, next token is the id */ + tok = tok.next(); + + if (tok.type() == Tid_token::END) { + PWRN("Invalid format of thread name."); + return; + } + + /* convert string to thread id */ + ascii_to(tok.start(), &_tid); + + /* search for process-id portion of thread name */ + while (tok.type() != Tid_token::END && tok[0] != ':') + tok = tok.next(); + + /* tok points at the colon separator, next token is the id */ + tok = tok.next(); + + if (tok.type() == Tid_token::END) { + PWRN("Invalid format of thread name."); + return; + } + + /* convert string to process id */ + ascii_to(tok.start(), &_pid); + + /* initialize private members */ + size_t name_len = tok.start() - name; + strncpy(_name, name, min(sizeof(_name), name_len)); +} + + +void Platform_thread::cancel_blocking() +{ + PDBG("send cancel-blocking signal to %ld\n", _tid); + lx_tgkill(_pid, _tid, LX_SIGUSR1); +} + + +void Platform_thread::pause() +{ + PDBG("not implemented"); +} + + +void Platform_thread::resume() +{ + PDBG("not implemented"); +} diff --git a/base-linux/src/core/ram_session_support.cc b/base-linux/src/core/ram_session_support.cc new file mode 100644 index 000000000..dc5815dba --- /dev/null +++ b/base-linux/src/core/ram_session_support.cc @@ -0,0 +1,59 @@ +/* + * \brief Make dataspace accessible to other Linux processes + * \author Norman Feske + * \date 2006-07-03 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* glibc includes */ +#include + +/* Genode includes */ +#include + +/* local includes */ +#include "ram_session_component.h" + +/* Linux syscall bindings */ +#include +#include + + +using namespace Genode; + + +static int ram_ds_cnt = 0; /* counter for creating unique dataspace IDs */ + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) +{ + char fname_buf[Linux_dataspace::FNAME_LEN]; + + /* assign filename to dataspace */ + snprintf(fname_buf, sizeof(fname_buf), "%s/ds-%d", lx_rpath(), ram_ds_cnt++); + + ds->fname(fname_buf); + + /* create new file representing the dataspace */ + lx_unlink(fname_buf); + int fd = lx_open(fname_buf, O_CREAT | O_RDWR | O_TRUNC | LX_O_CLOEXEC, S_IRWXU); + lx_ftruncate(fd, ds->size()); + lx_close(fd); +} + + +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) +{ + lx_unlink(ds->fname().buf); +} + + +void Ram_session_component::_clear_ds(Dataspace_component *ds) +{ + memset((void *)ds->phys_addr(), 0, ds->size()); +} diff --git a/base-linux/src/core/rom_session_component.cc b/base-linux/src/core/rom_session_component.cc new file mode 100644 index 000000000..9a5f39d25 --- /dev/null +++ b/base-linux/src/core/rom_session_component.cc @@ -0,0 +1,71 @@ +/* + * \brief Linux-specific core implementation of the ROM session interface + * \author Norman Feske + * \date 2006-07-06 + * + * The Linux version of ROM session component does not use the + * Rom_fs as provided as constructor argument. Instead, we map + * rom modules directly to files of the host file system. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Linux includes */ +#include + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include "rom_session_component.h" + +using namespace Genode; + + +static Genode::size_t file_size(const char *path) +{ + struct stat64 s; + if (lx_stat(path, &s) < 0) + return 0; + else + return s.st_size; +} + + +Rom_session_component::Rom_session_component(Rom_fs *rom_fs, + Rpc_entrypoint *ds_ep, + const char *args) +: _ds_ep(ds_ep) +{ + /* extract filename from session arguments */ + char fname_buf[Linux_dataspace::FNAME_LEN]; + Arg_string::find_arg(args, "filename").string(fname_buf, sizeof(fname_buf), ""); + + Genode::size_t fsize = file_size(fname_buf); + + /* use invalid capability as default value */ + _ds_cap = Rom_dataspace_capability(); + + /* ROM module not found */ + if (fsize == 0) + throw Root::Invalid_args(); + + _ds = Dataspace_component(fsize, 0, false); + _ds.fname(fname_buf); + + Dataspace_capability ds_cap = _ds_ep->manage(&_ds); + _ds_cap = static_cap_cast(ds_cap); +} + + +Rom_session_component::~Rom_session_component() +{ + _ds_ep->dissolve(&_ds); +} diff --git a/base-linux/src/core/target.mk b/base-linux/src/core/target.mk new file mode 100644 index 000000000..c2b11d79a --- /dev/null +++ b/base-linux/src/core/target.mk @@ -0,0 +1,36 @@ +TARGET = core +REQUIRES = linux +LIBS = cxx ipc heap core_printf process lock raw_server syscall rpath + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = main.cc \ + platform.cc \ + platform_thread.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_port_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + thread.cc \ + thread_linux.cc \ + context_area.cc \ + debug.cc + +INC_DIR += $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include \ + $(REP_DIR)/src/platform \ + /usr/include + +vpath main.cc $(GEN_CORE_DIR) +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath signal_source_component.cc $(GEN_CORE_DIR) +vpath debug.cc $(REP_DIR)/src/base/env +vpath %.cc $(PRG_DIR) diff --git a/base-linux/src/core/thread_linux.cc b/base-linux/src/core/thread_linux.cc new file mode 100644 index 000000000..14f7c60a5 --- /dev/null +++ b/base-linux/src/core/thread_linux.cc @@ -0,0 +1,55 @@ +/* + * \brief Implementation of the core-internal Thread API via Linux threads + * \author Norman Feske + * \date 2006-06-13 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* Linux syscall bindings */ +#include + +using namespace Genode; + + +static void empty_signal_handler(int) { } + + +static void thread_start(void *) +{ + /* + * Set signal handler such that canceled system calls get not + * transparently retried after a signal gets received. + */ + lx_sigaction(LX_SIGUSR1, empty_signal_handler); + + Thread_base::myself()->entry(); + sleep_forever(); +} + + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() { } + + +void Thread_base::start() +{ + /* align initial stack to 16 byte boundary */ + void *thread_sp = (void *)((addr_t)(_context->stack) & ~0xf); + _tid.tid = lx_create_thread(thread_start, thread_sp, this); + _tid.pid = lx_getpid(); +} + + +void Thread_base::cancel_blocking() { } diff --git a/base-linux/src/platform/_main_helper.h b/base-linux/src/platform/_main_helper.h new file mode 100644 index 000000000..8aa049a03 --- /dev/null +++ b/base-linux/src/platform/_main_helper.h @@ -0,0 +1,40 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Christian Prochaska + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM___MAIN_HELPER_H_ +#define _PLATFORM___MAIN_HELPER_H_ + +#include + +#include + +/* + * Define 'lx_environ' pointer that is supposed to be initialized by the + * startup code. + */ +__attribute__((weak)) char **lx_environ = (char **)0; + + +static void main_thread_bootstrap() +{ + using namespace Genode; + + /* reserve context area */ + Genode::addr_t base = Thread_base::CONTEXT_AREA_VIRTUAL_BASE; + Genode::size_t size = Thread_base::CONTEXT_AREA_VIRTUAL_SIZE; + if (lx_vm_reserve(base, size) != base) + PERR("reservation of context area [%lx,%lx) failed", + (unsigned long) base, (unsigned long) base + size); +} + +#endif /* _PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-linux/src/platform/context_area.nostdlib.ld b/base-linux/src/platform/context_area.nostdlib.ld new file mode 100644 index 000000000..fa3e2f54a --- /dev/null +++ b/base-linux/src/platform/context_area.nostdlib.ld @@ -0,0 +1,25 @@ +/* + * \brief Linux-specific linker script additions (STDLIB = no) + * \author Christian Helmuth + * \date 2010-09-22 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +PHDRS +{ + context_area PT_LOAD FLAGS(0); +} + +SECTIONS +{ + . = 0x40000000; + _context_area_start = .; + .context_area : { . += 0x10000000; } : context_area + _context_area_end = .; +} diff --git a/base-linux/src/platform/context_area.stdlib.ld b/base-linux/src/platform/context_area.stdlib.ld new file mode 100644 index 000000000..1ef45a1c1 --- /dev/null +++ b/base-linux/src/platform/context_area.stdlib.ld @@ -0,0 +1,20 @@ +/* + * \brief Linux-specific linker script additions (STDLIB = yes) + * \author Christian Helmuth + * \date 2010-09-22 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +SECTIONS +{ + . = 0x40000000; + _context_area_start = .; + .context_area : { . += 0x10000000; } + _context_area_end = .; +} diff --git a/base-linux/src/platform/linux_rpath.cc b/base-linux/src/platform/linux_rpath.cc new file mode 100644 index 000000000..3432798c0 --- /dev/null +++ b/base-linux/src/platform/linux_rpath.cc @@ -0,0 +1,39 @@ +/* + * \brief Linux resource path + * \author Christian Helmuth + * \date 2011-09-25 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +#include +#include + + +namespace { + struct Rpath + { + char string[32]; + + Rpath() + { + Genode::snprintf(string, sizeof(string), "/tmp/genode-%d", lx_getuid()); + } + }; +} + + +char const * lx_rpath() +{ + static Rpath rpath; + + return rpath.string; +} diff --git a/base-linux/src/platform/linux_rpath.h b/base-linux/src/platform/linux_rpath.h new file mode 100644 index 000000000..34f52fe4f --- /dev/null +++ b/base-linux/src/platform/linux_rpath.h @@ -0,0 +1,25 @@ +/* + * \brief Linux resource path + * \author Christian Helmuth + * \date 2011-09-25 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__LINUX_RPATH_H_ +#define _PLATFORM__LINUX_RPATH_H_ + + +/** + * Return resource path for Genode + * + * Genode creates files for dataspaces and endpoints under in this directory. + */ +char const * lx_rpath(); + +#endif /* _PLATFORM__LINUX_RPATH_H_ */ diff --git a/base-linux/src/platform/linux_syscalls.h b/base-linux/src/platform/linux_syscalls.h new file mode 100644 index 000000000..5433178fb --- /dev/null +++ b/base-linux/src/platform/linux_syscalls.h @@ -0,0 +1,501 @@ +/* + * \brief Linux system-call wrappers + * \author Norman Feske + * \date 2008-10-22 + * + * This file is meant to be internally used by the framework. It is not public + * interface. + * + * From within the framework libraries, we have to use the Linux syscall + * interface directly rather than relying on convenient libC functions to be + * able to link this part of the framework to a custom libC. Otherwise, we + * would end up with very nasty cyclic dependencies when using framework + * functions such as IPC from the libC back end. + * + * The Linux syscall interface looks different for 32bit and 64bit system, in + * particular regarding the socket interface. On 32bit systems, all socket + * operations are invoked via the 'socketcall' syscall. On 64bit systems, the + * different socket functions have distinct syscalls. + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__LINUX_SYSCALLS_H_ +#define _PLATFORM__LINUX_SYSCALLS_H_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 /* needed to enable the definition of 'stat64' */ +#endif + +/* Linux includes */ +#include +#include +#include +#include +#include + +/* Genode includes */ +#include + + +/***************************************** + ** Functions used by the IPC framework ** + *****************************************/ + +#include + +extern "C" long lx_syscall(int number, ...); +extern "C" int lx_clone(int (*fn)(void *), void *child_stack, + int flags, void *arg); + + +inline Genode::uint16_t lx_bswap16(Genode::uint16_t x) +{ + char v[2] = { + (x & 0xff00) >> 8, + (x & 0x00ff) >> 0, + }; + return *(Genode::uint16_t *)v; +} + + +inline Genode::uint32_t lx_bswap32(Genode::uint32_t x) +{ + char v[4] = { + (x & 0xff000000) >> 24, + (x & 0x00ff0000) >> 16, + (x & 0x0000ff00) >> 8, + (x & 0x000000ff) >> 0, + }; + return *(Genode::uint32_t *)v; +} + +#define lx_htonl(x) lx_bswap32(x) +#define lx_htons(x) lx_bswap16(x) +#define lx_ntohs(x) lx_bswap16(x) + +#ifdef SYS_socketcall + +inline int lx_socketcall(int call, unsigned long *args) +{ + int res = lx_syscall(SYS_socketcall, call, args); + return res; +} + + +inline int lx_socket(int domain, int type, int protocol) +{ + unsigned long args[3] = { domain, type, protocol }; + return lx_socketcall(SYS_SOCKET, args); +} + + +inline int lx_connect(int sockfd, const struct sockaddr *serv_addr, + socklen_t addrlen) +{ + unsigned long args[3] = { sockfd, (unsigned long)serv_addr, addrlen }; + return lx_socketcall(SYS_CONNECT, args); +} + + +inline int lx_bind(int sockfd, const struct sockaddr *addr, + socklen_t addrlen) +{ + unsigned long args[3] = { sockfd, (unsigned long)addr, addrlen }; + return lx_socketcall(SYS_BIND, args); +} + + +inline int lx_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + unsigned long args[3] = { s, (unsigned long)name, (unsigned long)namelen }; + return lx_socketcall(SYS_GETSOCKNAME, args); +} + + +inline ssize_t lx_recvfrom(int s, void *buf, Genode::size_t len, int flags, + struct sockaddr *from, socklen_t *from_len) +{ + unsigned long args[6] = { s, (unsigned long)buf, len, flags, + (unsigned long)from, (unsigned long)from_len }; + return lx_socketcall(SYS_RECVFROM, args); +} + + +inline ssize_t lx_sendto(int s, void *buf, Genode::size_t len, int flags, + struct sockaddr *to, socklen_t to_len) +{ + unsigned long args[6] = { s, (unsigned long)buf, len, flags, + (unsigned long)to, (unsigned long)to_len }; + return lx_socketcall(SYS_SENDTO, args); +} + +#else + +inline int lx_socket(int domain, int type, int protocol) +{ + return lx_syscall(SYS_socket, domain, type, protocol); +} + + +inline int lx_connect(int sockfd, const struct sockaddr *serv_addr, + socklen_t addrlen) +{ + return lx_syscall(SYS_connect, sockfd, serv_addr, addrlen); +} + + +inline int lx_bind(int sockfd, const struct sockaddr *addr, + socklen_t addrlen) +{ + return lx_syscall(SYS_bind, sockfd, addr, addrlen); +} + + +inline int lx_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lx_syscall(SYS_getsockname, s, name, namelen); +} + + +inline ssize_t lx_recvfrom(int s, void *buf, Genode::size_t len, int flags, + struct sockaddr *from, socklen_t *from_len) +{ + return lx_syscall(SYS_recvfrom, s, buf, len, flags, from, from_len); +} + + +inline ssize_t lx_sendto(int s, void *buf, Genode::size_t len, int flags, + struct sockaddr *to, socklen_t to_len) +{ + return lx_syscall(SYS_sendto, s, buf, len, flags, to, to_len); +} + +#endif /* SYS_socketcall */ + + +inline int lx_write(int fd, const void *buf, Genode::size_t count) +{ + return lx_syscall(SYS_write, fd, buf, count); +} + + +inline int lx_close(int fd) +{ + return lx_syscall(SYS_close, fd); +} + + +/******************************************* + ** Functions used by the process library ** + *******************************************/ + +inline int lx_execve(const char *filename, char *const argv[], + char *const envp[]) +{ + return lx_syscall(SYS_execve, filename, argv, envp); +} + + +inline void lx_exit(int status) +{ + lx_syscall(SYS_exit, status); +} + + +inline void lx_exit_group(int status) +{ + lx_syscall(SYS_exit_group, status); +} + + +/************************************************************ + ** Functions used by the env library and local rm session ** + ************************************************************/ + +/* O_CLOEXEC is a GNU extension so we provide it here */ +enum { LX_O_CLOEXEC = 02000000 }; + +inline int lx_open(const char *pathname, int flags, mode_t mode = 0) +{ + return lx_syscall(SYS_open, pathname, flags, mode); +} + + +inline void *lx_mmap(void *start, Genode::size_t length, int prot, int flags, + int fd, off_t offset) +{ +#ifdef _LP64 + return (void *)lx_syscall(SYS_mmap, start, length, prot, flags, fd, offset); +#else + return (void *)lx_syscall(SYS_mmap2, start, length, prot, flags, fd, offset/4096); +#endif /* _LP64 */ +} + + +inline int lx_munmap(void *addr, size_t length) +{ + return lx_syscall(SYS_munmap, addr, length); +} + + +/** + * Exclude local virtual memory area from being used by mmap + * + * \param base base address of area to reserve + * \param size number of bytes to reserve + * + * \return start of allocated reserved area, or ~0 on failure + */ +inline Genode::addr_t lx_vm_reserve(Genode::addr_t base, Genode::size_t size) +{ + /* we cannot include sys/mman.h from here */ + enum { + LX_MAP_PRIVATE = 0x02, + LX_MAP_FIXED = 0x10, + LX_MAP_ANONYMOUS = 0x20, + LX_PROT_NONE = 0x0 + }; + + int const flags = LX_MAP_ANONYMOUS | LX_MAP_PRIVATE + | (base ? LX_MAP_FIXED : 0); + + void * const res = lx_mmap((void *)base, size, LX_PROT_NONE, flags, -1, 0); + + if (base) + return ((Genode::addr_t)res == base) ? base : ~0; + else + return (Genode::addr_t)res; +} + + +/******************************************************* + ** Functions used by core's ram-session support code ** + *******************************************************/ + +inline int lx_mkdir(char const *pathname, mode_t mode) +{ + return lx_syscall(SYS_mkdir, pathname, mode); +} + + +inline int lx_ftruncate(int fd, unsigned long length) +{ + return lx_syscall(SYS_ftruncate, fd, length); +} + + +inline int lx_unlink(const char *fname) +{ + return lx_syscall(SYS_unlink, fname); +} + +/******************************************************* + ** Functions used by core's rom-session support code ** + *******************************************************/ + +inline int lx_stat(const char *path, struct stat64 *buf) +{ +#ifdef _LP64 + return lx_syscall(SYS_stat, path, buf); +#else + return lx_syscall(SYS_stat64, path, buf); +#endif +} + +/*********************************************************************** + ** Functions used by thread lib and core's cancel-blocking mechanism ** + ***********************************************************************/ + +enum { + LX_SIGUSR1 = 10, /* used for cancel-blocking mechanism */ + LX_SIGCANCEL = 32, /* accoring to glibc, this equals SIGRTMIN, + used for killing threads */ +}; + + +struct kernel_sigaction +{ + void (*handler)(int); + unsigned long flags; + void (*restorer)(void); + sigset_t mask; +}; + + +inline int lx_sigemptyset(sigset_t *set) +{ + if (set == 0) + return -1; + Genode::memset(set, 0, sizeof(sigset_t)); + return 0; +} + + +#ifdef _LP64 +extern "C" void lx_restore_rt (void); +#endif + +/** + * Simplified binding for sigaction system call + */ +inline int lx_sigaction(int signum, void (*handler)(int)) +{ + struct kernel_sigaction act; + act.handler = handler; + +#ifdef _LP64 + /* + * The SA_RESTORER flag is not officially documented, but used internally + * by the glibc implementation of sigaction(). Without specifying this flag + * tgkill() does not work on x86_64. The restorer function gets called + * when leaving the signal handler and it should call the rt_sigreturn syscall. + */ + enum { SA_RESTORER = 0x04000000 }; + act.flags = SA_RESTORER; + act.restorer = lx_restore_rt;; +#else + act.flags = 0; + act.restorer = 0; +#endif + lx_sigemptyset(&act.mask); + + return lx_syscall(SYS_rt_sigaction, signum, &act, 0UL, _NSIG/8); +} + + +/** + * Send signal to process + * + * This function is used by core to kill processes. + */ +inline int lx_kill(int pid, int signal) +{ + return lx_syscall(SYS_kill, pid, signal); +} + + +/** + * Send signal to thread + * + * This function is used by core to cancel blocking operations of + * threads, and by the thread library to kill threads. + */ +inline int lx_tgkill(int pid, int tid, int signal) +{ + return lx_syscall(SYS_tgkill, pid, tid, signal); +} + + +inline int lx_create_thread(void (*entry)(void *), void *stack, void *arg) +{ + int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND + | CLONE_THREAD | CLONE_SYSVSEM; + + /* + * The syscall binding for clone does not exist in the FreeBSD libc, which + * we are using as libc for Genode. In glibc, clone is implemented as a + * assembler binding without external libc references. Hence, we are safe + * to rely on the glibc version of 'clone' here. + */ + return lx_clone((int (*)(void *))entry, stack, flags, arg); +} + + +inline int lx_create_process(int (*entry)(void *), void *stack, void *arg) +{ + int flags = CLONE_VFORK | SIGCHLD; + return lx_clone((int (*)(void *))entry, stack, flags, arg); +} + + +inline pid_t lx_getpid() { return lx_syscall(SYS_getpid); } +inline pid_t lx_gettid() { return lx_syscall(SYS_gettid); } +inline uid_t lx_getuid() { return lx_syscall(SYS_getuid); } + + +/************************************ + ** Functions used by lock library ** + ************************************/ + +struct timespec; + +inline int lx_nanosleep(const struct timespec *req, struct timespec *rem) +{ + return lx_syscall(SYS_nanosleep, req, rem); +} + + +/** + * Signal set corrsponding to glibc's 'sigset_t' + */ +class Lx_sigset +{ + unsigned long int _value[_SIGSET_NWORDS]; + + public: + + /** + * Constructor + */ + Lx_sigset() { } + + /** + * Constructor + * + * \param signum set specified entry of sigset + */ + Lx_sigset(int signum) + { + + for (unsigned i = 0; i < _SIGSET_NWORDS; i++) + _value[i] = 0; + + /* + * Both '__sigword' and '__sigmask' are macros, defined in the + * glibc header file 'bits/sigset.h' and not external functions. + * Therefore we can use them here without getting into conflicts + * with the linkage of another libc. + */ + _value[__sigword(signum)] = __sigmask(signum); + } + + bool is_set(int signum) { + return _value[__sigword(signum)] && __sigmask(signum); } +}; + + +/** + * Check if signal is pending + * + * \return true if signal is pending + */ +inline bool lx_sigpending(int signum) +{ + Lx_sigset sigset; + lx_syscall(SYS_rt_sigpending, &sigset, _NSIG/8); + return sigset.is_set(signum); +} + + +/** + * Set signal mask state + * + * \param signum signal to mask or unmask + * \param state mask state for the signal, + * true enables the signal, + * false blocks the signal + */ +inline bool lx_sigsetmask(int signum, bool state) +{ + Lx_sigset old_sigmask, sigset(signum); + lx_syscall(SYS_rt_sigprocmask, state ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old_sigmask, _NSIG/8); + return old_sigmask.is_set(signum); +} + +#endif /* _PLATFORM__LINUX_SYSCALLS_H_ */ diff --git a/base-linux/src/platform/lx_hybrid.cc b/base-linux/src/platform/lx_hybrid.cc new file mode 100644 index 000000000..db8bf950c --- /dev/null +++ b/base-linux/src/platform/lx_hybrid.cc @@ -0,0 +1,47 @@ +/* + * \brief Supplemental code for hybrid Genode/Linux programs + * \author Norman Feske + * \date 2011-09-02 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include <_main_helper.h> + + +extern "C" int raw_write_str(const char *str); + + +/** + * Dummy for symbol that is normally provided by '_main.cc' + */ +int genode___cxa_atexit(void (*func)(void*), void *arg, void *dso) +{ + raw_write_str("genode___cxa_atexit called, not implemented\n"); + return 0; +} + + +/* + * Manually initialize the 'lx_environ' pointer. For non-hybrid programs, this + * pointer is initialized by the startup code. + */ +extern char **environ; +extern char **lx_environ; + +/* + * This function must be called before any other static constructor in the Genode + * application, so it gets the highest priority (lowest priority number >100) + */ +__attribute__((constructor(101))) void lx_hybrid_init() +{ + main_thread_bootstrap(); + lx_environ = environ; +} diff --git a/base-linux/src/platform/x86_32/crt0.s b/base-linux/src/platform/x86_32/crt0.s new file mode 100644 index 000000000..384207b8b --- /dev/null +++ b/base-linux/src/platform/x86_32/crt0.s @@ -0,0 +1,70 @@ +/* + * \brief Startup code for Genode applications + * \author Christian Helmuth + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/*--- .text (program code) -------------------------*/ + .text + + .globl _start +_start: + + movl %esp, __initial_sp + /* + * environ = &argv[argc + 1] + * in Genode argc is always 1 + */ + popl %eax /* argc */ + popl %eax /* argv[0] */ + popl %eax /* NULL */ + movl %esp, lx_environ + + /* XXX Switch to our own stack. */ + movl $_stack_high,%esp + + /* Clear the base pointer so that stack backtraces will work. */ + xorl %ebp,%ebp + + /* Jump into init C code */ + call _main + + /* We should never get here since _main does not return */ +1: int $3 + jmp 2f + .ascii "_main() returned." +2: jmp 1b + + +/*--------------------------------------------------*/ + .data + .globl __dso_handle +__dso_handle: + .long 0 + + .globl __initial_sp +__initial_sp: + .long 0 + +/*--- .eh_frame (exception frames) -----------------*/ +/* + .section .eh_frame,"aw" + .globl __EH_FRAME_BEGIN__ +__EH_FRAME_BEGIN__: +*/ + +/*--- .bss (non-initialized data) ------------------*/ + .bss + .p2align 4 + .globl _stack_low +_stack_low: + .space 64*1024 + .globl _stack_high +_stack_high: diff --git a/base-linux/src/platform/x86_32/lx_clone.S b/base-linux/src/platform/x86_32/lx_clone.S new file mode 100644 index 000000000..cc290f656 --- /dev/null +++ b/base-linux/src/platform/x86_32/lx_clone.S @@ -0,0 +1,109 @@ +/* + * \brief Linux clone() binding + * \author Christian Prochaska + * \date 2009-07-14 + * + * based on glibc-2.9/sysdeps/unix/sysv/linux/i386/clone.S + */ + + +#define L(name) name + +#define ENTER_KERNEL int $0x80 +#define SYS_clone 120 +#define SYS_exit 1 + +#define LINKAGE 4 +#define PTR_SIZE 4 + +#define PARMS LINKAGE /* no space for saved regs */ +#define FUNC PARMS +#define STACK FUNC+4 +#define FLAGS STACK+PTR_SIZE +#define ARG FLAGS+4 +#define PTID ARG+PTR_SIZE +#define TLS PTID+PTR_SIZE +#define CTID TLS+PTR_SIZE + + .text + .globl lx_clone + .type lx_clone, @function +lx_clone: + .cfi_startproc + + /* Insert the argument onto the new stack. Make sure the new + thread is started with an alignment of (mod 16). */ + movl STACK(%esp),%ecx + andl $0xfffffff0, %ecx + subl $28,%ecx + movl ARG(%esp),%eax /* no negative argument counts */ + movl %eax,12(%ecx) + + /* Save the function pointer as the zeroth argument. + It will be popped off in the child in the ebx frobbing below. */ + movl FUNC(%esp),%eax + movl %eax,8(%ecx) + /* Don't leak any information. */ + movl $0,4(%ecx) + movl $0,(%ecx) + + /* Do the system call */ + pushl %ebx + .cfi_adjust_cfa_offset (4) + pushl %esi + .cfi_adjust_cfa_offset (4) + pushl %edi + .cfi_adjust_cfa_offset (4) + + movl TLS+12(%esp),%esi + .cfi_rel_offset %esi, 4 + movl PTID+12(%esp),%edx + movl FLAGS+12(%esp),%ebx + .cfi_rel_offset %ebx, 8 + movl CTID+12(%esp),%edi + .cfi_rel_offset %edi, 0 + movl $SYS_clone,%eax + + /* End FDE now, because in the child the unwind info will be + wrong. */ + .cfi_endproc + + ENTER_KERNEL + popl %edi + popl %esi + popl %ebx + + test %eax,%eax + jz L(thread_start) + +L(pseudo_end): + ret + +L(thread_start): + .cfi_startproc; + /* Clearing frame pointer is insufficient, use CFI. */ + .cfi_undefined %eip; + /* Note: %esi is zero. */ + movl %esi,%ebp /* terminate the stack frame */ + call *%ebx +#ifdef PIC + call L(here) +L(here): + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+[.-L(here)], %ebx +#endif + movl %eax, %ebx + movl $SYS_exit, %eax + ENTER_KERNEL + + .cfi_endproc; + + .cfi_startproc + .cfi_endproc + +/* + * Allow stacks to be mapped executable (needed because Genode does not + * offer an API to handle non-execute mappings yet). + */ +.section .note.GNU-stack, "", @progbits + diff --git a/base-linux/src/platform/x86_32/lx_syscall.S b/base-linux/src/platform/x86_32/lx_syscall.S new file mode 100644 index 000000000..fccc9c05a --- /dev/null +++ b/base-linux/src/platform/x86_32/lx_syscall.S @@ -0,0 +1,77 @@ +/* + * \brief Linux syscall() binding + * \author Christian Prochaska + * \date 2009-07-14 + * + * based on glibc-2.9/sysdeps/unix/sysv/linux/i386/syscall.S + * + * error case: + * glibc's syscall() function returns -1 and sets errno + * lx_syscall() returns -errno + */ + + +#define L(name) name + +#define ENTER_KERNEL int $0x80 + + .text + .globl lx_syscall + .type lx_syscall, @function +lx_syscall: + .cfi_startproc +/* PUSHARGS_6*/ /* Save register contents. */ +/* PUSHARGS_6 begin */ + pushl %ebp; + .cfi_adjust_cfa_offset 4; + .cfi_rel_offset %ebp, 0; +L(PUSHBP1): + pushl %edi; + .cfi_adjust_cfa_offset 4; + .cfi_rel_offset %edi, 0; +L(PUSHDI1): + pushl %esi; + .cfi_adjust_cfa_offset 4; + .cfi_rel_offset %esi, 0; +L(PUSHSI1): + pushl %ebx; + .cfi_adjust_cfa_offset 4; + .cfi_rel_offset %ebx, 0; +L(PUSHBX1): +/* PUSHARGS_6 end */ + + /*_DOARGS_6(44)*/ /* Load arguments. */ +/*_DOARGS_6(44) begin*/ + movl 44(%esp), %ebp; + movl 40(%esp), %edi; + movl 36(%esp), %esi; + movl 32(%esp), %edx; + movl 28(%esp), %ecx; + movl 24(%esp), %ebx; +/*_DOARGS_6(44) end*/ + movl 20(%esp), %eax /* Load syscall number into %eax. */ + ENTER_KERNEL /* Do the system call. */ +/* POPARGS_6*/ /* Restore register contents. */ +/* POPARGS_6 begin */ + popl %ebx; + .cfi_adjust_cfa_offset -4; + .cfi_restore %ebx; +L(POPBX1): + popl %esi; + .cfi_adjust_cfa_offset -4; + .cfi_restore %esi; +L(POPSI1): + popl %edi; + .cfi_adjust_cfa_offset -4; + .cfi_restore %edi; +L(POPDI1): + popl %ebp; + .cfi_adjust_cfa_offset -4; + .cfi_restore %ebp; +L(POPBP1): +/* POPARGS_6 end */ +L(pseudo_end): + ret /* Return to caller. */ + + .cfi_endproc + diff --git a/base-linux/src/platform/x86_64/crt0.s b/base-linux/src/platform/x86_64/crt0.s new file mode 100644 index 000000000..a51dd556e --- /dev/null +++ b/base-linux/src/platform/x86_64/crt0.s @@ -0,0 +1,74 @@ +/* + * \brief Startup code for Genode applications + * \author Christian Helmuth + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/*--- .text (program code) -------------------------*/ + .text + + .globl _start +_start: + + movq __initial_sp@GOTPCREL(%rip), %rax + movq %rsp, (%rax) + /* + * environ = &argv[argc + 1] + * in Genode argc is always 1 + */ + popq %rax /* argc */ + popq %rax /* argv[0] */ + popq %rax /* NULL */ + movq lx_environ@GOTPCREL(%rip), %rax + movq %rsp, (%rax) + + /* XXX Switch to our own stack. */ + leaq _stack_high@GOTPCREL(%rip), %rax + movq (%rax), %rsp + + /* Clear the base pointer so that stack backtraces will work. */ + xorq %rbp,%rbp + + /* Jump into init C code */ + callq _main + + /* We should never get here since _main does not return */ +1: int $3 + jmp 2f + .ascii "_main() returned." +2: jmp 1b + + +/*--------------------------------------------------*/ + .data + .p2align 8 + .globl __dso_handle +__dso_handle: + .quad 0 + + .globl __initial_sp +__initial_sp: + .quad 0 + +/*--- .eh_frame (exception frames) -----------------*/ +/* + .section .eh_frame,"aw" + .globl __EH_FRAME_BEGIN__ +__EH_FRAME_BEGIN__: +*/ + +/*--- .bss (non-initialized data) ------------------*/ + .bss + .p2align 8 + .globl _stack_low +_stack_low: + .space 64*1024 + .globl _stack_high +_stack_high: diff --git a/base-linux/src/platform/x86_64/lx_clone.S b/base-linux/src/platform/x86_64/lx_clone.S new file mode 100644 index 000000000..8edeea931 --- /dev/null +++ b/base-linux/src/platform/x86_64/lx_clone.S @@ -0,0 +1,71 @@ +/* + * \brief Linux clone() binding + * \author Christian Prochaska + * \date 2009-07-14 + * + * based on glibc-2.9/sysdeps/unix/sysv/linux/x86_64/clone.S + */ + +#define L(name) name + +#define SYS_clone 56 +#define SYS_exit 60 + + .text + .globl lx_clone + .type lx_clone, @function +lx_clone: + .cfi_startproc + + /* Insert the argument onto the new stack. */ + subq $16,%rsi + movq %rcx,8(%rsi) + + /* Save the function pointer. It will be popped off in the + child in the ebx frobbing below. */ + movq %rdi,0(%rsi) + + /* Do the system call. */ + movq %rdx, %rdi + movq %r8, %rdx + movq %r9, %r8 + movq 8(%rsp), %r10 + movl $SYS_clone,%eax + + /* End FDE now, because in the child the unwind info will be + wrong. */ + .cfi_endproc + syscall + + testq %rax,%rax + jz L(thread_start) + +L(pseudo_end): + /* parent returns */ + ret + +L(thread_start): + .cfi_startproc + /* Clearing frame pointer is insufficient, use CFI. */ + .cfi_undefined (%rip); + + /* Clear the frame pointer. The ABI suggests this be done, to mark + the outermost frame obviously. */ + xorl %ebp, %ebp + + /* Set up arguments for the function call. */ + popq %rax /* Function to call. */ + popq %rdi /* Argument. */ + call *%rax + /* Call exit with return value from function call. */ + movq %rax, %rdi + movq $SYS_exit, %rax + syscall + .cfi_endproc + +/* + * Allow stacks to be mapped executable (needed because Genode does not + * offer an API to handle non-executable mappings yet). + */ +.section .note.GNU-stack, "", @progbits + diff --git a/base-linux/src/platform/x86_64/lx_restore_rt.S b/base-linux/src/platform/x86_64/lx_restore_rt.S new file mode 100644 index 000000000..9297c9435 --- /dev/null +++ b/base-linux/src/platform/x86_64/lx_restore_rt.S @@ -0,0 +1,16 @@ +/* + * \brief Linux signal handler restorer function + * \author Christian Prochaska + * \date 2009-07-14 + * + */ + + +#define SYS_rt_sigreturn 15 + + .text + .globl lx_restore_rt + .type lx_restore_rt, @function +lx_restore_rt: + movq $SYS_rt_sigreturn, %rax + syscall diff --git a/base-linux/src/platform/x86_64/lx_syscall.S b/base-linux/src/platform/x86_64/lx_syscall.S new file mode 100644 index 000000000..47b008a88 --- /dev/null +++ b/base-linux/src/platform/x86_64/lx_syscall.S @@ -0,0 +1,29 @@ +/* + * \brief Linux syscall() binding + * \author Christian Prochaska + * \date 2009-07-14 + * + * based on glibc-2.9/sysdeps/unix/sysv/linux/x86_64/syscall.S + * + * error case: + * glibc's syscall() function returns -1 and sets errno + * lx_syscall() returns -errno + */ + + +#define L(name) name + + .text + .globl lx_syscall + .type lx_syscall, @function +lx_syscall: + movq %rdi, %rax /* Syscall number -> rax. */ + movq %rsi, %rdi /* shift arg1 - arg5. */ + movq %rdx, %rsi + movq %rcx, %rdx + movq %r8, %r10 + movq %r9, %r8 + movq 8(%rsp),%r9 /* arg6 is on the stack. */ + syscall /* Do the system call. */ +L(pseudo_end): + ret /* Return to caller. */ diff --git a/base-linux/src/test/lx_hybrid_ctors/main.cc b/base-linux/src/test/lx_hybrid_ctors/main.cc new file mode 100644 index 000000000..a7f7b83ec --- /dev/null +++ b/base-linux/src/test/lx_hybrid_ctors/main.cc @@ -0,0 +1,39 @@ +/* + * \brief Test if global static constructors in hybrid applications get called + * \author Christian Prochaska + * \date 2011-11-24 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + + +#include + +using namespace Genode; + + +struct Testapp_testclass +{ + Testapp_testclass() + { + Genode::printf("Global static constructor of Genode application called\n"); + } +}; + + +Testapp_testclass testapp_testclass; + + +int main(int argc, char *argv[]) +{ + printf("--- lx_hybrid global static constructor test ---\n"); + + printf("--- returning from main ---\n"); + + return 0; +} diff --git a/base-linux/src/test/lx_hybrid_ctors/target.mk b/base-linux/src/test/lx_hybrid_ctors/target.mk new file mode 100644 index 000000000..3125dfea4 --- /dev/null +++ b/base-linux/src/test/lx_hybrid_ctors/target.mk @@ -0,0 +1,22 @@ +TARGET = test-lx_hybrid_ctors +SRC_CC = main.cc +LIBS = env lx_hybrid + +EXT_OBJECTS += $(BUILD_BASE_DIR)/test/lx_hybrid_ctors/libtestlib.so + +TESTLIB_SO = libtestlib.so +TESTLIB_SRC_CC = testlib.cc + +$(TARGET): libtestlib.so + +$(TESTLIB_SO): $(TESTLIB_SRC_CC) + $(MSG_BUILD)$(TESTLIB_SO) + $(VERBOSE)g++ -fPIC -c $^ + $(VERBOSE)g++ -shared -Wlsoname,$(TESTLIB_SO) -o $@ $(notdir $(^:.cc=.o)) + +clean_libtestlib: + $(VERBOSE)rm -f $(TESTLIB_SO) $(TESTLIB_SRC_CC:.cc=.o) + +clean: clean_libtestlib + +vpath testlib.cc $(PRG_DIR) diff --git a/base-linux/src/test/lx_hybrid_ctors/testlib.cc b/base-linux/src/test/lx_hybrid_ctors/testlib.cc new file mode 100644 index 000000000..827a457a5 --- /dev/null +++ b/base-linux/src/test/lx_hybrid_ctors/testlib.cc @@ -0,0 +1,24 @@ +/* + * \brief Test if global static constructors in host shared libs get called + * \author Christian Prochaska + * \date 2011-11-24 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +struct Testlib_testclass +{ + Testlib_testclass() + { + printf("[init -> test-lx_hybrid_ctors] Global static constructor of host library called.\n"); + } +}; + +Testlib_testclass testlib_testclass; diff --git a/base-linux/src/test/lx_hybrid_exception/main.cc b/base-linux/src/test/lx_hybrid_exception/main.cc new file mode 100644 index 000000000..a79f7e0b3 --- /dev/null +++ b/base-linux/src/test/lx_hybrid_exception/main.cc @@ -0,0 +1,37 @@ +/* + * \brief Test if the exception mechanism works in hybrid applications + * \author Christian Prochaska + * \date 2011-11-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + +class Test_exception { }; + +/** + * Main program + */ +int main(int, char **) +{ + printf("--- lx_hybrid exception test ---\n"); + + try { + printf("Throwing Test_exception\n"); + throw Test_exception(); + } catch(Test_exception) { + printf("Caught Test_exception\n"); + } + + printf("--- returning from main ---\n"); + + return 0; +} diff --git a/base-linux/src/test/lx_hybrid_exception/target.mk b/base-linux/src/test/lx_hybrid_exception/target.mk new file mode 100644 index 000000000..7a7242111 --- /dev/null +++ b/base-linux/src/test/lx_hybrid_exception/target.mk @@ -0,0 +1,3 @@ +TARGET = test-lx_hybrid_exception +SRC_CC = main.cc +LIBS = env lx_hybrid diff --git a/base-linux/src/test/sub_rm/config.h b/base-linux/src/test/sub_rm/config.h new file mode 100644 index 000000000..ec66d4fdb --- /dev/null +++ b/base-linux/src/test/sub_rm/config.h @@ -0,0 +1,22 @@ +/* + * \brief Linux-specific policy for sub_rm test + * \author Norman Feske + * \date 2011-11-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* + * The Linux implementation of the RM service does not support attaching + * the same sub RM session twice. This configuration enables the respective + * error-handling test. + */ +enum { attach_twice_forbidden = true }; + +enum { support_attach_sub_any = false }; + diff --git a/base-mb/README b/base-mb/README new file mode 100644 index 000000000..730ff1f63 --- /dev/null +++ b/base-mb/README @@ -0,0 +1,11 @@ +This repository contains the port of Genode for Xilinx MicroBlaze-based +platforms. It is based on an custom microkernel implementation, which is also +part of this repository. To get an overview on the this platform and the +underlying microkernel please refer to the introduction located at: + +! /base-mb/doc/microblaze.txt + +To get a quick overview about how to work with this platform, you may read the +getting-started guide located at: + +! /base-mb/doc/getting_started.txt diff --git a/base-mb/doc/getting_started.txt b/base-mb/doc/getting_started.txt new file mode 100644 index 000000000..efdac7643 --- /dev/null +++ b/base-mb/doc/getting_started.txt @@ -0,0 +1,249 @@ + + ========================================================= + Getting started with Genode on MicroBlaze based platforms + ========================================================= + + + Martin Stein + + +This file describes in a practical manner how to work with Genode on platforms +which are based on the Xilinx MicroBlaze. It approaches the following aspects: + +* Build Genode with an existing static scenario of programs which are interacting + on with each other and printing information about it to the serial port. +* Run this Genode scenario on Qemu and on the Xilinx Spartan 3A Starter Kit +* Implement basic support aspects for new MicroBlaze-based platforms + +If you're not familar with the Genode OS framework, you can read the online +documentation: + + [http://genode.org/documentation/] + +If you need further information about the Xilinx MicroBlaze, you can read an +introduction written by the Genode developers inside your Genode directory: + + 'base-mb/doc/microblaze.txt' + +It also covers general issues and limitations respecting the status quo of the +Genode porting for MicroBlaze-based platforms. To go in detail about the Xilinx +MicroBlaze, you may refer to the Xilinx documentation: + + [http://www.xilinx.com/tools/microblaze.htm] + +Prerequisites +============= + +The MicroBlaze tool chain +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To build Genode for MicroBlaze, it is recommended to use the following +GCC/binutils-compliant tools: + +* mb-g++ (GCC) 4.1.1 20060524 (Xilinx 11.2 Build EDK_LS2.2 20 Apr 2009 Xilinx + 11.2 Build EDK_LS2.2 23 Apr 2009) +* GNU ld version 2.16 Xilinx 11.2 Build EDK_LS2.2 23 Apr 2009 +* GNU assembler 2.16 Xilinx 11.2 Build EDK_LS2.2 23 Apr 2009 + +These tools come with the Xilinx Embedded Development Kit (EDK). + +Expect +~~~~~~ + +To run the given test scenarios on Genode, you have to install the Tcl-based +testing-tool Expect, for example using 'apt-get' on Debian-based Linux +distributions: + +! sudo apt-get install expect + +Qemu +~~~~ + +To run Genode's MicroBlaze port on Qemu, the following Qemu-version is recommended: + + QEMU emulator version 0.14.50, Copyright (c) 2003-2008 Fabrice Bellard + +You can get the source code of the latest version via GIT as follows: + +! git clone git://git.qemu.org/qemu.git + +For the scenarios described in here, you have to compile qemu via: + +! configure --target-list=microblaze-softmmu +! make + +Hardware +~~~~~~~~ + +The tutorial that runs Genode on hardware uses the Xilinx Spartan 3A Starter Kit +Revision D board with the FPGA 'xc3s700a', package 'fg484' on speed grade '-4'. +It has to be connected to your machine via USB and a serial port RS-232. + +Tutorial: Build and run Genode's MicroBlaze port +================================================ + +Initially go to your Genode directory and ensure that the value of the 'QEMU' variable +within 'tool/builddir/etc/build.conf.mb-s3a_starter_kit' conforms to the path +of your 'qemu-system-microblaze' command. Now build a build directory with the +following shell commands: + +! ./tool/create_builddir mb-s3a_starter_kit \ +! BUILD_DIR=build.mb-s3a_starter_kit \ + +On Qemu +~~~~~~~ + +Change to '/build.mb-s3a_starter_kit'. In this directory, +build and run the Genode scenario 'nested_init' for Qemu as follows: + +! make run/nested_init + +This instructs the Genode build system to act according to the run-script +'/base-mb/run/nested_init.run'. This script initiates the build of +the Genode's core, the program 'init', and a configuration that describes the +scenario init start. Then it constructs a bootable image of these 3 files and +finally starts Qemu to boot the image virtually. Genode then starts 2 nested +'init' programs, each 'init' instance prints some information about its +capabilities. + + +On Hardware +~~~~~~~~~~~ + +Ensure that the Xilinx Spartan 3A Starter Kit jumpers are set as described for +the board-intern demo. Connect the board via USB to your machine and turn it +on. Wait till the LED next to the USB connector on board lights up, then list +all connected USB devices: + +! lsusb + +This should print, among others, one line like this 'Bus XXX Device XXX: ID XXXX:0008 +Xilinx, Inc.' (any X is a wildcard for a number 0-9). Now connect the Serial port that +is labeled on board with 'J27' with your computer, this allows us to track debugging +output from Genode later. Go to '/build.mb-s3a_starter_kit'. +First we have to configure the Spartan 3A with an appropriate MicroBlaze SoC as follows: + +! make -C ../base-mb/platform/mb-s3a_starter_kit + +If it has finished successfully, we can build and run the 'nested_init' scenario by +typing the following command from within the build directory: + +! RUN_OPT="--target jtag" make run/nested_init + +After this, the build chain leaves an XMD command prompt to you, which is connected +to the SoC on the FPGA via JTAG, so you can steer it as you wish. Genode isn't started +already, you can now run a program like 'gtkterm' which intercepts the serial port that +Genode will print to. The parameters of the serial port according to 'gtkterm' are: + +* Speed = 9600 +* Parity = none +* Bits = 8 +* Stopbits = 1 +* Flowcontrol = none + +To start the execution of the 'nested_init' scenario type + +! run + +to the open XMD prompt. The serial port interception should show output similar +to that of the Qemu-run. You should avoid uploading multiple times to a once +configured platform, it can lead to memory inconsistency. In contrast when +configuring the FPGA in between the RAM gets reset. + +Other scenarios +~~~~~~~~~~~~~~~ + +You can also find a simple hello-world program at 'base-mb/src/test/hello'. +An appropriate 'run' script also exists and can be build from within a build +directory via: + +! RUN_OPT="--target " make run/hello + +Hints: How to add support for other MicroBlaze-based platforms +============================================================== + +The steps described in here don't claim to be complete. They solely should +cover the basic of aspects to be considered when implementing support for new +platforms and reflect main conventions Genode's MicroBlaze port relies to. + +New MicroBlaze-based platforms have to fulfill several considerations for now +to be compliant to the Genode port. The core expects: + +* A MicroBlaze SoC with software-loaded MMU that has 64 entries, + RAM accessibility and no instruction- and data- caches +* The RAM address space to be mapped to 0x90000000 +* The CPUs IRQ controller to be an XPS interrupt controller, + mapped to 0x81800000 +* An XPS Timer mapped to 0x83c00000 with IRQ 0 +* An XPS UART Lite mapped to 0x84000000 + +Basics +~~~~~~ + +Add a file 'base-mb/mk/spec-.mk' with the content + +! SPECS += +! STARTUP_LIB ?= startup +! PRG_LIBS += $(STARTUP_LIB) + +This file contains aspects to be integrated if 'PLATFORM' occurs in the +make-variable 'SPECS' during the build process. It also can add 'SPECS' by +itself to provide further details to the build system. For example, +the word-width of the CPU like '32bit'. Any other program or library +can depend on 'PLATFORM' later by adding it to its 'SPECS'. The second and third +lines specify a library that all userland-programs on Genode use to start on +'PLATFORM'. The denoted one is the default '/base-mb/lib/mk/startup.mk' +used by the currently supported platforms. + +You can influence the build-process for 'PLATFORM' furthermore by adding additional +lines to this file, for according documentation please refer to: + + [http://genode.org/documentation/] + +FPGA Configuration and support by the tool 'run' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To automate testing via the 'run' tool, you have to create a Makefile +'/base-mb/platform//Makefile' that provides a target +'upload'. This target should upload an ELF-image, whose absolute path is +given by the make argument 'IMAGE', to the according hardware. +The above mentioned Makefile should also provide by convention a target +'configure' which prepares the according hardware for the upload of +boot images. Typically it configures the FPGA with an appropriate +SoC. Therefore, whose source should also be located within +'/base-mb/platform//'. + +Finally you have to edit '/base-mb/run/env' to hint 'run' to +your platform. Add inside the function definition 'proc hardware { } {' +an additional: + +! if { [have_spec {}] } { +! set _hardware +! return $_hardware +! } + +'run' then calls 'upload' on '/base-mb/platform//Makefile' +and gives the boot image when 'run_genode_until' is called by the according +'run'-script. But first you should create an according build directory as described +next. + +Support for the tool 'create_builddir' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Add a file 'tool/builddir/etc/build.conf.' with at least the content + +! REPOSITORIES = base-mb +! QEMU = + +Where 'QEMU' denotes your Qemu command to emulate 'PLATFORM' for Genode +Now add a make-target '::' to 'tool/create_builddir' that should +describe additional things to do for your build directory. A good point to +start is to overwrite the default specifications the build process should take +into account when selecting and build targets and libraries. + +! @echo "SPECS = genode " > $(BUILD_DIR)/etc/specs.conf + +This adds the specifics for basic Genode settings, libraries and programs as +well as to the contents of your previously created +'base-mb/mk/spec-.mk'. + diff --git a/base-mb/doc/microblaze.txt b/base-mb/doc/microblaze.txt new file mode 100644 index 000000000..9f38e7c2e --- /dev/null +++ b/base-mb/doc/microblaze.txt @@ -0,0 +1,124 @@ + + ========================================================== + Introduction into the Genode porting for Xilinx MicroBlaze + ========================================================== + + + Norman Feske + Martin Stein + +This file gives an overview to the Genode porting for MicroBlaze-based +platforms. To get a quick introduction in how to build and run Genode on +such platforms, please refer to: + +! + +Xilinx MicroBlaze is a so-called softcore CPU, which is commonly used as part +of FPGA-based System-on-Chip designs. At Genode Labs, we are regularly using +this IP core, in particular for our Genode FPGA Graphics Project, which is a +GUI software stack and a set of IP cores for implementing fully-fledged +windowed GUIs on FPGAs: + +:Website of the Genode FPGA Graphics Project: + + [http://genode-labs.com/products/fpga-graphics] + +Ever since we first released the Genode FPGA project, we envisioned to combine +it with the Genode OS Framework. In Spring 2010, Martin Stein joined our team +at Genode Labs and accepted the challenge to bring the Genode OS Framework to +the realms of FPGA-based SoCs. Technically, this implies porting the framework +to the MicroBlaze CPU architecture. In contrast to most softcore CPUs such as +the popular Lattice Mico32, the MicroBlaze features a MMU, which is a fundamental +requirement for implementing a microkernel-based system. Architecturally-wise +MicroBlaze is a RISC CPU similar to MIPS. Many system parameters of the CPU +(caches, certain arithmetic and shift instructions) can be parametrized at +synthesizing time of the SoC. We found that the relatively simple architecture +of this CPU provides a perfect playground for pursuing some of our ideas about +kernel design that go beyond the scope of current microkernels. So instead of +adding MicroBlaze support into one of the existing microkernels already +supported by Genode, we went for a new kernel design. Deviating from the typical +microkernel, which is a self-sufficient program running in kernel mode that +executes user-level processes on top, our design regards the kernel as a part of +Genode's core. It is not a separate program but a library that implements the +glue between user-level core and the raw CPU. Specifically, it provides the +entrypoint for hardware exceptions, a thread scheduler, an IPC mechanism, and +functions to manipulate virtual address spaces (loading and flushing entries +from the CPU's software-loaded TLB). It does not manage any physical memory +resources or the relationship between processes. This is the job of core. +From the kernel-developer's point of view, the kernel part can be summarized as +follows: + +* The kernel provides user-level threads that are scheduled in a round-robin + fashion. +* Threads can communicate via synchronous IPC. +* There is a mechanism for blocking and waking up threads. This mechanism + can be used by Genode to implement locking as well as asynchronous + inter-process communication. +* There is a single kernel thread, which never blocks in the kernel code paths. + So the kernel acts as a state machine. Naturally, there is no concurrency in the + execution paths traversed in kernel mode, vastly simplifying these code parts. + However, all code paths are extremely short and bounded with regard to + execution time. Hence, we expect the interference with interrupt latencies + to be low. +* The IPC operation transfers payload between UTCBs only. Each thread has a + so-called user-level thread control block which is mapped transparently by + the kernel. Because of this mapping, user-level page faults cannot occur + during IPC transfers. +* There is no mapping database. Virtual address spaces are manipulated by + loading and flushing physical TLB entries. There is no caching of mappings + done in the kernel. All higher-level information about the interrelationship + of memory and processes is managed by the user-level core. +* Core runs in user mode, mapped 1-to-1 from the physical address space + except for its virtual thread-context area. +* The kernel paths are executed in physical address space (MicroBlaze). + Because both kernel code and user-level core code are observing the same + address-space layout, both worlds appear to run within a single address + space. +* User processes can use the entire virtual address space (4G) except for a + helper page for invoking syscalls and a page containing atomic operations. + There is no reservation used for the kernel. +* The MicroBlaze architecture lacks an atomic compare-and-swap instruction. On + user-level, this functionality is emulated via delayed preemption. A kernel- + provided page holds the sequence of operations to be executed atomically and + prevents (actually delays) the preemption of a thread that is currently + executing instructions at that page. +* The MicroBlaze MMU supports several different page sizes (1K up to 16MB). + Genode fully supports this feature for page sizes >= 4K. This way, the TLB + footprint can be minimized by choosing sensible alignments of memory + objects. + +Current state +============= + +The MicroBlaze platform support resides in the 'base-mb' repository. At the +current stage, core is able to successfully start multiple nested instances of +the init process. Most of the critical kernel functionality is working. This +includes inter-process communication, address-space creation, multi-threading, +thread synchronization, page-fault handling, and TLB eviction. + +The nested init scenario runs on Qemu, emulating the Petalogix Spartan 3A +DSP1800 design, as well as on real hardware, tested with the Xilinx Spartan +3A Starter Kit configured with an appropriate Microblaze SoC. + +This simple scenario already illustrates the vast advantage of +using different page sizes supported by the MicroBlaze CPU. If using +4KB pages only, a scenario with three nested init processes produces more than +300.000 page faults. There is an extremely high pressure on the TLB, which +only contains 64 entries. Those entries are constantly evicted so that +threshing effects are likely to occur. By making use of flexible page +sizes (4K, 16K, 64K, 256K, 1M, 4M, 16M), the number of page faults gets +slashed to only 1.800, speeding up the boot time by factor 10. + +On hardware the capability remains to increase execution speed significantly +by turning on instruction- and data-caches. However this feature has not been +tested for now. + +The kernel provides, beyond the requirements of the nested init scenario, +allocation, handling and deallocation of IRQs to the userland to enable +core to offer IRQ and IO Memory session services. This allows +custom device-driver implementations within the userland. + +Currently, there is no restriction of IPC communication rights. Threads are +addressed using their global thread IDs (in fact, using their respective +indices in the KTCB array). For the future, we are planning to add +capabilty-based delegation of communication rights. diff --git a/base-mb/etc/specs.conf b/base-mb/etc/specs.conf new file mode 100755 index 000000000..a3b84ce6d --- /dev/null +++ b/base-mb/etc/specs.conf @@ -0,0 +1 @@ +SPECS ?= genode mb-s3a_starter_kit diff --git a/base-mb/etc/tools.conf b/base-mb/etc/tools.conf new file mode 100755 index 000000000..dc0d02af9 --- /dev/null +++ b/base-mb/etc/tools.conf @@ -0,0 +1,14 @@ + +# Microblaze toolchain command prefix +CROSS_DEV_PREFIX ?= mb- + +# GCC code optimization level +CC_OLEVEL = -O2 + +# Disable garbage collection of sections by LD because the MicroBlaze toolchain +# would produce corrupted code with this option enabled. +LD_OPT_GC_SECTIONS = + +# Microblaze toolchain doesn't support #pragma GCC diagnostic, +# so avoid correspondig warnings. +CC_WARN += -Wno-pragmas diff --git a/base-mb/include/base/ipc_msgbuf.h b/base-mb/include/base/ipc_msgbuf.h new file mode 100755 index 000000000..ce17cd31a --- /dev/null +++ b/base-mb/include/base/ipc_msgbuf.h @@ -0,0 +1,62 @@ +/* + * \brief Dummy IPC message buffer + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +namespace Genode { + + /** + * IPC message buffer layout + */ + class Msgbuf_base + { + protected: + + size_t _size; + char _msg_start[]; /* symbol marks start of message */ + + public: + + /* + * Begin of actual message buffer + */ + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; } + + /** + * Return address of message buffer + */ + inline void *addr() { return &_msg_start[0]; } + }; + + + /** + * Instance of IPC message buffer with specified buffer size + */ + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-mb/include/base/ipc_pager.h b/base-mb/include/base/ipc_pager.h new file mode 100755 index 000000000..ebb4c6b42 --- /dev/null +++ b/base-mb/include/base/ipc_pager.h @@ -0,0 +1,207 @@ +/* + * \brief Dummy pager support for Genode + * \author Norman Feske, + * Martin Stein + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* Kernel includes */ +#include +#include + + +namespace Genode { + + namespace Paging { + + typedef Kernel::Paging::Resolution Native_resolution; + + /** + * Used by Genode's IPC Pager and RM Session Component + */ + class Resolution : public Native_resolution{ + + public: + + typedef Kernel::Paging::Physical_page Physical_page; + typedef Kernel::Paging::Virtual_page Virtual_page; + + private: + + enum { + INVALID_SIZE = Physical_page::INVALID_SIZE, + NO_PROTECTION_ID = 0, + DEFAULT_SIZE_LOG2 = Kernel::DEFAULT_PAGE_SIZE_LOG2, + DEFAULT_WRITEABLE = true, + DEFAULT_EXECUTABLE = true + }; + + bool _valid; + + public: + + ::Genode::Native_page_size _native_size(unsigned const size_log2) + { + using namespace Kernel; + using namespace Kernel::Paging; + + Physical_page::size_t s; + return Physical_page::size_by_size_log2(s, size_log2) ? + Physical_page::INVALID_SIZE : s; + } + + Native_page_permission _native_permission(bool const writeable, + bool const executable) + { + typedef Kernel::Paging::Physical_page Physical_page; + + if (writeable){ + if (executable) return Physical_page::RWX; + else return Physical_page::RW;} + else{ + if (executable) return Physical_page::RX; + else return Physical_page::R;} + } + + Resolution(addr_t virtual_page_address, + addr_t physical_page_address, + bool write_combined, + unsigned size_log2 = DEFAULT_SIZE_LOG2, + bool writeable = DEFAULT_WRITEABLE) + : _valid(true) + { + virtual_page = Virtual_page(virtual_page_address, + NO_PROTECTION_ID); + + physical_page = Physical_page(physical_page_address, + _native_size(size_log2), + _native_permission(writeable, + DEFAULT_EXECUTABLE)); + } + + Resolution() : _valid(false) { } + + void prepare_map_operation() { } + + inline bool valid() { return _valid; } + }; + } + + typedef Paging::Resolution Mapping; + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + typedef Kernel::Paging::Request Request; + + Mapping _mapping; + Request _request; + + public: + + /** + * Constructor + */ + Ipc_pager() + : Native_capability(Genode::my_thread_id(), 0) + { + _request.source.tid = 0; + } + + /** + * Wait for a new fault received as short message IPC + */ + void wait_for_fault(); + + /** + * Reply current fault and wait for a new one + * + * Send short flex page and wait for next short-message (register) + * IPC -- pagefault + */ + void reply_and_wait_for_fault(); + + bool resolved(); + + /** + * Request instruction pointer of current fault + */ + addr_t fault_ip() { return _request.source.ip; } + + /** + * Request fault address of current page fault + */ + addr_t fault_addr() { return _request.virtual_page.address(); } + + /** + * Set parameters for next reply + */ + inline void set_reply_mapping(Mapping m) { _mapping=m; } + + /** + * Set destination for next reply + */ + inline void set_reply_dst(Native_capability pager_object) { } + + /** + * Answer call without sending a flex-page mapping + * + * This function is used to acknowledge local calls from one of + * core's region-manager sessions. + */ + inline void acknowledge_wakeup() + { + Kernel::thread_wake(_request.source.tid); + } + + /** + * Return thread ID of last faulter + */ + inline Native_thread_id last() const { return _request.source.tid; } + + /** + * Return badge for faulting thread + */ + inline unsigned long badge() const { return _request.source.tid; } + + /** + * Was last fault a write fault? + */ + bool is_write_fault() const + { + return _request.access==Kernel::Paging::Request::RW || + _request.access==Kernel::Paging::Request::RWX; + } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + /* + * Reflection of exceptions is not supported on this platform. + */ + return false; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-mb/include/base/native_types.h b/base-mb/include/base/native_types.h new file mode 100755 index 000000000..76c5a6180 --- /dev/null +++ b/base-mb/include/base/native_types.h @@ -0,0 +1,62 @@ +/* + * \brief Dummy definitions for native types used for compiling unit tests + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +#include + +namespace Genode { + + typedef Kernel::Thread_id Native_thread_id; + typedef Native_thread_id Native_thread; + + typedef Kernel::Protection_id Native_process_id; + + typedef Kernel::Utcb_unaligned Native_utcb; + typedef Kernel::Paging::Physical_page::Permissions Native_page_permission; + typedef Kernel::Paging::Physical_page::size_t Native_page_size; + + Native_thread_id my_thread_id(); + + class Native_capability + { + private: + + Native_thread_id _tid; + long _local_name; + + public: + + Native_capability() : _tid(0), _local_name(0) { } + + Native_capability(Native_thread_id tid, long local_name) + : _tid(tid), _local_name(local_name) { } + + bool valid() const { return _tid!=0; } + + int local_name() const { return _local_name; } + + int dst() const { return (int)_tid; } + + Native_thread_id tid() const { return _tid; } + }; + + typedef int Native_connection_state; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ + + + + diff --git a/base-mb/include/cpu/atomic.h b/base-mb/include/cpu/atomic.h new file mode 100755 index 000000000..a13806546 --- /dev/null +++ b/base-mb/include/cpu/atomic.h @@ -0,0 +1,71 @@ +/* + * \brief Atomic Userland operations for Microblaze + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CPU__ATOMIC_H_ +#define _INCLUDE__CPU__ATOMIC_H_ + +#include + +namespace Genode { + + extern void* const _atomic_cmpxchg; + + + /** + * Executes compare and exchange as atomic operation + * + * This function compares the value at dest with cmp_val. + * If both values are equal, dest is set to new_val. If + * both values are different, the value at dest remains + * unchanged. + * + * \return 1 if the value was successfully changed to new_val, + * 0 if cmp_val and the value at dest differ. + */ + inline int cmpxchg(volatile int *dest, + unsigned int cmp_val, + unsigned int new_val) + { + int result = 0; + unsigned int r15_buf = 0; + + /** + * r27-r30 are arguments/return-values + * for _atomic_cmpxchg in r31 kernel denotes if + * interrupt has occured while executing atomic code + */ + asm volatile ("lwi r30, %[dest] \n" + "lwi r29, %[cmp_val] \n" + "lwi r28, %[new_val] \n" + "lwi r27, %[dest_val] \n" + "or r31, r0, r0 \n" + "swi r15, %[r15_buf] \n" + "bralid r15, _atomic_cmpxchg \n" + "or r0, r0, r0 \n" + "lwi r15, %[r15_buf] \n" + "swi r28, %[result] " + : + [result] "=m" (result), + [r15_buf] "+m" (r15_buf), + [dest] "+m" (dest), + [cmp_val] "+m" (cmp_val), + [new_val] "+m" (new_val), + [dest_val] "+m" (*dest) + :: "r31", "r30", "r29", "r28", "r27", "memory"); + + return result; + } +} + + +#endif /* _INCLUDE__CPU__ATOMIC_H_ */ diff --git a/base-mb/include/cpu/config.h b/base-mb/include/cpu/config.h new file mode 100755 index 000000000..13c8cb7bd --- /dev/null +++ b/base-mb/include/cpu/config.h @@ -0,0 +1,116 @@ +/* + * \brief Configuration of underlying hardware + * \author Martin stein + * \date 07-05-2010 + */ + +/* + * Copyright (C) 07-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CPU__CONFIG_H_ +#define _INCLUDE__CPU__CONFIG_H_ + +#define ALWAYS_INLINE __attribute__((always_inline)) + +#define BITFIELD_ENUMS(name, bit_significancy_offset, bit_width) \ + name ## _LSH = bit_significancy_offset, \ + name ## _WID = bit_width, \ + name ## _MSK = ~((~0) << bit_width) << bit_significancy_offset, + +namespace Cpu { + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + + typedef uint8_t byte_t; + typedef uint32_t word_t; + + typedef unsigned long addr_t; + typedef __SIZE_TYPE__ size_t; + + enum { + BYTE_WIDTH_LOG2 = 3, + WORD_WIDTH_LOG2 = 5, + BYTE_WIDTH = 1 << BYTE_WIDTH_LOG2, + WORD_WIDTH = 1 << WORD_WIDTH_LOG2, + BYTE_SIZE = sizeof(byte_t), + WORD_SIZE = sizeof(word_t), + + _16B_SIZE_LOG2 = 1*WORD_SIZE, + _256B_SIZE_LOG2 = 2*WORD_SIZE, + _4KB_SIZE_LOG2 = 3*WORD_SIZE, + _64KB_SIZE_LOG2 = 4*WORD_SIZE, + _1MB_SIZE_LOG2 = 5*WORD_SIZE, + _16MB_SIZE_LOG2 = 6*WORD_SIZE, + _256MB_SIZE_LOG2 = 7*WORD_SIZE, + + _16B_SIZE = 1 << _16B_SIZE_LOG2, + _256B_SIZE = 1 << _256B_SIZE_LOG2, + _4KB_SIZE = 1 << _4KB_SIZE_LOG2, + _64KB_SIZE = 1 << _64KB_SIZE_LOG2, + _1MB_SIZE = 1 << _1MB_SIZE_LOG2, + _16MB_SIZE = 1 << _16MB_SIZE_LOG2, + _256MB_SIZE = 1 << _256MB_SIZE_LOG2, + }; + + enum { + RAM_BASE = 0x90000000, + RAM_SIZE = 0x06000000, + + XPS_INTC_BASE = 0x81800000, + + XPS_TIMER_0_BASE = 0x83c00000, + XPS_TIMER_0_IRQ = 0, + + XPS_ETHERNETLITE_BASE = 0x81000000, + XPS_ETHERNETLITE_IRQ = 1, + + XPS_UARTLITE_BASE = 0x84000000, + XPS_UARTLITE_IRQ = 3, + + XPS_TIMER_1_BASE = 0x70000000, + XPS_TIMER_1_IRQ = 4, + }; + + typedef uint8_t Irq_id; + typedef uint8_t Exception_id; + + enum { + FAST_SIMPLEX_LINK = 0, + UNALIGNED = 1, + ILLEGAL_OPCODE = 2, + INSTRUCTION_BUS = 3, + DATA_BUS = 4, + DIV_BY_ZERO_EXCEPTON = 5, + FPU = 6, + PRIVILEGED_INSTRUCTION = 7, + + INTERRUPT = 10, + EXTERNAL_NON_MASKABLE_BREAK = 11, + EXTERNAL_MASKABLE_BREAK = 12, + + DATA_STORAGE = 16, + INSTRUCTION_STORAGE = 17, + DATA_TLB_MISS = 18, + INSTRUCTION_TLB_MISS = 19, + + MIN_EXCEPTION_ID = 0, + MAX_EXCEPTION_ID = 19, + + INVALID_EXCEPTION_ID = 20 + }; + + enum { + MIN_IRQ_ID = 0, + MAX_IRQ_ID = 31, + + INVALID_IRQ_ID = 32, + }; +} + +#endif /* _INCLUDE__CPU__CONFIG_H_ */ diff --git a/base-mb/include/kernel/config.h b/base-mb/include/kernel/config.h new file mode 100755 index 000000000..25f04b243 --- /dev/null +++ b/base-mb/include/kernel/config.h @@ -0,0 +1,79 @@ +/* + * \brief Configuration of kernel features + * \author Martin stein + * \date 24-06-2010 + */ + +/* + * Copyright (C) 24-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__KERNEL__CONFIG_H_ +#define _INCLUDE__KERNEL__CONFIG_H_ + +#include + +namespace Kernel +{ + enum { + SCHEDULING_MS_INTERVAL = 10, + SCHEDULING_TIMER_BASE = Cpu::XPS_TIMER_0_BASE, + SCHEDULING_TIMER_IRQ = Cpu::XPS_TIMER_0_IRQ, + + DEFAULT_PAGE_SIZE_LOG2 = Cpu::_4KB_SIZE_LOG2, + }; + + typedef Cpu::uint8_t Thread_id; + typedef Cpu::uint8_t Protection_id; + + enum{ + MIN_THREAD_ID = 1, + MAX_THREAD_ID = 64, + + MIN_PROTECTION_ID = 1, + MAX_PROTECTION_ID = 64, + + INVALID_THREAD_ID = 0, + INVALID_PROTECTION_ID = 0 }; +} + +namespace Roottask +{ + enum { + MAIN_STACK_SIZE = 1024*1024*Cpu::WORD_SIZE, + MAIN_THREAD_ID = 2, + + PROTECTION_ID = 1, + }; +} + +namespace User +{ + enum { + UART_BASE = Cpu::XPS_UARTLITE_BASE, + UART_IRQ = Cpu::XPS_UARTLITE_IRQ, + + IO_MEM_BASE = 0x70000000, + IO_MEM_SIZE = 0x10000000, + + XPS_TIMER_0_BASE = 0x70000000, + XPS_TIMER_0_IRQ = 4, + + MIN_IRQ = 4, + MAX_IRQ = 31, + + MIN_PROTECTION_ID = Roottask::PROTECTION_ID+1, + MAX_PROTECTION_ID = Kernel::MAX_PROTECTION_ID, + + MIN_THREAD_ID = Roottask::MAIN_THREAD_ID+1, + MAX_THREAD_ID = Kernel::MAX_THREAD_ID, + + VADDR_BASE = 0 + 1*(1< +#include + +/** + * Inline assembly clobber lists for syscalls with no arguments + */ +#define SYSCALL_7_ASM_CLOBBER "r24", SYSCALL_6_ASM_CLOBBER +#define SYSCALL_6_ASM_CLOBBER "r25", SYSCALL_5_ASM_CLOBBER +#define SYSCALL_5_ASM_CLOBBER "r26", SYSCALL_4_ASM_CLOBBER +#define SYSCALL_4_ASM_CLOBBER "r27", SYSCALL_3_ASM_CLOBBER +#define SYSCALL_3_ASM_CLOBBER "r28", SYSCALL_2_ASM_CLOBBER +#define SYSCALL_2_ASM_CLOBBER "r29", SYSCALL_1_ASM_CLOBBER +#define SYSCALL_1_ASM_CLOBBER SYSCALL_0_ASM_CLOBBER +#define SYSCALL_0_ASM_CLOBBER "r31", "r30" + +/** + * Inline assembly list for write access during syscalls with no arguments + */ +#define SYSCALL_0_ASM_WRITE \ + [result] "=m" (result), \ + [r15_buf] "+m" (r15_buf), \ + [opcode] "+m" (opcode) + + +/** + * Inline assembly lists for write access during syscalls with arguments + */ +#define SYSCALL_1_ASM_WRITE [arg_0] "+m" (arg_0), SYSCALL_0_ASM_WRITE +#define SYSCALL_2_ASM_WRITE [arg_1] "+m" (arg_1), SYSCALL_1_ASM_WRITE +#define SYSCALL_3_ASM_WRITE [arg_2] "+m" (arg_2), SYSCALL_2_ASM_WRITE +#define SYSCALL_4_ASM_WRITE [arg_3] "+m" (arg_3), SYSCALL_3_ASM_WRITE +#define SYSCALL_5_ASM_WRITE [arg_4] "+m" (arg_4), SYSCALL_4_ASM_WRITE +#define SYSCALL_6_ASM_WRITE [arg_5] "+m" (arg_5), SYSCALL_5_ASM_WRITE +#define SYSCALL_7_ASM_WRITE [arg_6] "+m" (arg_6), SYSCALL_6_ASM_WRITE + +/** + * Inline assembly ops for syscalls with no arguments + * - r19-r31 are save when occuring in the clobber list + * r15 is a 'dedicated' register and so we have to save it manually + */ +#define SYSCALL_0_ASM_OPS \ + "lwi r31, %[opcode] \n" \ + "swi r15, %[r15_buf] \n" \ + "brki r15, 0x8 \n" \ + "or r0, r0, r0 \n" \ + "lwi r15, %[r15_buf] \n" \ + "swi r30, %[result] " + +/** + * Inline assembly ops for syscalls with arguments + */ +#define SYSCALL_1_ASM_OPS "lwi r30, %[arg_0]\n" SYSCALL_0_ASM_OPS +#define SYSCALL_2_ASM_OPS "lwi r29, %[arg_1]\n" SYSCALL_1_ASM_OPS +#define SYSCALL_3_ASM_OPS "lwi r28, %[arg_2]\n" SYSCALL_2_ASM_OPS +#define SYSCALL_4_ASM_OPS "lwi r27, %[arg_3]\n" SYSCALL_3_ASM_OPS +#define SYSCALL_5_ASM_OPS "lwi r26, %[arg_4]\n" SYSCALL_4_ASM_OPS +#define SYSCALL_6_ASM_OPS "lwi r25, %[arg_5]\n" SYSCALL_5_ASM_OPS +#define SYSCALL_7_ASM_OPS "lwi r24, %[arg_6]\n" SYSCALL_6_ASM_OPS + +/** + * Inline assembly lists for read access during syscalls with arguments + */ +#define SYSCALL_0_ASM_READ +#define SYSCALL_1_ASM_READ SYSCALL_0_ASM_READ +#define SYSCALL_2_ASM_READ SYSCALL_1_ASM_READ +#define SYSCALL_3_ASM_READ SYSCALL_2_ASM_READ +#define SYSCALL_4_ASM_READ SYSCALL_3_ASM_READ +#define SYSCALL_5_ASM_READ SYSCALL_4_ASM_READ +#define SYSCALL_6_ASM_READ SYSCALL_5_ASM_READ +#define SYSCALL_7_ASM_READ SYSCALL_6_ASM_READ + + +namespace Kernel { + + using namespace Cpu; + + typedef unsigned int Syscall_arg; + + /** + * Syscall with 1 Argument + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode); + + + /** + * Syscall with 2 Arguments + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode, + Syscall_arg arg_0); + + /** + * Syscall with 3 Arguments + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1); + + /** + * Syscall with 4 Arguments + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2); + + /** + * Syscall with 5 Arguments + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3); + + /** + * Syscall with 6 Arguments + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4); + + /** + * Syscall with 7 Arguments + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4, + Syscall_arg arg_5); + + /** + * Syscall with 8 Arguments + */ + ALWAYS_INLINE inline int syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4, + Syscall_arg arg_5, + Syscall_arg arg_6); + + /** + * Yield thread execution and coninue with next + */ + inline void thread_yield(); + + /** + * Block thread that calls this + */ + inline void thread_sleep(); + + /** + * Create and start threads + * + * \param tid ident that thread should get + * \param pid threads protection domain + * \param pager_id threads page fault handler thread + * \param utcb_p virtual address of utcb + * \param vip initial virtual ip + * \param vsp initial virtual sp + * \param param scheduling parameters, not used by now + * \return 0 if new thread was created + * n > 0 if any error has occured (errorcodes planned) + */ + inline int thread_create(Thread_id tid, + Protection_id pid, + Thread_id pager_tid, + Utcb* utcb_p, + Cpu::addr_t vip, + Cpu::addr_t vsp, + unsigned int params); + + /** + * Kill thread - only with root rights + * + * \param tid ident of thread + * \return 0 if thread is awake after syscall + * n > 0 if any error has occured (errorcodes planned) + */ + inline int thread_kill(Thread_id tid); + + /** + * Unblock denoted thread + * + * \param tid ident of thread thats blocked + * \detail works only if destination has same protection + * domain or caller has rootrights + * \return 0 if thread is awake after syscall + * n > 0 if any error has occured (errorcodes planned) + */ + inline int thread_wake(Thread_id tid); + + /** + * Re-set pager of another thread + * + * \param dst_tid thread whose pager shall be changed + * \param pager_tid ident of pager thread + * \detail works only if caller has rootrights + * \return 0 if new pager of thread is successfully set + * n > 0 if any error has occured (errorcodes planned) + */ + inline int thread_pager(Thread_id dst_tid, + Thread_id pager_tid); + + /** + * Reply last and wait for new ipc request + * + * \param msg_length length of reply message + * \return length of received message + */ + inline int ipc_serve(unsigned int reply_size); + + /** + * Send ipc request denoted in utcb to specific thread + * + * \param dest_id ident of destination thread + * \param msg_length number of request-message words + * \return number of reply-message words, or + * zero if request was not successfull + */ + inline int ipc_request(Thread_id dest_tid, unsigned int msg_size); + + /** + * Load pageresolution to memory managment unit + * + * \param p_addr physical page address + * \param v_addr virtual page address + * \param pid protection domain ident + * \param size size of page + * \param permissions permission flags for page + * \return 0 if thread is awake after syscall + * n > 0 if any error has occured (errorcodes planned) + */ + inline int tlb_load(Cpu::addr_t p_address, + Cpu::addr_t v_address, + Protection_id pid, + Paging::Physical_page::size_t size, + Paging::Physical_page::Permissions permissions); + + /** + * Flush page resolution area from tlb + * + * \param pid protection domain id + * \param start startaddress of area + * \param size_kbyte size of area in 1KB units + * \return 0 if new thread was created + * n > 0 if any error has occured (errorcodes planned) + */ + inline int tlb_flush(Protection_id pid, + Cpu::addr_t start, + unsigned size); + + /** + * Print char to serial ouput + * + * \param c char to print + */ + inline void print_char(char c); + + /** + * Print various informations about a specific thread + * \param i Unique ID of the thread, if it remains 0 take our own ID + */ + inline void print_info(Thread_id const & i = 0); + + /** + * Allocate an IRQ to the calling thread if the IRQ is + * not allocated yet to another thread + * + * \param i Unique ID of the IRQ + * \return 0 If the IRQ is allocated to this thread now + * n != 0 If the IRQ is not allocated to this thread already + * (code of the error that has occured) + */ + inline int irq_allocate(Irq_id i); + + /** + * Free an IRQ from allocation if it is allocated by the + * calling thread + * + * \param i Unique ID of the IRQ + * \return 0 If the IRQ is free now + * n != 0 If the IRQ is allocated already + * (code of the error that has occured) + */ + inline int irq_free(Irq_id i); + + /** + * Sleep till the 'Irq_message'-queue of this thread is not + * empty. For any IRQ that is allocated by this thread and occures + * between the kernel-entrance inside 'irq_wait' and the next time this + * thread wakes up, an 'Irq_message' with metadata about the according + * IRQ is added to the threads 'Irq_message'-queue. + * When returning from 'irq_wait' the first message from the threads + * 'Irq_message'-queue is dequeued and written to the threads UTCB-base. + */ + inline void irq_wait(); +} + + +void Kernel::print_info(Thread_id const & i) +{ + syscall(PRINT_INFO, (Syscall_arg) i); +} + + +void Kernel::irq_wait() { syscall(IRQ_WAIT); } + + +int Kernel::irq_allocate(Irq_id i) +{ + return syscall(IRQ_ALLOCATE, (Syscall_arg) i); +} + + +int Kernel::irq_free(Irq_id i) { return syscall(IRQ_FREE, (Syscall_arg) i); } + + +void Kernel::thread_yield() { syscall(THREAD_YIELD); } + + +void Kernel::thread_sleep() { syscall(THREAD_SLEEP); } + + +int Kernel::thread_create(Thread_id tid, + Protection_id pid, + Thread_id pager_tid, + Utcb* utcb_p, + Cpu::addr_t vip, + Cpu::addr_t vsp, + unsigned int params) +{ + return syscall(THREAD_CREATE, + (Syscall_arg) tid, + (Syscall_arg) pid, + (Syscall_arg) pager_tid, + (Syscall_arg) utcb_p, + (Syscall_arg) vip, + (Syscall_arg) vsp, + (Syscall_arg) params); +} + + +int Kernel::thread_kill(Thread_id tid) +{ + return syscall(THREAD_KILL, (Syscall_arg) tid); +} + + +int Kernel::thread_wake(Thread_id tid) +{ + return syscall(THREAD_WAKE, (Syscall_arg) tid); +} + + +int Kernel::thread_pager(Thread_id dst_tid, + Thread_id pager_tid) +{ + return syscall( + THREAD_PAGER, + (Syscall_arg) dst_tid, + (Syscall_arg) pager_tid); +} + + +int Kernel::ipc_serve(unsigned int reply_size) +{ + return syscall(IPC_SERVE, (Syscall_arg) reply_size); +} + + +int Kernel::ipc_request(Thread_id dest_tid, + unsigned int msg_size) +{ + return syscall( + IPC_REQUEST, + (Syscall_arg) dest_tid, + (Syscall_arg) msg_size); +} + + +int Kernel::tlb_load(Cpu::addr_t p_address, + Cpu::addr_t v_address, + Protection_id pid, + Paging::Physical_page::size_t size, + Paging::Physical_page::Permissions permissions) +{ + return syscall( + TLB_LOAD, + (Syscall_arg) p_address, + (Syscall_arg) v_address, + (Syscall_arg) pid, + (Syscall_arg) size, + (Syscall_arg) permissions); +} + + +int Kernel::tlb_flush(Protection_id pid, + Cpu::addr_t start, + unsigned size) +{ + return syscall( + TLB_FLUSH, + (Syscall_arg) pid, + (Syscall_arg) start, + (Syscall_arg) size); +} + + +void Kernel::print_char(char c) { syscall(PRINT_CHAR, (Syscall_arg) c); } + + +int Kernel::syscall(Syscall_id opcode) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_0_ASM_OPS + : SYSCALL_0_ASM_WRITE + : SYSCALL_0_ASM_READ + : SYSCALL_0_ASM_CLOBBER); + + return result; +} + + +int Kernel::syscall(Syscall_id opcode, Syscall_arg arg_0) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_1_ASM_OPS + : SYSCALL_1_ASM_WRITE + : SYSCALL_1_ASM_READ + : SYSCALL_1_ASM_CLOBBER); + + return result; +} + + +int Kernel::syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_2_ASM_OPS + : SYSCALL_2_ASM_WRITE + : SYSCALL_2_ASM_READ + : SYSCALL_2_ASM_CLOBBER); + + return result; +} + + +int Kernel::syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_3_ASM_OPS + : SYSCALL_3_ASM_WRITE + : SYSCALL_3_ASM_READ + : SYSCALL_3_ASM_CLOBBER); + + return result; +} + + +int Kernel::syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_4_ASM_OPS + : SYSCALL_4_ASM_WRITE + : SYSCALL_4_ASM_READ + : SYSCALL_4_ASM_CLOBBER); + + return result; +} + + +int Kernel::syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_5_ASM_OPS + : SYSCALL_5_ASM_WRITE + : SYSCALL_5_ASM_READ + : SYSCALL_5_ASM_CLOBBER); + + return result; +} + + +int Kernel::syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4, + Syscall_arg arg_5) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_6_ASM_OPS + : SYSCALL_6_ASM_WRITE + : SYSCALL_6_ASM_READ + : SYSCALL_6_ASM_CLOBBER); + + return result; +} + + +int Kernel::syscall(Syscall_id opcode, + Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4, + Syscall_arg arg_5, + Syscall_arg arg_6) +{ + int result; + unsigned int r15_buf; + + asm volatile(SYSCALL_7_ASM_OPS + : SYSCALL_7_ASM_WRITE + : SYSCALL_7_ASM_READ + : SYSCALL_7_ASM_CLOBBER); + + return result; +} + + +#endif /* _INCLUDE__KERNEL__SYSCALLS_H_ */ diff --git a/base-mb/include/kernel/types.h b/base-mb/include/kernel/types.h new file mode 100755 index 000000000..1dfaca10a --- /dev/null +++ b/base-mb/include/kernel/types.h @@ -0,0 +1,329 @@ +/* + * \brief Kernel specific data types + * \author Martin stein + * \date 2010-10-01 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__KERNEL__TYPES_H_ +#define _INCLUDE__KERNEL__TYPES_H_ + +#include +#include +#include + +namespace Kernel { + + using namespace Cpu; + + enum {THREAD_CREATE_PARAMS_ROOTRIGHT_LSHIFT=0}; + + + struct Utcb_unaligned + { + enum { + ALIGNMENT_LOG2 = 0, + SIZE_LOG2 = Cpu::_4KB_SIZE_LOG2, + }; + + union + { + volatile Cpu::byte_t byte[1<= sizeof(size_by_size_log2) / sizeof(size_by_size_log2[0])) + return -2; + + if (size_by_size_log2[size_log2] == INVALID_SIZE) + return -3; + + s = size_by_size_log2[(unsigned)size_log2]; + return 0; +} + + +Cpu::size_t Kernel::Utcb_unaligned::size() +{ + return (Cpu::size_t)1< + +namespace Xilinx { + + /** + * Driver for the Xilinx LogiCORE IP XPS Interrupt Controller 2.01 + */ + class Xps_intc + { + public: + + typedef Cpu::uint32_t Register; + typedef Cpu::uint8_t Irq; + + enum { + REGISTER_WIDTH = sizeof(Register)*Cpu::BYTE_WIDTH, + MIN_IRQ = Cpu::MIN_IRQ_ID, + MAX_IRQ = Cpu::MAX_IRQ_ID, + INVALID_IRQ = Cpu::INVALID_IRQ_ID, + }; + + /** + * Constructor argument + */ + struct Constr_arg + { + Cpu::addr_t base; + + Constr_arg(Cpu::addr_t const & b) : base(b) { } + }; + + /** + * Probe if IRQ ID is valid at this controller + */ + inline bool valid(Irq const & i); + + /** + * Enable propagation of all IRQ inputs + */ + inline void unmask(); + + /** + * Enable propagation of all IRQ inputs + */ + inline void unmask(Irq const & i); + + /** + * Disable propagation of all IRQ inputs + * (anyhow the occurency of IRQ's gets noticed in ISR) + */ + inline void mask(); + + /** + * Disable propagation of an IRQ input + * (anyhow the occurency of the IRQ's gets noticed in ISR) + */ + inline void mask(Irq const & i); + + /** + * Constructor + * All IRQ's are masked initially + */ + inline Xps_intc(Constr_arg const & ca); + + /** + * Destructor + * All IRQ's are left masked + */ + inline ~Xps_intc(); + + /** + * Get the pending IRQ with + * the highest priority (that one with the lowest IRQ ID) + */ + inline Irq next_irq(); + + /** + * Release IRQ input so it can occure again + * (in general IRQ source gets acknowledged thereby) + */ + inline void release(Irq const & i); + + /** + * Probe if IRQ is pending (unmasked and active) + */ + inline bool pending(Irq const & i); + + private: + + /** + * Register mapping offsets relative to the device base address + */ + enum { + RISR_OFFSET = 0 * Cpu::WORD_SIZE, + RIPR_OFFSET = 1 * Cpu::WORD_SIZE, + RIER_OFFSET = 2 * Cpu::WORD_SIZE, + RIAR_OFFSET = 3 * Cpu::WORD_SIZE, + RSIE_OFFSET = 4 * Cpu::WORD_SIZE, + RCIE_OFFSET = 5 * Cpu::WORD_SIZE, + RIVR_OFFSET = 6 * Cpu::WORD_SIZE, + RMER_OFFSET = 7 * Cpu::WORD_SIZE, + RMAX_OFFSET = 8 * Cpu::WORD_SIZE, + + RMER_ME_LSHIFT = 0, + RMER_HIE_LSHIFT = 1 + }; + + /** + * Short register description (no optional registers) + * + * ISR IRQ status register, a bit in here is '1' as long as the + * according IRQ-input is '1', IRQ/bit correlation: [MAX_IRQ,...,1,0] + * IER IRQ unmask register, as long as a bit is '1' in IER the controller + * output equals the according bit in ISR as long as MER[ME] is '1' + * IAR IRQ acknowledge register, writing a '1' to a bit in IAR writes + * '0' to the according bit in ISR and '0' to bit in IAR + * SIE Set IRQ unmask register, writing a '1' to a bit in SIE sets the + * according bit in IER to '1' and writes '0' to the bit in SIE + * CIE Clear IRQ unmask register, writing a '1' to a bit in SIE sets the + * according bit in IER to '0' and writes '0' to the bit in CIE + * MER Master unmask register, structure: [0,...,0,HIE,ME], controller + * output is '0' as long as ME is '0', HIE is '0' initally so + * software IRQ mode is active writing '1' to HIE switches to + * hardware IRQ mode and masks writing to HIE + */ + volatile Register* const _risr; + volatile Register* const _rier; + volatile Register* const _riar; + volatile Register* const _rsie; + volatile Register* const _rcie; + volatile Register* const _rmer; + }; +} + + +void Xilinx::Xps_intc::unmask() { *_rsie = ~0; } + + +void Xilinx::Xps_intc::unmask(Irq const & i) +{ + if (!valid(i)) { return; } + *_rsie = 1 << i; +} + + +void Xilinx::Xps_intc::mask() { *_rcie = ~0; } + + +void Xilinx::Xps_intc::mask(Irq const & i) +{ + if (!valid(i)) { return; } + *_rcie = 1 << i; +} + + +bool Xilinx::Xps_intc::pending(Irq const & i) +{ + if (!valid(i)) { return false; } + Register const pending = *_risr & *_rier; + return pending & (1 << i); +} + + +bool Xilinx::Xps_intc::valid(Irq const & i) +{ + return !(i == INVALID_IRQ || i > MAX_IRQ); +} + + +Xilinx::Xps_intc::Xps_intc(Constr_arg const & ca) : + _risr((Register*)(ca.base + RISR_OFFSET)), + _rier((Register*)(ca.base + RIER_OFFSET)), + _riar((Register*)(ca.base + RIAR_OFFSET)), + _rsie((Register*)(ca.base + RSIE_OFFSET)), + _rcie((Register*)(ca.base + RCIE_OFFSET)), + _rmer((Register*)(ca.base + RMER_OFFSET)) +{ + *_rmer = 1 << RMER_HIE_LSHIFT | 1 << RMER_ME_LSHIFT; + mask(); +} + + +Xilinx::Xps_intc::~Xps_intc() +{ + mask(); +} + + +Xilinx::Xps_intc::Irq Xilinx::Xps_intc::next_irq() +{ + Register const pending = *_risr & *_rier; + Register bit_mask = 1; + + for (unsigned int i=0; i + +namespace Xilinx { + + /** + * Driver for the Xilinx LogiCORE XPS Timer/Counter IP 1.02 + */ + class Xps_timer + { + public: + + /** + * CPU dependencies + */ + typedef Cpu::word_t word_t; + typedef Cpu::addr_t addr_t; + typedef Cpu::size_t size_t; + typedef Cpu::uint32_t uint32_t; + + /** + * MMIO register + */ + typedef uint32_t Register; + + /** + * Constructor, resets timer, overwrites timer value with '0' + */ + inline Xps_timer(addr_t const &base); + + /** + * Destructor, resets timer, overwrites timer value with '0' + */ + inline ~Xps_timer(); + + /** + * Overwrite timer value with X>0, count downwards to '0', set the + * IRQ output to '1' for one cycle and simultaneously start counting + * downwards again from X, and so on ... + * + * \param value the value X + */ + inline void run_periodic(unsigned int const &value); + + /** + * Overwrite timer value with X>0, count downwards to '0', set the + * IRQ output to '1' for one cycle and simultaneously start counting + * downwards from max_value() to '0', and so on ... + * + * \param value the value X + */ + inline void run_circulating(unsigned int const &value); + + /** + * Overwrite timer value with X>0, count downwards to '0', set the + * IRQ output to '1' for one cycle, timer value remains '0' + * + * \param value the value X + */ + inline void run_oneshot(unsigned int const &value); + + /** + * Prepare a 'run_oneshot()'-like run that shall be triggered with + * simple means. Useful for starting the timer out of assembly-code. + * + * \param value native time-value used to assess the delay + * of the timer IRQ as of the triggering + * \param start_val at this address the start value gets deposited + * \param start_reg at this address an address X gets deposited + * writing the start value to X later starts the + * timer as prepared + */ + inline void prepare_oneshot(unsigned int const & value, + volatile Register * & start_reg, + Register & start_val); + + /** + * Current timer value + */ + inline unsigned int value(); + + /** + * Return the timers current value and determine if the timer has hit '0' + * before the returned value and after the last time we started it or called + * 'period_value' on it. Called during non-periodic runs 'rolled_over' + * becomes 'true' if value is '0' and 'false' otherwise + * + * Enable exclusive access only to this function to ensure correct behavior! + * This function delays the timer about the duration of a few cpu cycles! + */ + inline unsigned int period_value(bool * const & rolled_over); + + /** + * Size of the MMIO provided by the timer device + */ + static inline size_t size(); + + /** + * Maximum timer value + */ + static inline unsigned int max_value(); + + /** + * Converting a native time value to milliseconds + */ + static inline unsigned int native_to_msec(unsigned int const &v); + + /** + * Converting milliseconds to a native time value + */ + static inline unsigned int msec_to_native(unsigned int const &v); + + /** + * Converting a native time value to microseconds + */ + static inline unsigned int native_to_usec(unsigned int const &v); + + /** + * Converting microseconds to a native time value + */ + static inline unsigned int usec_to_native(unsigned int const &v); + + private: + + /** + * General constraints + */ + enum { + WORD_SIZE = sizeof(word_t), + BYTE_WIDTH = Cpu::BYTE_WIDTH, + FREQUENCY_PER_US = 62, + }; + + /** + * Registers + */ + enum { + /* Control/status register */ + RTCSR0_OFFSET = 0*WORD_SIZE, + + /* Load register, written to RTCR when RTCSR[LOAD]='1' */ + RTLR0_OFFSET = 1*WORD_SIZE, + + /* On timer/counter register the counting is done */ + RTCR0_OFFSET = 2*WORD_SIZE, + + /* Respectively for the second timer/counter module */ + RTCSR1_OFFSET = 4*WORD_SIZE, + RTLR1_OFFSET = 5*WORD_SIZE, + RTCR1_OFFSET = 6*WORD_SIZE, + + MMIO_SIZE = 8*WORD_SIZE, + + /* r/w '0': generate timer mode + * r/w '1': capture timer mode */ + RTCSR_MDT_LSHIFT = 0, + + /* r/w '0': count upward mode + * r/w '1': count downward mode */ + RTCSR_UDT_LSHIFT = 1, + + /* r/w '0': external generate signal disabled mode + * r/w '1': external generate signal enabled mode */ + RTCSR_GENT_LSHIFT = 2, + + /* r/w '0': external capture trigger disabled mode + * r/w '1': external capture trigger enabled mode */ + RTCSR_CAPT_LSHIFT = 3, + + /* r/w '0': hold values mode + * r/w '0': auto reload (generate timer) / overwrite (capture timer) mode */ + RTCSR_ARHT_LSHIFT = 4, + + /* w/r '0': disable loading mode + * w/r '1': loading timer mode (RTCR=RTLR) */ + RTCSR_LOAD_LSHIFT = 5, + + /* w/r '0': throw no IRQ mode (doesn't affect RTCSR[TINT]) + * w/r '1': throw IRQ on 0-1-edge at RTCSR[TINT] mode */ + RTCSR_ENIT_LSHIFT = 6, + + /* w/r '0': don't count (RTCR remains constant) + * w/r '1': count on RTCR */ + RTCSR_ENT_LSHIFT = 7, + + /* r '0': no IRQ has occured + * r '1': IRQ has occured + * w '0': no effect + * w '1': RTCSR[TINT]=0 */ + RTCSR_TINT_LSHIFT = 8, + + /* r/w '0': pulse width modulation disabled mode + * r/w '1': pulse width modulation enabled mode */ + RTCSR_PWM_LSHIFT = 9, + + /* w/r '0': nothing + * w/r '1': RTCSR[ENT]='1' for all timer/counter modules */ + RTCSR_ENALL_LSHIFT = 10, + }; + + /** + * Controls for RTCSR + */ + enum { + RUN_ONCE = 0 + | 0 << RTCSR_MDT_LSHIFT + | 1 << RTCSR_UDT_LSHIFT + | 0 << RTCSR_CAPT_LSHIFT + | 0 << RTCSR_GENT_LSHIFT + | 0 << RTCSR_ARHT_LSHIFT + | 0 << RTCSR_LOAD_LSHIFT + | 1 << RTCSR_ENIT_LSHIFT + | 1 << RTCSR_ENT_LSHIFT + | 1 << RTCSR_TINT_LSHIFT + | 0 << RTCSR_PWM_LSHIFT + | 0 << RTCSR_ENALL_LSHIFT + , + + STOP_N_LOAD = 0 + | 0 << RTCSR_MDT_LSHIFT + | 1 << RTCSR_UDT_LSHIFT + | 0 << RTCSR_CAPT_LSHIFT + | 0 << RTCSR_GENT_LSHIFT + | 0 << RTCSR_ARHT_LSHIFT + | 1 << RTCSR_LOAD_LSHIFT + | 0 << RTCSR_ENIT_LSHIFT + | 0 << RTCSR_ENT_LSHIFT + | 0 << RTCSR_TINT_LSHIFT + | 0 << RTCSR_PWM_LSHIFT + | 0 << RTCSR_ENALL_LSHIFT + , + + RUN_PERIODIC = RUN_ONCE + | 1 << RTCSR_ARHT_LSHIFT + , + + STOP_N_RESET = STOP_N_LOAD + | 1 << RTCSR_TINT_LSHIFT + , + }; + + /** + * Absolute register addresses + */ + volatile Register *const _rtcsr0; + volatile Register *const _rtlr0; + volatile Register *const _rtcr0; + volatile Register *const _rtcsr1; + volatile Register *const _rtlr1; + volatile Register *const _rtcr1; + }; +} + + +Xilinx::Xps_timer::Xps_timer(addr_t const &base) +: + _rtcsr0((Register *)(base + RTCSR0_OFFSET)), + _rtlr0((Register *)(base + RTLR0_OFFSET)), + _rtcr0((Register *)(base + RTCR0_OFFSET)), + _rtcsr1((Register *)(base + RTCSR1_OFFSET)), + _rtlr1((Register *)(base + RTLR1_OFFSET)), + _rtcr1((Register *)(base + RTCR1_OFFSET)) +{ + *_rtcsr0 = STOP_N_RESET; + *_rtcsr1 = STOP_N_RESET; + *_rtlr0 = 0; +} + + +Xilinx::Xps_timer::size_t Xilinx::Xps_timer::size() +{ + return (size_t)MMIO_SIZE; +} + + +unsigned int Xilinx::Xps_timer::max_value() { return ((unsigned int)~0); } + + +void Xilinx::Xps_timer::prepare_oneshot(unsigned int const & value, + volatile Register * & start_reg, + Register & start_val) +{ + *_rtcsr0 = STOP_N_LOAD; + *_rtlr0 = value; + + start_reg = _rtcsr0; + start_val = RUN_ONCE; +} + + +void Xilinx::Xps_timer::run_circulating(unsigned int const &value) +{ + *_rtcsr0 = STOP_N_LOAD; + *_rtlr0 = value; + *_rtcsr0 = RUN_PERIODIC; + *_rtlr0 = max_value(); +} + + +void Xilinx::Xps_timer::run_periodic(unsigned int const &value) +{ + *_rtcsr0 = STOP_N_LOAD; + *_rtlr0 = value; + *_rtcsr0 = RUN_PERIODIC; +} + + +void Xilinx::Xps_timer::run_oneshot(unsigned int const &value) +{ + *_rtcsr0 = STOP_N_LOAD; + *_rtlr0 = value; + *_rtcsr0 = RUN_ONCE; +} + + +unsigned int Xilinx::Xps_timer::value() { return *_rtcr0; } + + +unsigned int Xilinx::Xps_timer::period_value(bool * const &rolled_over) +{ + if(!(*_rtcsr0 & (1 << RTCSR_ARHT_LSHIFT))){ + /* this is no periodic run */ + unsigned int const v = *_rtcr0; + *rolled_over = !(v); + return value(); + } + + /* 2 measurements are necessary to ensure that + * 'rolled_over' and the returned value are fit together + * because we can not halt the timer or read both simulanously */ + unsigned int const v1 = *_rtcr0; + *rolled_over = (bool)(*_rtcsr0 & (1 << RTCSR_TINT_LSHIFT)); + unsigned int const v2 = *_rtcr0; + + if(*rolled_over) { + /* v2 must be a value the timer had after rolling over, so restart + * the timer with the current value but RTCSR[TINT] reset */ + unsigned int const initial_rtlr = *_rtlr0; + unsigned int const restart_n_reset = *_rtcsr0 | (1 << RTCSR_TINT_LSHIFT); + *_rtlr0 = *_rtcr0; // timer gets delayed about the + *_rtcsr0 = restart_n_reset; // duration of these two operations + *_rtlr0 = initial_rtlr; + return v2; + } + + /* v1 must be a value that the timer had before rolling + * over, so we don't have to reset the "rolled over" status even + * if the timer has rolled over till now */ + return v1; +} + + +Xilinx::Xps_timer::~Xps_timer() +{ + *_rtcsr0 = STOP_N_RESET; + *_rtcsr1 = STOP_N_RESET; +} + + +unsigned int Xilinx::Xps_timer::native_to_msec(unsigned int const &v) +{ + return 1000*native_to_usec(v); +} + + +unsigned int Xilinx::Xps_timer::msec_to_native(unsigned int const &v) +{ + return 1000*usec_to_native(v); +} + + +unsigned int Xilinx::Xps_timer::native_to_usec(unsigned int const &v) +{ + return v/FREQUENCY_PER_US; +} + + +unsigned int Xilinx::Xps_timer::usec_to_native(unsigned int const &v) +{ + return v*FREQUENCY_PER_US; +} + + +#endif /* _INCLUDE__DEVICES__XILINX_XPS_TIMER_H_ */ diff --git a/base-mb/include/xilinx/xps_uartl.h b/base-mb/include/xilinx/xps_uartl.h new file mode 100644 index 000000000..57e531ee0 --- /dev/null +++ b/base-mb/include/xilinx/xps_uartl.h @@ -0,0 +1,111 @@ +/* + * \brief Driver for the Xilinx LogiCORE IP XPS UART Lite 1.01a + * \author Martin stein + * \date 2011-05-06 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__DEVICES__XILINX_XPS_UARTL_H_ +#define _INCLUDE__DEVICES__XILINX_XPS_UARTL_H_ + +#include + +namespace Xilinx { + + /** + * Driver for the Xilinx LogiCORE IP XPS UART Lite 1.01a + */ + class Xps_uartl + { + public: + + /** + * Constructor + */ + Xps_uartl(Cpu::addr_t const & base); + + /** + * Send one ASCII char over the UART interface + */ + inline void send(char const & c); + + private: + + /** + * Relative MMIO structure + */ + + typedef Cpu::uint32_t Register; + + enum { + RX_FIFO_OFF = 0 * Cpu::WORD_SIZE, + TX_FIFO_OFF = 1 * Cpu::WORD_SIZE, + STAT_REG_OFF = 2 * Cpu::WORD_SIZE, + CTRL_REG_OFF = 3 * Cpu::WORD_SIZE, + }; + + struct Rx_fifo { + enum { + BITFIELD_ENUMS(RX_DATA, 0, 8) + }; + }; + + struct Tx_fifo { + enum { + BITFIELD_ENUMS(TX_DATA, 0, 8) + }; + }; + + struct Ctrl_reg { + enum { + BITFIELD_ENUMS(RST_TX_FIFO, 0, 1) + BITFIELD_ENUMS(RST_RX_FIFO, 1, 1) + BITFIELD_ENUMS(ENABLE_INTR, 4, 1) + }; + }; + + struct Stat_reg { + enum { + BITFIELD_ENUMS(RX_FIFO_VALID_DATA, 0, 1) + BITFIELD_ENUMS(RX_FIFO_FULL, 1, 1) + BITFIELD_ENUMS(TX_FIFO_EMPTY, 2, 1) + BITFIELD_ENUMS(TX_FIFO_FULL, 3, 1) + BITFIELD_ENUMS(INTR_ENABLED, 4, 1) + BITFIELD_ENUMS(OVERRUN_ERROR, 5, 1) + BITFIELD_ENUMS(FRAME_ERROR, 6, 1) + BITFIELD_ENUMS(PARITY_ERROR, 7, 1) + }; + }; + + /** + * Absolute register pointers + */ + volatile Register* const _rx_fifo; + volatile Register* const _tx_fifo; + volatile Register* const _stat_reg; + volatile Register* const _ctrl_reg; + }; +} + + +Xilinx::Xps_uartl::Xps_uartl(Cpu::addr_t const & base) : + _rx_fifo((Register*)(base + RX_FIFO_OFF)), + _tx_fifo((Register*)(base + TX_FIFO_OFF)), + _stat_reg((Register*)(base + STAT_REG_OFF)), + _ctrl_reg((Register*)(base + CTRL_REG_OFF)) +{} + + +void Xilinx::Xps_uartl::send(char const & c){ + while(*_stat_reg & Stat_reg::TX_FIFO_FULL_MSK); + *_tx_fifo = c; +} + + +#endif /* _INCLUDE__DEVICES__XILINX_XPS_UARTL_H_ */ diff --git a/base-mb/lib/mk/cxx.mk b/base-mb/lib/mk/cxx.mk new file mode 100755 index 000000000..5d176c384 --- /dev/null +++ b/base-mb/lib/mk/cxx.mk @@ -0,0 +1,87 @@ +LIBS = allocator_avl +CXX_SRC_CC += misc.cc new_delete.cc malloc_free.cc exception.cc guard.cc + +vpath %.cc $(BASE_DIR)/src/base/cxx + +# +# Microblaze-specific supplement +# +CXX_SRC_CC += atexit.cc + +vpath %.cc $(REP_DIR)/src/base/cxx + +# +# Here we define all symbols we want to hide in libsupc++ and libgcc_eh +# +LIBC_SYMBOLS += malloc free calloc realloc \ + abort fputc fputs fwrite \ + stderr strcat strcpy strlen up \ + memcmp strncmp strcmp sprintf + +# +# Take the right system libraries +# +# Normally, we never include build-system-internal files from library- +# description files. For building the 'cxx' library, however, we need the +# information about the used 'gcc' for resolving the location of the C++ +# support libraries. This definition is performed by 'mk/lib.mk' after +# including this library description file. Hence, we need to manually +# include 'global.mk' here. +# +include $(BASE_DIR)/mk/global.mk + +LIBCXX_GCC = $(shell $(CUSTOM_CXX_LIB) -print-file-name=libsupc++.a) \ + $(shell $(CUSTOM_CXX_LIB) -print-libgcc-file-name) + +# $(shell $(CUSTOM_CXX_LIB) -print-file-name=libgcc_eh.a) + +# +# Dummy target used by the build system +# +SRC_S = supc++.o +CXX_SRC = $(sort $(CXX_SRC_CC)) +CXX_OBJECTS = $(addsuffix .o,$(basename $(CXX_SRC))) +LOCAL_SYMBOLS = $(patsubst %,--localize-symbol=%,$(LIBC_SYMBOLS)) + +# +# Prevent symbols of the gcc support libs from being discarded during 'ld -r' +# +KEEP_SYMBOLS += __cxa_guard_acquire +KEEP_SYMBOLS += __moddi3 __divdi3 __umoddi3 __udivdi3 +KEEP_SYMBOLS += _ZTVN10__cxxabiv116__enum_type_infoE +KEEP_SYMBOLS += __fixunsdfdi +KEEP_SYMBOLS += __udivsi3 __divsi3 + +# +# Keep symbols additionally needed for linking the libc on ARM +# +KEEP_SYMBOLS += __muldi3 __eqdf2 __fixdfsi __ltdf2 __ltdf2 __nedf2 __ltdf2 \ + __gtdf2 __ltdf2 __ledf2 __fixdfsi __ltdf2 __ltdf2 __eqdf2 \ + __fixdfsi __ltdf2 __fixdfsi __eqdf2 __gtdf2 __ltdf2 __gtdf2 \ + __eqdf2 __muldi3 __muldi3 + +# +# Keep symbols needed for floating-point support on ARM +# +KEEP_SYMBOLS += __addsf3 __gtsf2 __ltsf2 + +# +# Additional symbols we need to keep when using the arm-none-linux-gnueabi +# tool chain +# +KEEP_SYMBOLS += __aeabi_ldivmod __aeabi_uldivmod __dynamic_cast +KEEP_SYMBOLS += _ZN10__cxxabiv121__vmi_class_type_infoD0Ev +KEEP_SYMBOLS += __aeabi_idiv __aeabi_ulcmp __aeabi_fmul __aeabi_dcmpun \ + __aeabi_d2lz __aeabi_f2lz __aeabi_d2f __aeabi_fcmpun \ + __aeabi_f2iz ctx_done sincos sincosf tgamma + +# +# Rule to link all libc definitions and libsupc++ libraries +# and to hide after that the exported libc symbols +# +$(SRC_S): $(CXX_OBJECTS) + $(MSG_MERGE)$@ + $(VERBOSE)$(LD) $(addprefix -u ,$(KEEP_SYMBOLS)) -r $(CXX_OBJECTS) $(LIBCXX_GCC) -o $@.tmp + $(MSG_CONVERT)$@ + $(VERBOSE)$(OBJCOPY) $(LOCAL_SYMBOLS) $@.tmp $@ + $(VERBOSE)$(RM) $@.tmp diff --git a/base-mb/lib/mk/ipc.mk b/base-mb/lib/mk/ipc.mk new file mode 100755 index 000000000..8c5d5d845 --- /dev/null +++ b/base-mb/lib/mk/ipc.mk @@ -0,0 +1,6 @@ +SRC_CC = ipc.cc +SRC_CC += pager.cc +LIBS += thread_context + +vpath ipc.cc $(REP_DIR)/src/base/ipc +vpath pager.cc $(REP_DIR)/src/base/ipc diff --git a/base-mb/lib/mk/kernel.inc b/base-mb/lib/mk/kernel.inc new file mode 100755 index 000000000..5d7fbe218 --- /dev/null +++ b/base-mb/lib/mk/kernel.inc @@ -0,0 +1,41 @@ +KERNEL_DIR = $(REP_DIR)/src/kernel + +INC_DIR += $(KERNEL_DIR)/include +INC_DIR += $(REP_DIR)/src/core/include + + +## +## Platform-specific kernel parts +## + +PLATFORM = petalogix_s3adsp1800_mmu + +# +# Basic platform support +# +include $(LIBINC_DIR)/$(PLATFORM)__kernel_support.inc + +# +# Enable atomic operations for this platform +# +LIBS += $(PLATFORM)__atomic_operations + + +## +## Generic kernel parts +## + +GENERIC_DIR = $(KERNEL_DIR)/generic + +SRC_CC += kernel.cc +SRC_CC += scheduler.cc +SRC_CC += thread.cc +SRC_CC += blocking.cc +SRC_CC += syscall_events.cc + +vpath kernel.cc $(GENERIC_DIR) +vpath scheduler.cc $(GENERIC_DIR) +vpath thread.cc $(GENERIC_DIR) +vpath blocking.cc $(GENERIC_DIR) +vpath syscall_events.cc $(GENERIC_DIR) + diff --git a/base-mb/lib/mk/kernel_core.mk b/base-mb/lib/mk/kernel_core.mk new file mode 100755 index 000000000..090e52136 --- /dev/null +++ b/base-mb/lib/mk/kernel_core.mk @@ -0,0 +1,13 @@ +LIBINC_DIR = $(REP_DIR)/lib/mk + +include $(LIBINC_DIR)/kernel.inc + +INC_DIR += $(REP_DIR)/src/platform +INC_DIR += $(REP_DIR)/src/core +INC_DIR += $(BASE_DIR)/src/platform + +include $(LIBINC_DIR)/kernel.inc +CC_OPT += -DROOTTASK_ENTRY=_main +SRC_CC += _main.cc + +vpath _main.cc $(BASE_DIR)/src/platform diff --git a/base-mb/lib/mk/kernel_test.inc b/base-mb/lib/mk/kernel_test.inc new file mode 100755 index 000000000..a35e00097 --- /dev/null +++ b/base-mb/lib/mk/kernel_test.inc @@ -0,0 +1,7 @@ +LIBINC_DIR = $(REP_DIR)/lib/mk + +include $(LIBINC_DIR)/kernel.inc + +INC_DIR += $(REP_DIR)/src/platform +INC_DIR += $(REP_DIR)/src/core +INC_DIR += $(BASE_DIR)/src/platform diff --git a/base-mb/lib/mk/lock.mk b/base-mb/lib/mk/lock.mk new file mode 100755 index 000000000..b34dacabd --- /dev/null +++ b/base-mb/lib/mk/lock.mk @@ -0,0 +1,6 @@ +PLATFORM = petalogix_s3adsp1800_mmu +LIBS = thread_context $(PLATFORM)__atomic_operations +SRC_CC = lock.cc +INC_DIR += $(REP_DIR)/src/base/lock + +vpath lock.cc $(BASE_DIR)/src/base/lock diff --git a/base-mb/lib/mk/pager.mk b/base-mb/lib/mk/pager.mk new file mode 100755 index 000000000..24f25bcf8 --- /dev/null +++ b/base-mb/lib/mk/pager.mk @@ -0,0 +1,4 @@ +SRC_CC = pager.cc +INC_DIR += $(REP_DIR)/include/codezero/dummies + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-mb/lib/mk/petalogix_s3adsp1800_mmu__atomic_operations.mk b/base-mb/lib/mk/petalogix_s3adsp1800_mmu__atomic_operations.mk new file mode 100644 index 000000000..8ace07986 --- /dev/null +++ b/base-mb/lib/mk/petalogix_s3adsp1800_mmu__atomic_operations.mk @@ -0,0 +1,6 @@ +PLATFORM_DIR = $(REP_DIR)/src/kernel/platforms/petalogix_s3adsp1800_mmu + +INC_DIR += $(PLATFORM_DIR)/include + +SRC_S += atomic.s +vpath atomic.s $(PLATFORM_DIR) diff --git a/base-mb/lib/mk/petalogix_s3adsp1800_mmu__kernel_support.inc b/base-mb/lib/mk/petalogix_s3adsp1800_mmu__kernel_support.inc new file mode 100755 index 000000000..5c703ad10 --- /dev/null +++ b/base-mb/lib/mk/petalogix_s3adsp1800_mmu__kernel_support.inc @@ -0,0 +1,29 @@ +## +## Platform +## +PLATFORM = petalogix_s3adsp1800_mmu + +# +# Assembly include paths +# +INC_DIR += $(KERNEL_DIR)/platforms/$(PLATFORM)/include + +# +# C++ include paths +# +INC_DIR += $(KERNEL_DIR)/include/$(PLATFORM) + +# +# Sources +# +PLATFORM_DIR = $(KERNEL_DIR)/platforms/$(PLATFORM) + +SRC_CC += platform.cc +SRC_S += crt0_kernel.s +SRC_S += kernel_entry.s +SRC_S += userland_entry.s + +vpath platform.cc $(PLATFORM_DIR) +vpath crt0_kernel.s $(PLATFORM_DIR) +vpath kernel_entry.s $(PLATFORM_DIR) +vpath userland_entry.s $(PLATFORM_DIR) diff --git a/base-mb/lib/mk/printf_microblaze.mk b/base-mb/lib/mk/printf_microblaze.mk new file mode 100755 index 000000000..00c6c7588 --- /dev/null +++ b/base-mb/lib/mk/printf_microblaze.mk @@ -0,0 +1,5 @@ +SRC_CC = microblaze_console.cc +LIBS = cxx console +INC_DIR += $(REP_DIR)/src/platform + +vpath %.cc $(REP_DIR)/src/base/console diff --git a/base-mb/lib/mk/startup.mk b/base-mb/lib/mk/startup.mk new file mode 100755 index 000000000..9aec768c8 --- /dev/null +++ b/base-mb/lib/mk/startup.mk @@ -0,0 +1,13 @@ +PLATFORM = petalogix_s3adsp1800_mmu +KERNEL_DIR = $(REP_DIR)/src/kernel +PLATFORM_DIR = $(KERNEL_DIR)/platforms/$(PLATFORM) + +LIBS = cxx lock +SRC_S = crt0.s +SRC_CC += _main.cc +INC_DIR += $(REP_DIR)/src/platform +INC_DIR += $(BASE_DIR)/src/platform +INC_DIR += $(PLATFORM_DIR)/include + +vpath crt0.s $(PLATFORM_DIR) +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-mb/lib/mk/test_env.mk b/base-mb/lib/mk/test_env.mk new file mode 100755 index 000000000..99adcd34f --- /dev/null +++ b/base-mb/lib/mk/test_env.mk @@ -0,0 +1,6 @@ +SRC_CC = context_area.cc thread_roottask.cc thread.cc +LIBS += lock thread_context + +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath thread_roottask.cc $(REP_DIR)/src/test +vpath context_area.cc $(REP_DIR)/src/test diff --git a/base-mb/lib/mk/thread.mk b/base-mb/lib/mk/thread.mk new file mode 100644 index 000000000..265adc053 --- /dev/null +++ b/base-mb/lib/mk/thread.mk @@ -0,0 +1,8 @@ +SRC_CC = thread.cc thread_start.cc thread_bootstrap.cc +INC_DIR += $(REP_DIR)/include/codezero/dummies +INC_DIR += $(REP_DIR)/src/core/include + +vpath thread.cc $(REP_DIR)/src/base/thread +vpath thread_start.cc $(REP_DIR)/src/base/thread +vpath thread_bootstrap.cc $(REP_DIR)/src/base/thread +vpath %.cc $(BASE_DIR)/src/base/thread diff --git a/base-mb/lib/mk/thread_context.mk b/base-mb/lib/mk/thread_context.mk new file mode 100755 index 000000000..3693bce60 --- /dev/null +++ b/base-mb/lib/mk/thread_context.mk @@ -0,0 +1,5 @@ +SRC_CC = thread_context.cc + +INC_DIR += $(REP_DIR)/src/core/include + +vpath thread_context.cc $(REP_DIR)/src/base/thread diff --git a/base-mb/mk/spec-mb_ml507.mk b/base-mb/mk/spec-mb_ml507.mk new file mode 100644 index 000000000..e6199c897 --- /dev/null +++ b/base-mb/mk/spec-mb_ml507.mk @@ -0,0 +1,7 @@ +SPECS += 32bit mb_timer + +STARTUP_LIB ?= startup + +PRG_LIBS += $(STARTUP_LIB) + +include $(call select_from_repositories,mk/spec-32bit.mk) diff --git a/base-mb/mk/spec-mb_s3a_starter_kit.mk b/base-mb/mk/spec-mb_s3a_starter_kit.mk new file mode 100644 index 000000000..e6199c897 --- /dev/null +++ b/base-mb/mk/spec-mb_s3a_starter_kit.mk @@ -0,0 +1,7 @@ +SPECS += 32bit mb_timer + +STARTUP_LIB ?= startup + +PRG_LIBS += $(STARTUP_LIB) + +include $(call select_from_repositories,mk/spec-32bit.mk) diff --git a/base-mb/platform/mb_s3a_starter_kit/Makefile b/base-mb/platform/mb_s3a_starter_kit/Makefile new file mode 100644 index 000000000..5d215f2c9 --- /dev/null +++ b/base-mb/platform/mb_s3a_starter_kit/Makefile @@ -0,0 +1,22 @@ +# +# \brief Prepare the Xilinx Spartan 3A Starter Kit to run Genode on it +# \author Martin Stein +# \date 2011-05-23 +# + +HW = s3a_starter_kit +WORK_DIR = $(shell pwd) +BIT_FILE = $(WORK_DIR)/system.bit +VERBOSE ?= @ +REP_DIR = ../.. +MK_DIR = $(REP_DIR)/platform/mk/ + +all: configure + +clean: + $(VERBOSE) rm -f $(TMP_FILES) + +.PHONY: all clean + +include $(MK_DIR)/$(HW).mk +include $(MK_DIR)/microblaze.mk diff --git a/base-mb/platform/mb_s3a_starter_kit/system.bit b/base-mb/platform/mb_s3a_starter_kit/system.bit new file mode 100644 index 0000000000000000000000000000000000000000..13ca4c7f449374790dd6f3371249337f5c596cfb GIT binary patch literal 341653 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 z%0DAQfrUxwpn3y{ujzP%1w?bOu+9O|5*& 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

jwnG&%FflNL=4P0U6AoOy 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 literal 0 HcmV?d00001 diff --git a/base-mb/platform/mk/microblaze.mk b/base-mb/platform/mk/microblaze.mk new file mode 100644 index 000000000..a0b8be9a6 --- /dev/null +++ b/base-mb/platform/mk/microblaze.mk @@ -0,0 +1,19 @@ +# +# \brief Upload an image to the supported Microblaze SoC's +# \author Martin Stein +# \date 2011-05-23 +# + +UPLOAD_XMD = $(WORK_DIR)/upload.xmd + +upload: $(UPLOAD_XMD) + $(VERBOSE) xmd -opt $(UPLOAD_XMD) + +$(UPLOAD_XMD): + $(VERBOSE)echo \ + "connect mb mdm"\ + "\ndow $(IMAGE)"\ + > $(UPLOAD_XMD) + +.INTERMEDIATE: $(UPLOAD_XMD) +.PHONY: upload diff --git a/base-mb/platform/mk/ml507.mk b/base-mb/platform/mk/ml507.mk new file mode 100644 index 000000000..e26bf6d7b --- /dev/null +++ b/base-mb/platform/mk/ml507.mk @@ -0,0 +1,19 @@ +# +# \brief Configure support for the Xilinx ML507 Development Kit via JTAG +# \author Martin Stein +# \date 2011-05-23 +# + +$(CONFIGURE_IMPACT): + $(VERBOSE) echo \ + "setMode -bscan"\ + "\nsetCable -p auto"\ + "\nidentify"\ + "\nassignfile -p 5 -file $(BIT_FILE)"\ + "\nprogram -p 5"\ + "\nquit"\ + > $@ + +.INTERMEDIATE: $(CONFIGURE_IMPACT) + +include $(MK_DIR)/xilinx.mk diff --git a/base-mb/platform/mk/s3a_starter_kit.mk b/base-mb/platform/mk/s3a_starter_kit.mk new file mode 100644 index 000000000..5ea33e93e --- /dev/null +++ b/base-mb/platform/mk/s3a_starter_kit.mk @@ -0,0 +1,22 @@ +# +# \brief Configure support for the Xilinx Spartan 3A Starter Kit via JTAG +# \author Martin Stein +# \date 2011-05-23 +# + +CONFIGURE_IMPACT = $(WORK_DIR)/configure.impact + +$(CONFIGURE_IMPACT): + $(VERBOSE) echo \ + "\nsetMode -bs"\ + "\nsetCable -port auto"\ + "\nIdentify -inferir"\ + "\nidentifyMPM"\ + "\nassignFile -p 1 -file \"$(BIT_FILE)\""\ + "\nProgram -p 1"\ + "\nquit"\ + > $@ + +.INTERMEDIATE: $(CONFIGURE_IMPACT) + +include $(MK_DIR)/xilinx.mk diff --git a/base-mb/platform/mk/xilinx.mk b/base-mb/platform/mk/xilinx.mk new file mode 100644 index 000000000..a607f7bce --- /dev/null +++ b/base-mb/platform/mk/xilinx.mk @@ -0,0 +1,24 @@ +# +# \brief Configure the supported Xilinx FPGAs +# \author Martin Stein +# \date 2011-05-23 +# + +TMP_FILES += $(WORK_DIR)/_impactbatch.log +CLEANLOCK_IMPACT = $(WORK_DIR)/cleanlock.impact + +configure: $(CONFIGURE_IMPACT) cleanlock + $(VERBOSE) impact -batch $(CONFIGURE_IMPACT) + $(VERBOSE) make clean + +cleanlock: $(CLEANLOCK_IMPACT) + $(VERBOSE) impact -batch $(CLEANLOCK_IMPACT) || true + +$(CLEANLOCK_IMPACT): + $(VERBOSE) echo \ + "cleancablelock"\ + "\nexit"\ + > $@ + +.INTERMEDIATE: $(UPLOAD_XMD) $(CLEANLOCK_IMPACT) +.PHONY: configure cleanlock diff --git a/base-mb/run/env b/base-mb/run/env new file mode 100755 index 000000000..fb595ba1f --- /dev/null +++ b/base-mb/run/env @@ -0,0 +1,211 @@ +# +# \brief Environment for executing Genode on Microblaze +# \author Norman Feske +# \author Martin Stein +# \date 2010-09-01 +# +# For the documentation of the implemented API functions, +# please refer to the comments in 'tool/run'. +# + +proc create_boot_directory { } { + catch { + exec rm -rf [run_dir] + exec mkdir -p [run_dir] + } +} + + +proc build {targets {build_core 0}} { + + if {[get_cmd_switch --skip-build]} return + + regsub -all {\s\s+} $targets " " targets + + # Save building 'core' until last + if {$build_core == 0} { + regsub -all {\mcore\M} $targets "" targets + } + + puts "building targets: $targets" + set timeout 10000 + set pid [eval "spawn make $targets"] + expect { eof { } } + if {[lindex [wait $pid] end] != 0} { + puts "Error: Genode build failed" + exit -4 + } + puts "genode build completed" +} + +proc stripped_copy {binary} { + exec mkdir -p bin/stripped/ + exec rm -rf bin/stripped/$binary + exec cp bin/${binary} bin/stripped/${binary} + catch {exec [cross_dev_prefix]strip bin/stripped/${binary}} +} + + +# +# Microblaze needs a single boot image, thus this function creates an temporary assembly file that +# includes all images wich are needed by the boot image to be included by it +# +proc build_boot_modules {} { + + global boot_modules + global boot_modules_s + set boot_modules_s "[genode_dir]/base-mb/src/core/boot_modules.s" + + exec echo -e \ + "\n/**"\ + "\n * This file was generated by the expect procedure"\ + "\n * 'build_boot_modules' in 'run/env'"\ + "\n */"\ + "\n\n"\ + "\n.global _boot_modules_meta_start" \ + "\n.global _boot_modules_meta_end" \ + "\n\n" \ + "\n.section .data" \ + "\n.string \"GROM\"" \ + "\n.long header_end" \ + "\n.align 4" \ + "\n_boot_modules_meta_start:" > $boot_modules_s + + # Header, pointers part + set i 1 + foreach module $boot_modules { + exec echo -e \ + "\n.long mod${i}_name" \ + "\n.long mod${i}_start" \ + "\n.long mod${i}_end - mod${i}_start" >> $boot_modules_s + incr i + } + + exec echo -e \ + "\n.align 4"\ + "\n_boot_modules_meta_end:" >> $boot_modules_s + + # Header, names part + set i 1 + foreach module $boot_modules { + exec echo -e \ + "\nmod${i}_name:" \ + "\n.string \"${module}\"" \ + "\n.byte 0" >> $boot_modules_s + incr i + } + + exec echo -e "header_end:" >> $boot_modules_s + + # Modulecontents + set i 1 + foreach module $boot_modules { + exec echo -e ".align 12" >> $boot_modules_s + +# Stripped images the boot image depends on are not enabled because 'mb-strip' destroys +# the file offset alignments and Genode needs a specific minimum file offset alignment +# +# if { [catch {exec [cross_dev_prefix]readelf -h bin/${module}}] } { + exec echo -e "mod${i}_start: .incbin \"../bin/${module}\"" >> $boot_modules_s +# } else { +# exec echo -e "mod${i}_start: .incbin \"../bin/stripped/${module}\"" >> $boot_modules_s +# } + exec echo -e "mod${i}_end:" >> $boot_modules_s + incr i + } + + exec echo -e ".align 12" >> $boot_modules_s +} + + +proc build_boot_image {images} { + global boot_modules + global boot_modules_s + + foreach image $images { + if {$image != "core"} { + +# Stripped images the boot image depends on are not enabled because 'mb-strip' destroys +# the file offset alignments and Genode needs a specific minimum file offset alignment +# +# if { [catch {exec [cross_dev_prefix]readelf -h bin/${image}}] == 0 } { +# stripped_copy $image +# } + append boot_modules "${image}" " " + } + } + + build_boot_modules + build "core" 1 + stripped_copy "core" + catch { + exec ln -sf ../../../bin/stripped/core [run_dir]/image.elf + } + exec rm -f $boot_modules_s +} + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + + set image [pwd]/[run_dir]/image.elf + set target [get_cmd_arg --target "qemu"] + + if { $target == "jtag" } { + + # try to run on device via jtag + spawn make -C [genode_dir]/base-mb/platform/[hardware] upload IMAGE=$image VERBOSE= + interact + + } elseif { $target == "qemu" } { + + # run on qemu + global output + set timeout $timeout_value + set pid [spawn [qemu] -kernel $image -serial stdio] + if {$wait_for_re == "forever"} { interact $pid } + expect { + -re $wait_for_re { } + timeout { puts stderr "Error: Test execution timed out"; exit -2 } + } + set output $expect_out(buffer) + } else { + + puts stderr "Error: Target '${target}' is not supported" + puts stderr " Supported targets are: 'jtag' and 'qemu'"; exit -3 + + } +} + + +proc install_config {config} { + global boot_modules + append boot_modules "config" " " + + set fh [open "bin/config" "WRONLY CREAT TRUNC"] + puts $fh $config + close $fh + exec touch [genode_dir]/base-mb/src/core/boot_modules.s +} + + +proc qemu { } { + global _qemu + set _qemu [get_cmd_arg --qemu "qemu-system-microblaze"] + return $_qemu +} + +proc hardware { } { + global _hardware + + # + # Test on all supported platforms + # + + if { [have_spec {mb_s3a_starter_kit}] } { + set _hardware mb_s3a_starter_kit + return $_hardware + } + if { [have_spec {mb_ml507}] } { + set _hardware mb_ml507 + return $_hardware + } +} diff --git a/base-mb/run/hello.run b/base-mb/run/hello.run new file mode 100755 index 000000000..eaec3f3fe --- /dev/null +++ b/base-mb/run/hello.run @@ -0,0 +1,15 @@ +build "core init test/hello" + +install_config { + + + + + + + +} + +create_boot_directory +build_boot_image "core init hello" +run_genode_until "child exited with exit value 0" 20 diff --git a/base-mb/run/nested_init.run b/base-mb/run/nested_init.run new file mode 100755 index 000000000..fef6631f3 --- /dev/null +++ b/base-mb/run/nested_init.run @@ -0,0 +1,34 @@ +build "init core" + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +create_boot_directory +build_boot_image "init core" +run_genode_until forever diff --git a/base-mb/src/base/console/microblaze_console.cc b/base-mb/src/base/console/microblaze_console.cc new file mode 100755 index 000000000..c88c97508 --- /dev/null +++ b/base-mb/src/base/console/microblaze_console.cc @@ -0,0 +1,64 @@ +/* + * \brief Console backend for Microblaze + * \author Martin Stein + * \date 2011-02-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +namespace Genode { + + class Microblaze_console : public Console + { + private: + + Xilinx::Xps_uartl _uart; + + protected: + + virtual void _out_char(char c) + { + _uart.send(c); + } + + public: + + Microblaze_console() : _uart(0x84000000) {} + }; +} + + +using namespace Genode; + + +static Microblaze_console µblaze_console() +{ + static Microblaze_console static_microblaze_console; + return static_microblaze_console; +} + + +void Genode::printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + microblaze_console().vprintf(format, list); + + va_end(list); +} + + +void Genode::vprintf(const char *format, va_list list) +{ + microblaze_console().vprintf(format, list); +} diff --git a/base-mb/src/base/cxx/atexit.cc b/base-mb/src/base/cxx/atexit.cc new file mode 100755 index 000000000..68453711a --- /dev/null +++ b/base-mb/src/base/cxx/atexit.cc @@ -0,0 +1,19 @@ +/* + * \brief C++ support for Microblaze cross compiler + * \author Norman Feske + * \date 2010-07-21 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* + * The mb-gcc generates calls to 'atexit' instead of '__cxa_atexit' as + * usual. + */ +extern "C" __attribute__((weak)) +void *atexit() { return 0; } diff --git a/base-mb/src/base/ipc/ipc.cc b/base-mb/src/base/ipc/ipc.cc new file mode 100755 index 000000000..b95392bf2 --- /dev/null +++ b/base-mb/src/base/ipc/ipc.cc @@ -0,0 +1,201 @@ +/* + * \brief Implementation of the IPC API + * \author Norman Feske + * \date 2010-09-06 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* kernel includes */ +#include + +using namespace Genode; + + +/*************** + ** Utilities ** + ***************/ + +template +static unsigned size_to_size_in(unsigned s) +{ + return (unsigned)(s+sizeof(T)-1)/sizeof(T); +} + + +/** + * Copy message registers from UTCB to destination message buffer + */ +static void copy_utcb_to_msgbuf(unsigned message_size, + Msgbuf_base *receive_buffer) +{ + if (!message_size) return; + + if (message_size > receive_buffer->size()) + message_size = receive_buffer->size(); + + Cpu::word_t *message_buffer = (Cpu::word_t*)receive_buffer->buf; + Native_utcb *utcb = Thread_base::myself()->utcb(); + unsigned msg_size_in_words = size_to_size_in(message_size); + + for (unsigned i=0; i < msg_size_in_words; i++) + message_buffer[i] = utcb->word[i]; +} + + +/** + * Copy message payload to UTCB message registers + */ +static void copy_msgbuf_to_utcb(Msgbuf_base *send_buffer, + unsigned message_size, + unsigned local_name) +{ + typedef Kernel::Utcb Utcb; + + if (!message_size) return; + + Native_utcb *utcb = Thread_base::myself()->utcb(); + unsigned header_size = sizeof(local_name); + + if (message_size + header_size > utcb->size()) { + if (header_size > utcb->size()) + return; + + message_size = utcb->size()-header_size; + } + + Cpu::word_t *message_buffer = (Cpu::word_t*)send_buffer->buf; + unsigned msg_size_in_words = size_to_size_in(message_size); + unsigned h_size_in_words = size_to_size_in(header_size); + + utcb->word[0] = local_name; + + for (unsigned i = h_size_in_words; i < msg_size_in_words; i++) { + utcb->word[i] = message_buffer[i]; + } +} + + +/***************** + ** Ipc_ostream ** + *****************/ + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) +: + Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()), + _snd_msg(snd_msg), + _dst(dst) +{ + _write_offset = sizeof(umword_t); +} + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() { Kernel::thread_sleep(); } + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) +: + Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), + Native_capability(Genode::my_thread_id(), 0), + _rcv_msg(rcv_msg), + _rcv_cs(-1) +{ + _read_offset = sizeof(umword_t); +} + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + + +void Ipc_client::_call() +{ + unsigned request_size = _write_offset; + copy_msgbuf_to_utcb(_snd_msg, request_size, _dst.local_name()); + + unsigned reply_size = Kernel::ipc_request(_dst.tid(), request_size); + + copy_utcb_to_msgbuf(reply_size, _rcv_msg); + + /* reset marshalling / unmarshalling pointers */ + _write_offset = _read_offset=sizeof(umword_t); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, + Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) +{ } + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_prepare_next_reply_wait() +{ + /* now we have a request to reply */ + _reply_needed = true; + + enum { RETURN_VALUE_SIZE = sizeof(umword_t) }; + _write_offset = sizeof(umword_t)+RETURN_VALUE_SIZE; + + _read_offset = sizeof(umword_t); +} + + +void Ipc_server::_wait() +{ + /* wait for new request */ + Cpu::size_t reply_size = 0; + Cpu::size_t request_size = Kernel::ipc_serve(reply_size); + + + copy_utcb_to_msgbuf(request_size, _rcv_msg); + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() { _prepare_next_reply_wait(); } + + +void Ipc_server::_reply_wait() +{ + unsigned reply_size = 0; + if (_reply_needed) { + reply_size = _write_offset; + copy_msgbuf_to_utcb(_snd_msg, reply_size, _dst.local_name()); + } + + unsigned request_size = Kernel::ipc_serve(reply_size); + + copy_utcb_to_msgbuf(request_size, _rcv_msg); + _prepare_next_reply_wait(); +} + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg) +: + Ipc_istream(rcv_msg), + Ipc_ostream(Native_capability(my_thread_id(), 0), snd_msg), + _reply_needed(false) +{ } + diff --git a/base-mb/src/base/ipc/pager.cc b/base-mb/src/base/ipc/pager.cc new file mode 100755 index 000000000..91a443027 --- /dev/null +++ b/base-mb/src/base/ipc/pager.cc @@ -0,0 +1,74 @@ +/* + * \brief Pager support for Microblaze Kernel + * \author Norman Feske + * \author Martin Stein + * \date 2010-09-23 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* kernel includes */ +#include + +using namespace Genode; + + +void Ipc_pager::wait_for_fault() +{ + typedef Kernel::Paging::Request Request; + + while (1) { + + /* wait for fault message */ + unsigned const msg_length=Kernel::ipc_serve(0); + + /* check message format */ + if (msg_length==sizeof(Request)){ + + _request=*((Request*)Thread_base::myself()->utcb()); + +// PERR( +// "Recieved pagefault, va=%p, tid=%i, pid=%i", +// _request.virtual_page.address(), +// _request.source.tid, +// _request.virtual_page.protection_id()); + + return; + } + } +} + + +void Ipc_pager::reply_and_wait_for_fault() +{ + /* load mapping to tlb (not to be considered permanent) */ + if (_mapping.valid()) + Kernel::tlb_load( + _mapping.physical_page.address(), + _mapping.virtual_page.address(), + _request.virtual_page.protection_id(), + _mapping.physical_page.size(), + _mapping.physical_page.permissions()); + +// PERR( +// "Resoluted, pa=%p, va=%p, tid=%i, pid=%i", +// _mapping.physical_page.address(), +// _mapping.virtual_page.address(), +// _request.source.tid, +// _request.virtual_page.protection_id()); + + /* wake up faulter if mapping succeeded */ acknowledge_wakeup(); + + /* wait for next page fault */ wait_for_fault(); +} + + diff --git a/base-mb/src/base/lock/lock_helper.h b/base-mb/src/base/lock/lock_helper.h new file mode 100755 index 000000000..dfa1bf20c --- /dev/null +++ b/base-mb/src/base/lock/lock_helper.h @@ -0,0 +1,57 @@ +/* + * \brief Dummy helper functions for the Lock implementation + * \author Norman Feske + * \date 2009-10-02 + * + * For documentation about the interface, please revisit the 'base-pistachio' + * implementation. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* kernel includes */ +#include +#include + + +static inline void thread_yield() { Kernel::thread_yield(); } + + +static bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + Kernel::thread_wake(tid); + return true; +} + + +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + return Genode::my_thread_id(); +} + + +static inline Genode::Native_thread_id thread_invalid_id() { return -1; } + + +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return tid != thread_invalid_id(); +} + + +static inline void thread_switch_to(Genode::Native_thread_id tid) +{ + thread_yield(); +} + + +static inline void thread_stop_myself() { Kernel::thread_sleep(); } diff --git a/base-mb/src/base/pager/pager.cc b/base-mb/src/base/pager/pager.cc new file mode 100644 index 000000000..52c5e722b --- /dev/null +++ b/base-mb/src/base/pager/pager.cc @@ -0,0 +1,115 @@ +/* + * \brief Dummy pager framework + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + + +using namespace Genode; + + +/********************** + ** Pager activation ** + **********************/ + +void Pager_activation_base::entry() +{ + Ipc_pager pager; + _cap = pager; + _cap_valid.unlock(); + +// PINF("Ready for page faults"); + pager.wait_for_fault(); + while (1) { + + /* lookup referenced object */ + Pager_object *obj = _ep ? _ep->obj_by_id(pager.badge()) : 0; + + /* handle request */ + if (obj) { +// PINF("Pagefault request from a common pager object"); + + if (!pager.resolved()){ + if (obj->pager(pager)){ + /* something strange occured - leave thread in pagefault */ +// PINF("Leave unresolved, wait for next page fault"); + pager.wait_for_fault(); + } + else{ +// PINF("Resolved, reply and wait for next page fault"); + pager.reply_and_wait_for_fault(); + } + } + else{ + pager.reply_and_wait_for_fault(); + } + } + else { +// PINF("Pagefault request from one of cores region manager sessions"); + + /* + * We got a request from one of cores region-manager sessions + * to answer the pending page fault of a resolved region-manager + * client. Hence, we have to send the page-fault reply to the + * specified thread and answer the call of the region-manager + * session. + * + * When called from a region-manager session, we receive the + * core-local address of the targeted pager object via the + * first message word, which corresponds to the 'fault_ip' + * argument of normal page-fault messages. + */ + obj = reinterpret_cast(pager.fault_ip()); + + /* send reply to the calling region-manager session */ + pager.acknowledge_wakeup(); + + /* answer page fault of resolved pager object */ + pager.set_reply_dst(obj->cap()); + pager.acknowledge_wakeup(); + +// PINF("Wait for next page fault"); + pager.wait_for_fault(); + } + } +} + + +/********************** + ** Pager entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *, Pager_activation_base *a) +: _activation(a) +{ + _activation->ep(this); +} + + +void Pager_entrypoint::dissolve(Pager_object *obj) { remove(obj); } + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + /* return invalid capability if no activation is present */ + if (!_activation) return Pager_capability(); + + Native_capability cap = Native_capability(_activation->cap().tid(), obj->badge()); + + /* add server object to object pool */ + obj->cap(cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return reinterpret_cap_cast(cap); +} diff --git a/base-mb/src/base/thread/thread.cc b/base-mb/src/base/thread/thread.cc new file mode 100644 index 000000000..dbc926009 --- /dev/null +++ b/base-mb/src/base/thread/thread.cc @@ -0,0 +1,207 @@ +/* + * \brief Implementation of the Thread API + * \author Norman Feske + * \date 2010-01-11 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +using namespace Genode; + + +/** + * Return the managed dataspace holding the thread context area + * + * This function is provided by the process environment. + */ +namespace Genode { + Rm_session *env_context_area_rm_session(); + Ram_session *env_context_area_ram_session(); +} + + +/****************************** + ** Thread-context allocator ** + ******************************/ + +Thread_base::Context *Thread_base::Context_allocator::base_to_context(addr_t base) +{ + addr_t result = base + CONTEXT_VIRTUAL_SIZE - sizeof(Context); + return reinterpret_cast(result); +} + + +addr_t Thread_base::Context_allocator::addr_to_base(void *addr) +{ + return ((addr_t)addr) & CONTEXT_VIRTUAL_BASE_MASK; +} + + +bool Thread_base::Context_allocator::_is_in_use(addr_t base) +{ + List_element *le = _threads.first(); + for (; le; le = le->next()) + if (base_to_context(base) == le->object()->_context) + return true; + + return false; +} + + +Thread_base::Context *Thread_base::Context_allocator::alloc(Thread_base *thread_base) +{ + Lock::Guard _lock_guard(_threads_lock); + + /* + * Find slot in context area for the new context + */ + addr_t base = CONTEXT_AREA_VIRTUAL_BASE; + for (; _is_in_use(base); base += CONTEXT_VIRTUAL_SIZE) { + + /* check upper bound of context area */ + if (base >= CONTEXT_AREA_VIRTUAL_BASE + CONTEXT_AREA_VIRTUAL_SIZE) + return 0; + } + + _threads.insert(&thread_base->_list_element); + + return base_to_context(base); +} + + +void Thread_base::Context_allocator::free(Thread_base *thread_base) +{ + Lock::Guard _lock_guard(_threads_lock); + + _threads.remove(&thread_base->_list_element); +} + + +/***************** + ** Thread base ** + *****************/ + +Thread_base::Context_allocator *Thread_base::_context_allocator() +{ + static Context_allocator context_allocator_inst; + return &context_allocator_inst; +} + + +Thread_base::Context *Thread_base::_alloc_context(size_t stack_size) +{ + /* + * Synchronize context list when creating new threads from multiple threads + * + * XXX: remove interim fix + */ + static Lock alloc_lock; + Lock::Guard _lock_guard(alloc_lock); + + /* allocate thread context */ + Context *context = _context_allocator()->alloc(this); + if (!context) throw Context_alloc_failed(); + + /* determine size of dataspace to allocate for context members and stack */ + enum { PAGE_SIZE_LOG2 = 12 }; + size_t ds_size = align_addr(stack_size, PAGE_SIZE_LOG2); + + if (stack_size >= CONTEXT_VIRTUAL_SIZE - sizeof(Native_utcb) - (1 << PAGE_SIZE_LOG2)) + throw Stack_too_large(); + + /* + * Calculate base address of the stack + * + * The stack is always located at the top of the context. + */ + addr_t ds_addr = Context_allocator::addr_to_base(context) + CONTEXT_VIRTUAL_SIZE + - ds_size; + + /* add padding for UTCB if defined for the platform */ + if (sizeof(Native_utcb) >= (1 << PAGE_SIZE_LOG2)) + ds_addr -= sizeof(Native_utcb); + + /* allocate and attach backing store for the stack */ + Ram_dataspace_capability ds_cap; + try { + ds_cap = env_context_area_ram_session()->alloc(ds_size); + addr_t attach_addr = ds_addr - CONTEXT_AREA_VIRTUAL_BASE; + env_context_area_rm_session()->attach_at(ds_cap, attach_addr, ds_size); + + } catch (Ram_session::Alloc_failed) { + throw Stack_alloc_failed(); + } + + _init_context(context); + + /* + * Now the thread context is backed by memory, so it is safe to access its + * members. + */ + + context->thread_base = this; + context->stack_base = ds_addr; + context->ds_cap = ds_cap; + return context; +} + + +void Thread_base::_free_context() +{ + addr_t ds_addr = _context->stack_base - CONTEXT_AREA_VIRTUAL_BASE; + Ram_dataspace_capability ds_cap = _context->ds_cap; + Genode::env_context_area_rm_session()->detach((void *)ds_addr); + Genode::env_context_area_ram_session()->free(ds_cap); + _context_allocator()->free(this); +} + + +void Thread_base::name(char *dst, size_t dst_len) +{ + snprintf(dst, min(dst_len, (size_t)Context::NAME_LEN), _context->name); +} + + +Thread_base *Thread_base::myself() +{ + addr_t sp = Xilinx::Microblaze::stack_pointer(); + + /* + * If the stack pointer is outside the thread-context area, we assume that + * we are the main thread because this condition can never met by any other + * thread. + */ + if (sp < CONTEXT_AREA_VIRTUAL_BASE + || sp >= CONTEXT_AREA_VIRTUAL_BASE + CONTEXT_AREA_VIRTUAL_SIZE) + return 0; + + addr_t base = Context_allocator::addr_to_base((void*)sp); + return Context_allocator::base_to_context(base)->thread_base; +} + + +Thread_base::Thread_base(const char *name, size_t stack_size) +: _list_element(this), _context(_alloc_context(stack_size)) +{ + strncpy(_context->name, name, sizeof(_context->name)); + _init_platform_thread(); +} + + +Thread_base::~Thread_base() +{ + _deinit_platform_thread(); + _free_context(); +} diff --git a/base-mb/src/base/thread/thread_bootstrap.cc b/base-mb/src/base/thread/thread_bootstrap.cc new file mode 100755 index 000000000..2f8134424 --- /dev/null +++ b/base-mb/src/base/thread/thread_bootstrap.cc @@ -0,0 +1,22 @@ +/* + * \brief Default thread bootstrap code + * \author Norman Feske + * \date 2009-04-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + +void Thread_base::_thread_bootstrap() +{ + myself()->_tid=*((Native_thread_id*)myself()->utcb()); +} diff --git a/base-mb/src/base/thread/thread_context.cc b/base-mb/src/base/thread/thread_context.cc new file mode 100755 index 000000000..93f819e66 --- /dev/null +++ b/base-mb/src/base/thread/thread_context.cc @@ -0,0 +1,57 @@ +/* + * \brief Thread-context specific part of the thread library + * \author Norman Feske + * \date 2010-01-19 + * + * This part of the thread library is required by the IPC framework + * also if no threads are used. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* kernel includes */ +#include +#include +#include + +using namespace Genode; + + +extern Genode::Native_utcb* _main_utcb_addr; +Genode::Native_thread_id _main_thread_id; + + +bool is_this_main_thread() { return Thread_base::myself() == 0; } + + +Native_utcb* Thread_base::utcb() +{ + if (is_this_main_thread()) + return _main_utcb_addr; + + return &_context->utcb; +} + + +Native_thread_id Genode::my_thread_id() +{ + if (!is_this_main_thread()) + return Thread_base::myself()->tid(); + + unsigned pid = (unsigned)Xilinx::Microblaze::protection_id(); + + if (pid == Roottask::PROTECTION_ID) + return Roottask::MAIN_THREAD_ID; + + return _main_thread_id; +} + + diff --git a/base-mb/src/base/thread/thread_start.cc b/base-mb/src/base/thread/thread_start.cc new file mode 100644 index 000000000..01158ffe2 --- /dev/null +++ b/base-mb/src/base/thread/thread_start.cc @@ -0,0 +1,73 @@ +/* + * \brief Implementation of the Thread API + * \author Norman Feske + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +using namespace Genode; + + +/** + * Entry point entered by new threads + */ +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + Genode::sleep_forever(); +} + + +/***************** + ** Thread base ** + *****************/ + +void Thread_base::_init_context(Context* c) { } + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() +{ + env()->cpu_session()->kill_thread(_thread_cap); +} + + +void Thread_base::start() +{ + /* create thread at core */ + char buf[48]; + name(buf, sizeof(buf)); + _thread_cap = env()->cpu_session()->create_thread(buf); + + /* assign thread to protection domain */ + env()->pd_session()->bind_thread(_thread_cap); + + /* create new pager object and assign it to the new thread */ + Pager_capability pager_cap = env()->rm_session()->add_client(_thread_cap); + env()->cpu_session()->set_pager(_thread_cap, pager_cap); + + /* register initial IP and SP at core */ + addr_t thread_sp = (addr_t)&_context->stack[-4]; + thread_sp &= ~0xf; /* align initial stack to 16 byte boundary */ + env()->cpu_session()->start(_thread_cap, (addr_t)_thread_start, thread_sp); +} + + +void Thread_base::cancel_blocking() +{ + env()->cpu_session()->cancel_blocking(_thread_cap); +} diff --git a/base-mb/src/core/context_area.cc b/base-mb/src/core/context_area.cc new file mode 100644 index 000000000..0d1bdec47 --- /dev/null +++ b/base-mb/src/core/context_area.cc @@ -0,0 +1,148 @@ +/* + * \brief Support code for the thread API + * \author Norman Feske + * \date 2010-01-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include + +using namespace Genode; + + +/** + * Pointer to dataspace used to hold core contexts + */ +enum { MAX_CORE_CONTEXTS = 256 }; +static Dataspace_component *context_ds[MAX_CORE_CONTEXTS]; + + +/** + * Region-manager session for allocating thread contexts + * + * This class corresponds to the managed dataspace that is normally + * used for organizing thread contexts with the thread context area. + * It "emulates" the sub address space by adjusting the local address + * argument to 'attach' with the offset of the thread context area. + */ +class Context_area_rm_session : public Rm_session +{ + public: + + /** + * Attach backing store to thread-context area + */ + Local_addr attach(Dataspace_capability ds_cap, + size_t size, off_t offset, + bool use_local_addr, Local_addr local_addr) + { + Dataspace_component *ds = context_ds[ds_cap.local_name()]; + if (!ds) { + PERR("dataspace for core context does not exist"); + return 0; + } + + if (!map_local(ds->phys_addr(), + (addr_t)local_addr + Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + ds->size() >> get_page_size_log2())) + return 0; + + return local_addr; + } + + void detach(Local_addr) { } + + Pager_capability add_client(Thread_capability) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } +}; + + +class Context_area_ram_session : public Ram_session +{ + public: + + Ram_dataspace_capability alloc(size_t size) + { + /* find free context */ + unsigned i; + for (i = 0; i < MAX_CORE_CONTEXTS; i++) + if (!context_ds[i]) + break; + + if (i == MAX_CORE_CONTEXTS) { + PERR("maximum number of core contexts (%d) reached", MAX_CORE_CONTEXTS); + return Ram_dataspace_capability(); + } + + /* allocate physical memory */ + size = round_page(size); + void *phys_base; + if (!platform_specific()->ram_alloc()->alloc_aligned(size, &phys_base, + get_page_size_log2())) { + PERR("could not allocate backing store for new context"); + return Ram_dataspace_capability(); + } + + context_ds[i] = new (platform()->core_mem_alloc()) + Dataspace_component(size, 0, (addr_t)phys_base, false, true); + + /* + * We do not manage the dataspace via an entrypoint because it will + * only be used by the 'context_area_rm_session'. Therefore, we + * construct a "capability" by hand using the context ID as local + * name. + */ + Native_capability cap; + return reinterpret_cap_cast(Native_capability(cap.dst(), i)); + } + + void free(Ram_dataspace_capability ds) { PDBG("not yet implemented"); } + + int ref_account(Ram_session_capability ram_session) { return 0; } + + int transfer_quota(Ram_session_capability ram_session, size_t amount) { return 0; } + + size_t quota() { return 0; } + + size_t used() { return 0; } +}; + + +/** + * Return single instance of the context-area RM and RAM session + */ +namespace Genode { + + Rm_session *env_context_area_rm_session() + { + static Context_area_rm_session inst; + return &inst; + } + + Ram_session *env_context_area_ram_session() + { + static Context_area_ram_session inst; + return &inst; + } +} diff --git a/base-mb/src/core/core_rm_session.cc b/base-mb/src/core/core_rm_session.cc new file mode 100644 index 000000000..dd5cb249a --- /dev/null +++ b/base-mb/src/core/core_rm_session.cc @@ -0,0 +1,36 @@ +/* + * \brief Core-local RM session + * \author MArtin Stein + * \date 2010-09-09 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include + +using namespace Genode; + + +Rm_session::Local_addr +Core_rm_session::attach(Dataspace_capability ds_cap, size_t size, + off_t offset, bool use_local_addr, + Rm_session::Local_addr local_addr) +{ + Dataspace_component *ds = static_cast(_ds_ep->obj_by_cap(ds_cap)); + if (!ds) + throw Invalid_dataspace(); + + /* roottask is mapped identically */ + return ds->phys_addr(); +} diff --git a/base-mb/src/core/include/core_rm_session.h b/base-mb/src/core/include/core_rm_session.h new file mode 100644 index 000000000..5ddd3af33 --- /dev/null +++ b/base-mb/src/core/include/core_rm_session.h @@ -0,0 +1,52 @@ +/* + * \brief Core-local region manager session + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_RM_SESSION_H_ +#define _CORE__INCLUDE__CORE_RM_SESSION_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +namespace Genode { + + class Core_rm_session : public Rm_session + { + private: + + Rpc_entrypoint *_ds_ep; + + public: + + Core_rm_session(Rpc_entrypoint *ds_ep) : _ds_ep(ds_ep) { } + + Local_addr attach(Dataspace_capability ds_cap, size_t size=0, + off_t offset=0, bool use_local_addr = false, + Local_addr local_addr = 0); + + void detach(Local_addr) { } + + Pager_capability add_client(Thread_capability thread) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability handler) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_RM_SESSION_H_ */ diff --git a/base-mb/src/core/include/cpu/prints.h b/base-mb/src/core/include/cpu/prints.h new file mode 100644 index 000000000..ec68637eb --- /dev/null +++ b/base-mb/src/core/include/cpu/prints.h @@ -0,0 +1,67 @@ +/* + * \brief Saver print methods than the luxury dynamic-number/type-of-arguments one's + * \author Martin Stein + * \date 2010-09-16 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__XMB__PRINTS_H_ +#define _INCLUDE__XMB__PRINTS_H_ + + +#include + + +enum { UART_OUT_REGISTER=0x84000004 }; + + +inline static void _prints_chr1(volatile char chr1) +{ + unsigned volatile* uart = (volatile unsigned*)UART_OUT_REGISTER; + *uart = chr1; +} + + +inline static void _prints_hex2(volatile char hex2) +{ + volatile char hex1 = ((hex2 >> 4) & 0xf); + if (hex1 > 9) hex1 += 39; + hex1 += 48; + _prints_chr1((volatile char)hex1); + + hex1 = hex2 & 0xf; + if (hex1 > 9) hex1 += 39; + hex1 += 48; + _prints_chr1((volatile char)hex1); +} + + +inline static void _prints_hex8(unsigned volatile hex8) +{ + _prints_hex2((volatile char)(hex8 >> 24)); + _prints_hex2((volatile char)(hex8 >> 16)); + _prints_hex2((volatile char)(hex8 >> 8)); + _prints_hex2((volatile char)(hex8 >> 0)); +} + + +inline static void _prints_hex8l(unsigned volatile hex8) +{ + _prints_hex8(hex8); + _prints_chr1('\n'); +} + + +inline static void _prints_str0(const char* volatile str0) +{ + while (*str0) _prints_chr1(*str0++); +} + + +#endif /* _INCLUDE__XMB__PRINTS_H_ */ diff --git a/base-mb/src/core/include/irq_session_component.h b/base-mb/src/core/include/irq_session_component.h new file mode 100644 index 000000000..7e87d5cc9 --- /dev/null +++ b/base-mb/src/core/include/irq_session_component.h @@ -0,0 +1,73 @@ +/* + * \brief IRQ session interface for the Microblaze Kernel + * \author Norman Feske + * \author Martin Stein + * \date 2010-01-30 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ + +#include +#include +#include + +#include + +namespace Genode { + + class Irq_session_component : public Rpc_object, + public List::Element + { + private: + + enum { STACK_SIZE = 4096 }; + + unsigned _irq_number; + Range_allocator *_irq_alloc; + Rpc_entrypoint _entrypoint; + Irq_session_capability _cap; + bool _attached; + + public: + + /** + * Constructor + * + * \param cap_session capability session to use + * \param irq_alloc platform-dependent IRQ allocator + * \param args session construction arguments + */ + Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args); + + /** + * Destructor + */ + ~Irq_session_component(); + + /** + * Return capability to this session + * + * If an initialization error occurs, returned capability is invalid. + */ + Irq_session_capability cap() const { return _cap; } + + + /*************************** + ** Irq session interface ** + ***************************/ + + void wait_for_irq(); + }; +} + +#endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */ diff --git a/base-mb/src/core/include/kernel/print.h b/base-mb/src/core/include/kernel/print.h new file mode 100644 index 000000000..28795f46f --- /dev/null +++ b/base-mb/src/core/include/kernel/print.h @@ -0,0 +1,98 @@ +/* + * \brief Kernels syscall frontend + * \author Martin stein + * \date 2010.07.02 + */ + +#ifndef _INCLUDE__KERNEL__PRINT_H_ +#define _INCLUDE__KERNEL__PRINT_H_ + +#include + +namespace Kernel { + + class Serial_port + { + /** + * Print constant integer < 2^4 as hexadecimal value + * to serial port via syscalls + */ + inline void _print_hex_4(unsigned char x); + + public: + + /** + * Print constant zero-terminated string via syscalls to serial port + */ + inline Serial_port &operator << (char const *s); + + /** + * Print constant integer < 2^32 as hexadecimal value + * to serial port via syscalls (no leading zeros) + */ + inline Serial_port &operator << (unsigned int const &x); + }; + + /** + * Give static 'Serial_port' reference as target for stream operators + */ + inline Serial_port& serial_port(); +} + + +Kernel::Serial_port& Kernel::serial_port() +{ + static Serial_port _sp; + return _sp; +} + + +void Kernel::Serial_port::_print_hex_4(unsigned char x) +{ + x &= 0x0f; + if (x > 9) + x += 39; + + x += 48; + + Kernel::print_char(x); +} + + +Kernel::Serial_port& Kernel::Serial_port::operator << (char const *s) +{ + while (*s) print_char(*s++); + return *this; +} + + +Kernel::Serial_port& Kernel::Serial_port::operator << (unsigned int const &x) +{ + enum{ + BYTE_WIDTH = 8, + CW = sizeof(unsigned char)*BYTE_WIDTH, + IW = sizeof(unsigned int)*BYTE_WIDTH + }; + + bool leading = true; + for (int i = IW - CW; i >= 0; i = i - CW){ + unsigned char c =(char)((x >> i) & 0xff); + if (leading) { + if (c == 0x00) { + if (i == 0) + _print_hex_4(c); + continue; + } + leading = false; + if (c < 0x10) { + _print_hex_4(c); + continue; + } + } + _print_hex_4(c >> 4); + _print_hex_4(c); + } + return *this; +} + +#endif /* _INCLUDE__KERNEL__PRINT_H_ */ diff --git a/base-mb/src/core/include/map_local.h b/base-mb/src/core/include/map_local.h new file mode 100755 index 000000000..217105990 --- /dev/null +++ b/base-mb/src/core/include/map_local.h @@ -0,0 +1,49 @@ +/* + * \brief Core-local mapping + * \author Norman Feske + * \date 2010-02-15 + * + * These functions are normally doing nothing because core's using 1-to-1 paging at kernel + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__CORE__INCLUDE__MAP_LOCAL_H_ +#define _SRC__CORE__INCLUDE__MAP_LOCAL_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + + +namespace Genode { + + /** + * Map physical pages to core-local virtual address range + * + * Always true because roottask pager handles all core page faults + */ + inline bool map_local(addr_t from_phys, addr_t to_virt, size_t num_pages) + { + return true; + } + + /** + * Unmap virtual pages from core-local virtual address range + * + * Does nothing because roottask pager handles all core page faults + */ + inline bool unmap_local(addr_t virt_addr, size_t num_pages) + { + return true; + } +} + +#endif /* _SRC__CORE__INCLUDE__MAP_LOCAL_H_ */ diff --git a/base-mb/src/core/include/platform.h b/base-mb/src/core/include/platform.h new file mode 100755 index 000000000..52d65bc1b --- /dev/null +++ b/base-mb/src/core/include/platform.h @@ -0,0 +1,118 @@ +/* + * \brief Platform interface + * \author Martin Stein + * \date 2010-09-08 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__CORE__INCLUDE__PLATFORM_H_ +#define _SRC__CORE__INCLUDE__PLATFORM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Roottask +{ + enum { + PAGER_TID = User::MIN_THREAD_ID, + + CONTEXT_PAGE_SIZE_LOG2 = Kernel::Utcb::ALIGNMENT_LOG2, + CONTEXT_PAGE_SIZE = 1< Phys_allocator; + + /* + * Core is mapped 1-to-1 physical-to-virtual except for the thread + * context area. mapping out of context area. So a single memory + * allocator suffices for both, assigning physical RAM to + * dataspaces and allocating core-local memory. + */ + Phys_allocator _core_mem_alloc; /* core-accessible memory */ + Phys_allocator _io_mem_alloc; /* MMIO allocator */ + Phys_allocator _io_port_alloc; /* I/O port allocator */ + Phys_allocator _irq_alloc; /* IRQ allocator */ + Rom_fs _rom_fs; /* ROM file system */ + + /** + * Virtual address range usable by non-core processes + */ + addr_t _vm_base; + size_t _vm_size; + + void _optimize_init_img_rom(long int & base, size_t const & size); + + public: + + virtual ~Platform() {} + + /** + * Constructor + */ + Platform(); + + + /******************************** + ** Generic platform interface ** + ********************************/ + + inline Range_allocator *ram_alloc() { return &_core_mem_alloc; } + inline Range_allocator *io_mem_alloc() { return &_io_mem_alloc; } + inline Range_allocator *io_port_alloc() { return &_io_port_alloc; } + inline Range_allocator *irq_alloc() { return &_irq_alloc; } + inline Range_allocator *region_alloc() { return 0; } + + /** + * We need a 'Range_allocator' instead of 'Allocator' as in + * 'Platform_generic' to allocate aligned space for e.g. UTCB's + */ + inline Range_allocator *core_mem_alloc() { return &_core_mem_alloc; } + + inline addr_t vm_start() const { return _vm_base; } + inline size_t vm_size() const { return _vm_size; } + + inline Rom_fs *rom_fs() { return &_rom_fs; } + + inline void wait_for_exit() { sleep_forever(); } + + }; +} + +#endif /* _SRC__CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-mb/src/core/include/platform_pd.h b/base-mb/src/core/include/platform_pd.h new file mode 100755 index 000000000..0eae04423 --- /dev/null +++ b/base-mb/src/core/include/platform_pd.h @@ -0,0 +1,253 @@ +/* + * \brief Protection-domain facility + * \author Martin Stein + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__CORE__INCLUDE__PLATFORM_PD_H_ +#define _SRC__CORE__INCLUDE__PLATFORM_PD_H_ + +/* core includes */ +#include +#include +#include + +namespace Genode { + + class Platform_pd; + + typedef Id_allocator Pid_allocator; + + Pid_allocator *pid_allocator(); + + + class Platform_thread; + class Platform_pd + { + public: + + typedef unsigned Context_id; + typedef Thread_base::Context Context; + + private: + + enum{ + CONTEXT_AREA_BASE = Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + CONTEXT_AREA_SIZE = Thread_base::CONTEXT_AREA_VIRTUAL_SIZE, + CONTEXT_AREA_TOP = CONTEXT_AREA_BASE + CONTEXT_AREA_SIZE, + CONTEXT_SIZE = Thread_base::CONTEXT_VIRTUAL_SIZE, + CONTEXT_BASE_MASK = Thread_base::CONTEXT_VIRTUAL_BASE_MASK, + CONTEXT_OFFSET_MASK = ~CONTEXT_BASE_MASK, + + MAX_CONTEXT_ID = CONTEXT_AREA_SIZE/CONTEXT_SIZE-1 + }; + + Native_process_id _pid; + + Native_thread_id owner_tid_by_context_id[MAX_CONTEXT_ID+1]; + + void _free_context(Native_thread_id const & t) + { + for (Context_id cid = 0; cid <= MAX_CONTEXT_ID; cid++) { + if (owner_tid_by_context_id[cid] == t) { + owner_tid_by_context_id[cid] = 0; + } + } + } + + + public: + + /** + * Constructors + */ + Platform_pd(signed pid = 0, bool create = true) : _pid(pid) + { + static bool const verbose = false; + + if ((unsigned)User::MAX_THREAD_ID>(unsigned)MAX_CONTEXT_ID) { + PERR("More threads allowed than context areas available"); + return; + } + if (!_pid) + _pid=pid_allocator()->allocate(this); + + if (!_pid) { + PERR("Allocating new Process ID failed"); + return; + } + if (verbose) + PDBG("Create protection domain %i", (unsigned int)_pid); + } + + /** + * Destructor + */ + ~Platform_pd() { } + + enum Context_part{ NO_CONTEXT_PART = 0, + MISC_AREA = 1, + UTCB_AREA = 2, + STACK_AREA = 3 }; + + bool cid_if_context_address(addr_t a, Context_id* cid) + { + if (a < CONTEXT_AREA_BASE || a >= CONTEXT_AREA_TOP) + return false; + + addr_t context_base = a & CONTEXT_BASE_MASK; + *cid = (Context_id)((context_base-CONTEXT_AREA_BASE) / CONTEXT_SIZE); + return true; + } + + Context *context(Context_id i) + { + return (Context*)(CONTEXT_AREA_BASE+(i+1)*CONTEXT_SIZE-sizeof(Context)); + } + + Context *context_by_tid(Native_thread_id tid) + { + for (unsigned cid = 0; cid <= MAX_CONTEXT_ID; cid++) + if (owner_tid_by_context_id[cid] == tid) + return context(cid); + + return 0; + } + + bool metadata_if_context_address(addr_t a, Native_thread_id *context_owner_tid, + Context_part *cp, unsigned *stack_offset) + { + Context_id cid; + if (!cid_if_context_address(a, &cid)) + return false; + + if (cid > MAX_CONTEXT_ID) { + PERR("Context ID %i out of range", (unsigned int)cid); + return false; + } + + *context_owner_tid = owner_tid_by_context_id[cid]; + if (!*context_owner_tid) { + if (_pid == Roottask::PROTECTION_ID) + PERR("Context %p is not in use", (void*)a); + + return false; + } + + addr_t offset = a & CONTEXT_OFFSET_MASK; + Context *context = (Context *)(CONTEXT_SIZE - sizeof(Context)); + + if ((void*)offset >= &context->utcb) { + *cp = UTCB_AREA; + + } else if ((void*)offset < &context->stack) { + *cp = STACK_AREA; + *stack_offset = (((unsigned)&(context->stack)) - (unsigned)offset); + } else { + *cp = MISC_AREA; + } + return true; + } + + bool allocate_context(Native_thread_id tid, Context_id cid) + { + static bool const verbose = false; + + if (cid > MAX_CONTEXT_ID) + return 0; + + if (owner_tid_by_context_id[cid]){ + PERR("Context is already in use"); + return false; + } + owner_tid_by_context_id[cid] = tid; + if (verbose) + PDBG("Thread %i owns Context %i (0x%p) of PD %i", + tid, cid, context(cid), _pid); + return true; + } + + Context *allocate_context(Native_thread_id tid) + { + static bool const verbose = false; + + /* + * First thread is assumed to be the main thread and gets last + * context-area by convention + */ + if (!owner_tid_by_context_id[MAX_CONTEXT_ID]){ + owner_tid_by_context_id[MAX_CONTEXT_ID] = tid; + if (verbose) + PDBG("Thread %i owns Context %i (0x%p) of Protection Domain %i", + tid, MAX_CONTEXT_ID, context(MAX_CONTEXT_ID), _pid); + + return context(MAX_CONTEXT_ID); + } + + for (unsigned i = 0; i <= MAX_CONTEXT_ID - 1; i++) { + if (!owner_tid_by_context_id[i]) { + owner_tid_by_context_id[i] = tid; + if (verbose) + PDBG("Thread %i owns Context %i (0x%p) of Protection Domain %i", + tid, MAX_CONTEXT_ID, context(MAX_CONTEXT_ID), _pid); + return context(i); + } + } + return 0; + } + + /** + * Bind thread to protection domain + * + * \return 0 on success + */ + inline int bind_thread(Platform_thread* pt) + { + Context *context = allocate_context(pt->tid()); + if (!context) { + PERR("Context allocation failed"); + return -1; + } + Native_utcb *utcb = &context->utcb; + pt->_assign_physical_thread(_pid, utcb, this); + return 0; + } + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + inline void unbind_thread(Platform_thread *pt) + { + _free_context(pt->tid()); + } + + /** + * Free a context so it is allocatable again + * \param c PD wide unique context ID + */ + void free_context(Context_id const & c) + { + if (c > MAX_CONTEXT_ID) { return; } + owner_tid_by_context_id[c] = Kernel::INVALID_THREAD_ID; + } + + /** + * Assign parent interface to protection domain + */ + inline int assign_parent(Native_capability parent) { return 0; } + }; +} + +#endif /* _SRC__CORE__INCLUDE__PLATFORM_PD_H */ + + + diff --git a/base-mb/src/core/include/platform_thread.h b/base-mb/src/core/include/platform_thread.h new file mode 100755 index 000000000..63412d995 --- /dev/null +++ b/base-mb/src/core/include/platform_thread.h @@ -0,0 +1,162 @@ +/* + * \brief Thread facility + * \author Martin Stein + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _SRC__CORE__INCLUDE__PLATFORM_THREAD_H_ + +#include +#include +#include +#include +#include +#include + +namespace Genode { + + class Platform_thread; + typedef Id_allocator Tid_allocator; + Tid_allocator* tid_allocator(); + + /** + * Base of the physical UTCB belonging to a specific thread + */ + Kernel::Utcb* physical_utcb(Native_thread_id tid); + + + /** + * Set physical UTCB address according to a specific thread ID. + * Useful to propagate UTCB address when allocating the whole context + * at a stretch as e.g. in core + */ + int physical_utcb(Native_thread_id const &tid, Kernel::Utcb * const &utcb); + + + class Platform_pd; + class Platform_thread + { + private: + + friend class Platform_pd; + + Native_thread_id _tid; /* global kernel thread ID */ + Native_process_id _pid; + Native_utcb* _utcb; + Pager_object *_pager; + uint32_t _params; + + /* for debugging purpose only */ + Platform_pd* _pd; + + /** + * Assign physical thread ID and UTCB address to thread + * + * This function is called from 'Platform_pd::bind_thread'. + */ + void _assign_physical_thread(int pid, Native_utcb* utcb, Platform_pd* pd) + { + _utcb = utcb; + _pid = pid; + _pd = pd; + } + + public: + + unsigned pid(){ return (unsigned)_pid; } + unsigned tid(){ return (unsigned)_tid; } + + enum { THREAD_INVALID = -1 }; /* invalid thread number */ + + /** + * Constructor + */ + Platform_thread(const char *name = 0, unsigned priority = 0, + int thread_id = THREAD_INVALID, uint32_t params = 0); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * \param cpu_no target cpu + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp, unsigned int cpu_no = 0); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Set pager capability + */ + inline Pager_object *pager() { return _pager; } + inline void pager(Pager_object *pager) { _pager = pager; } + + /** + * Return identification of thread when faulting + */ + inline unsigned long pager_object_badge() const { return _tid; } + + /** + * Set the executing CPU for this thread. + */ + void set_cpu(unsigned int cpu_no = 0); + + /** + * Get thread name + */ + inline const char *name() const { return "noname"; } + + + /********************* + ** Kernel specific ** + *********************/ + + inline addr_t utcb() const { return (addr_t)_utcb; } + }; +} + +#endif /* _SRC__CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-mb/src/core/include/util.h b/base-mb/src/core/include/util.h new file mode 100755 index 000000000..81b422019 --- /dev/null +++ b/base-mb/src/core/include/util.h @@ -0,0 +1,55 @@ +/* + * \brief Core-internal utilities + * \author Martin Stein + * \date 2010-09-08 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__CORE__INCLUDE__UTIL_H_ +#define _SRC__CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include + +/* Kernel includes */ +#include + +namespace Genode { + + inline size_t get_page_size_log2() { return 12; } + inline size_t get_page_size() { return 1 << get_page_size_log2(); } + inline addr_t get_page_mask() { return ~(get_page_size() - 1); } + inline addr_t trunc_page(addr_t addr) { return addr & get_page_mask(); } + inline addr_t round_page(addr_t addr) { return trunc_page(addr + get_page_size() - 1); } + + inline addr_t map_src_addr(addr_t core_local, addr_t phys) { return phys; } + inline size_t constrain_map_size_log2(size_t size_log2) + { + if (size_log2<14) return 12; + if (size_log2<16) return 14; + if (size_log2<18) return 16; + if (size_log2<20) return 18; + if (size_log2<22) return 20; + if (size_log2<24) return 22; + return 24; + } + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long faulter_badge) + { + printf("%s (%s pf_addr=%p pf_ip=%p from %02lx)\n", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, + faulter_badge); + } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-mb/src/core/include/util/array.h b/base-mb/src/core/include/util/array.h new file mode 100644 index 000000000..ef7830631 --- /dev/null +++ b/base-mb/src/core/include/util/array.h @@ -0,0 +1,21 @@ +/* + * \brief Utils to ease the work with arrays + * \author Martin stein + * \date 2011-03-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__ARRAY_H_ +#define _INCLUDE__UTIL__ARRAY_H_ + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) +#define MAX_ARRAY_ID(array) (ARRAY_SIZE(array)-1) +#define LAST_ARRAY_ELEM(array) array[MAX_ARRAY_ID(array)] + +#endif /* _INCLUDE__UTIL__ARRAY_H_ */ diff --git a/base-mb/src/core/include/util/debug.h b/base-mb/src/core/include/util/debug.h new file mode 100644 index 000000000..d9c562b8e --- /dev/null +++ b/base-mb/src/core/include/util/debug.h @@ -0,0 +1,62 @@ +/* + * \brief Some tools for general purpose debugging + * \author Martin stein + * \date 2011-04-06 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__DEBUG_H_ +#define _INCLUDE__UTIL__DEBUG_H_ + +#include +#include + +namespace Debug { + + /** + * Print out address and the according 32 bit memory-value + * XXX Should print a word instead of a fixed bitwidth XXX + */ + inline void dump(Cpu::addr_t const & a); + + /** + * Print memory-contents of a given area over the local addressspace + * as list with the according addresses in front + */ + inline void dump(Cpu::addr_t const & base, Cpu::size_t const & size, + bool downward = false); +}; + + +void Debug::dump(Cpu::addr_t const & a) { + printf("%8X: %8X", (Cpu::uint32_t)a, *((Cpu::uint32_t*)a)); +} + + +void Debug::dump(Cpu::addr_t const & base, Cpu::size_t const & size, + bool downward) +{ + using namespace Genode; + Cpu::addr_t top = base + size; + + if(!downward) { + for(Cpu::addr_t i=base; i=base;) { + i = i-Cpu::WORD_SIZE; + dump(i); + } +} + +#endif /* _INCLUDE__UTIL__DEBUG_H_ */ + diff --git a/base-mb/src/core/include/util/id_allocator.h b/base-mb/src/core/include/util/id_allocator.h new file mode 100644 index 000000000..b40290e4b --- /dev/null +++ b/base-mb/src/core/include/util/id_allocator.h @@ -0,0 +1,122 @@ +/* + * \brief Allocator for ID-labeled resources + * \author Martin Stein + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__ID_ALLOCATOR_H_ +#define _INCLUDE__UTIL__ID_ALLOCATOR_H_ + +/** + * \param HOLDER_T type that should hold ID's + * \param ID_T type of the allocatable ID's should be + * an enumeration type that expresses a variety + * less than the CPUs word width to the power of 2 + * \param BYTE_WIDTH the CPU's bytewidth + */ +template +class Id_allocator +{ + enum { + ID_WIDTH = sizeof(ID_T)*BYTE_WIDTH, + ID_RANGE = 1 << ID_WIDTH + }; + + ID_T _first_allocatable; + ID_T _last_allocatable; + + bool _id_in_use[ID_RANGE]; + HOLDER_T *_holder_by_id[ID_RANGE]; + + public: + + Id_allocator() : + _first_allocatable(0), + _last_allocatable(ID_RANGE-1) + { + for (unsigned i = _first_allocatable; + i <= _last_allocatable; i++) + + _id_in_use[i]=false; + } + + Id_allocator(ID_T first, ID_T last) : + _first_allocatable(first), + _last_allocatable(last) + { + for (unsigned i = _first_allocatable; + i <= _last_allocatable; i++) + + _id_in_use[i]=false; + } + + ID_T allocate() + { + for (unsigned i = _first_allocatable; + i <= _last_allocatable; i++) { + + if (_id_in_use[i]) + continue; + + _id_in_use[i] = true; + _holder_by_id[i] = 0; + return (ID_T)i; + } + + PERR("All ID's in use"); + return (ID_T)0; + } + + ID_T allocate(HOLDER_T* o) + { + for (unsigned i = _first_allocatable; + i <= _last_allocatable; i++) { + + if (_id_in_use[i]) + continue; + + _id_in_use[i] = true; + _holder_by_id[i] = o; + return (ID_T)i; + } + + PERR("All ID's in use"); + return (ID_T)0; + } + + bool allocate(HOLDER_T *o, ID_T id) + { + if (id < _first_allocatable || id > _last_allocatable) { + PERR("ID unallocatable"); + return false; + } + if (!_id_in_use[id]) { + _id_in_use[id] = true; + _holder_by_id[id] = o; + return true; + } + else{ + PERR("ID in use"); + return false; + } + } + + HOLDER_T *holder(ID_T id) { return _holder_by_id[id]; } + + void free(ID_T id) + { + _id_in_use[id]=false; + _holder_by_id[id]=0; + } +}; + +#endif /*_INCLUDE__UTIL__ID_ALLOCATOR_H_*/ + + diff --git a/base-mb/src/core/include/util/math.h b/base-mb/src/core/include/util/math.h new file mode 100644 index 000000000..9dd852969 --- /dev/null +++ b/base-mb/src/core/include/util/math.h @@ -0,0 +1,40 @@ +/* + * \brief Core-internal utilities + * \author Martin Stein + * \date 2011-03-17 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__MATH_H_ +#define _INCLUDE__UTIL__MATH_H_ + +namespace Math +{ + template + inline T round_up(T v, T rounding_log2); + + template + inline T round_down(T v, T rounding_log2); +} + + +template +T Math::round_up(T v, T rounding_log2) +{ + return round_down(v + (1< +T Math::round_down(T v, T rounding_log2) +{ + return v & ~((1 << rounding_log2) - 1); +} + +#endif /* _INCLUDE__UTIL__MATH_H_ */ diff --git a/base-mb/src/core/include/util/queue.h b/base-mb/src/core/include/util/queue.h new file mode 100644 index 000000000..d2b70e336 --- /dev/null +++ b/base-mb/src/core/include/util/queue.h @@ -0,0 +1,145 @@ +/* + * \brief Queue with first-in first-out semantics + * \author Norman Feske + * \date 2008-08-15 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__QUEUE_H_ +#define _INCLUDE__UTIL__QUEUE_H_ + +namespace Kernel { + + /* + * \param QT queue element type + */ + template + class Queue + { + protected: + + QT *_head; /* oldest element */ + QT *_tail; /* newest element */ + + public: + + class Item + { + protected: + + friend class Queue; + + QT *_next; + + public: + + Item() : _next(0) {} + + ~Item() { } + +// /** +// * Return true is fifo element is enqueued in a fifo +// */ +// bool is_enqueued() { return _next != 0; } + }; + + public: + + QT* head(){ return _head; } + + /** + * Return true if queue is empty + */ + bool empty() { return _tail == 0; } + + /** + * Constructor + * + * Start with an empty list. + */ + Queue(): _head(0), _tail(0) { } + + /** + * Destructor + */ + virtual ~Queue() { } + + /** + * Attach element at the end of the queue + */ + void enqueue(QT *e) + { + e->_next = 0; + + if (empty()) + _tail = _head = e; + else { + _tail->_next = e; + _tail = e; + } + _enqueue__verbose__success(); +} + + /** + * Obtain head element of the queue and remove element from queue + * + * \return head element or 0 if queue is empty + */ + QT *dequeue() + { + QT *result = _head; + + /* check if queue has only one last element */ + if (_head == _tail) + _head = _tail = 0; + else + _head = _head->_next; + + /* mark fifo queue element as free */ + if (result) + result->_next = 0; + + return result; + } + + /** + * Remove element from queue if it is enqueued + */ + void remove(QT *e) + { + QT* current=_head; + QT* predecessor=0; + + if (current!=e){ + while (1){ + predecessor=current; + current=current->_next; + if (current==e || !current) + break; + } + } else { + dequeue(); + return; + } + + if (!current) return; + if (current==_tail) _tail=predecessor; + + predecessor->_next=e->_next; + e->_next=0; +} + + protected: + + virtual void _enqueue__verbose__success(){} + + }; +} + +#endif /* _INCLUDE__UTIL__QUEUE_H_ */ diff --git a/base-mb/src/core/include/xilinx/microblaze.h b/base-mb/src/core/include/xilinx/microblaze.h new file mode 100644 index 000000000..8b9d50ad4 --- /dev/null +++ b/base-mb/src/core/include/xilinx/microblaze.h @@ -0,0 +1,403 @@ +/* + * \brief Implementation of the Microblaze MMU + * \author Martin Stein + * \date 2010-11-08 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__DEVICES__XILINX_MICROBLAZE_H_ +#define _INCLUDE__DEVICES__XILINX_MICROBLAZE_H_ + +#include +#include +#include +#include + +namespace Xilinx { + + struct Microblaze + { + typedef Cpu::uint8_t Protection_id; + typedef Cpu::uint32_t Register; + + class Mmu { + + enum Error { + SUCCESS = 0, + INVALID_ENTRY_ID = -1, + INVALID_PAGE_SIZE = -2, + }; + + enum { + VERBOSE = 0, + USE_PROTECTION_ZONES = 0, + UTLB_SIZE = 64, + + TLBLO_GUARDED_LSHIFT = 0, + TLBLO_MEMCOHER_LSHIFT = 1, + TLBLO_INHIBCACHE_LSHIFT = 2, + TLBLO_WTHROUGH_LSHIFT = 3, + TLBLO_ZONE_LSHIFT = 4, + TLBLO_WRITEABLE_LSHIFT = 8, + TLBLO_EXECUTABLE_LSHIFT = 9, + TLBLO_REALPAGE_LSHIFT = 10, TLBLO_REALPAGE_MASK=0x3fffff, + + TLBHI_USER_LSHIFT = 4, + TLBHI_ENDIAN_LSHIFT = 5, + TLBHI_VALID_LSHIFT = 6, + TLBHI_SIZE_LSHIFT = 7, TLBHI_SIZE_MASK = 0x7, + TLBHI_TAG_LSHIFT = 10, TLBHI_TAG_MASK = 0x3fffff, + }; + + public: + + typedef Cpu::uint8_t Entry_id; + + enum { MAX_ENTRY_ID = UTLB_SIZE - 1, }; + + struct Page + { + typedef Cpu::uint8_t Size_id; + + enum { + MAX_SIZE_LOG2 = 24, + MAX_SIZE_ID = 7, + INVALID_SIZE_ID = MAX_SIZE_ID+1, + }; + + /** + * Translation between the native size ID's and the real memory size + * the page covers + */ + static inline unsigned int size_id_to_size_log2(Size_id const & i); + static inline Size_id size_log2_to_size_id(unsigned int const & size_log2); + }; + + private: + + /** + * Focus further operations on a specific entry + */ + inline void _entry(Entry_id & i); + + /** + * Read basic informations from a specific TLB entry + */ + inline void _entry(Entry_id & i, + Register & tlblo, + Register & tlbhi, + Protection_id & pid); + + /** + * Protection zones constrain access to mappings additionally, + * disable this feature + */ + inline void _disable_protection_zones(); + + public: + + /** + * Constructor + */ + inline Mmu(); + + /** + * Get some informations about a specific TLB entry + */ + inline signed int get_entry(Entry_id & i, Cpu::addr_t & vbase, + Protection_id & pid, unsigned int & size_log2); + /** + * Get all informations about a specific TLB entry + */ + inline signed int get_entry(Entry_id & i, Cpu::addr_t & pb, Cpu::addr_t & vb, + Protection_id & pid, unsigned int & size_log2, + bool & writeable, bool & executable); + + /** + * Overwrite a specific TLB entry with a new resolution + */ + inline signed int set_entry(Entry_id i, Cpu::addr_t const & pb, Cpu::addr_t const & vb, + Protection_id const & pid, unsigned int const & size_log2, + bool const & writeable, bool const & executable); + + /** + * Render a specific TLB entry ineffective + */ + inline void clear_entry(Entry_id i); + + /** + * Maximum available entry ID + */ + static inline Entry_id max_entry_id() { return (Entry_id) MAX_ENTRY_ID; }; + }; + + /** + * Read the current stack-pointer + */ + ALWAYS_INLINE static inline Cpu::addr_t stack_pointer(); + + /** + * Read, write and exchange the current protection ID + */ + static inline Protection_id protection_id(); + static inline void protection_id(Protection_id i); + static inline void protection_id(Protection_id & o, Protection_id n); + + inline Mmu * mmu(); + }; +} + + +/************************************** + * Xilinx::Microblaze implementations * + **************************************/ + +Cpu::addr_t Xilinx::Microblaze::stack_pointer() +{ + Register sp; + asm volatile("add %[sp], r1, r0" :[sp]"=r"(sp)::); + return (Cpu::addr_t)sp; +} + + +Xilinx::Microblaze::Mmu * Xilinx::Microblaze::mmu() { + static Mmu _mmu; + return &_mmu; +} + + +Xilinx::Microblaze::Protection_id Xilinx::Microblaze::protection_id() +{ + Protection_id i; + asm volatile ("mfs %[i], rpid" + : [i] "=r" (i) ::); + return i; +} + + +void Xilinx::Microblaze::protection_id(Protection_id i) +{ + asm volatile ("mts rpid, %[i] \n" + "bri 4" + : [i] "+r" (i) ::); +} + + +void Xilinx::Microblaze::protection_id(Protection_id & o, Protection_id n) +{ + asm volatile ("mfs %[o], rpid \n" + "mts rpid, %[n] \n" + "bri 4" + : [o] "=r" (o), + [n] "+r" (n) ::); +} + + +/******************************************* + * Xilinx::Microblaze::Mmu implementations * + *******************************************/ + +Xilinx::Microblaze::Mmu::Mmu() +{ + if (!USE_PROTECTION_ZONES) { _disable_protection_zones(); } + else { PERR("Protection zones not supported"); } +} + + +void Xilinx::Microblaze::Mmu::_disable_protection_zones() +{ + asm volatile ("addik r31, r0, 0xC0000000 \n" + "mts rzpr, r31 \n" + "bri 4" + ::: "r31" ); +} + + +signed int Xilinx::Microblaze::Mmu::get_entry(Entry_id & i, Cpu::addr_t & vb, + Protection_id & pid, unsigned int & size_log2) +{ + if(i>MAX_ENTRY_ID) { return INVALID_ENTRY_ID; }; + + Protection_id opid = protection_id(); + + /* Read TLB entry */ + asm volatile ("mts rtlbx, %[i] \n" + "bri 4 \n" + "mfs %[vb], rtlbhi \n" + "mfs %[pid], rpid" + : [i] "+r" (i), + [vb] "=r" (vb), + [pid] "=r" (pid) ::); + + protection_id(opid); + + /** + * Decode informations + */ + Page::Size_id const s = (vb & (TLBHI_SIZE_MASK<>TLBHI_SIZE_LSHIFT; + size_log2 = Page::size_id_to_size_log2(s); + + vb = Math::round_down(vb, size_log2); + return SUCCESS; +} + + +signed int Xilinx::Microblaze::Mmu::get_entry(Entry_id & i, Cpu::addr_t & pb, Cpu::addr_t & vb, + Protection_id & pid, unsigned int & size_log2, + bool & writeable, bool & executable) +{ + if(i>MAX_ENTRY_ID) { return INVALID_ENTRY_ID; }; + + Protection_id opid = protection_id(); + + /* Read TLB entry */ + asm volatile ("mts rtlbx, %[i] \n" + "bri 4 \n" + "mfs %[pb], rtlblo \n" + "mfs %[vb], rtlbhi \n" + "mfs %[pid], rpid" + : [i] "+r" (i), + [pb] "=r" (pb), + [vb] "=r" (vb), + [pid] "=r" (pid) ::); + + protection_id(opid); + + /** + * Decode informations + */ + writeable = pb & (1<>TLBHI_SIZE_LSHIFT; + size_log2 = Page::size_id_to_size_log2(s); + + pb = Math::round_down(pb, size_log2); + vb = Math::round_down(vb, size_log2); + return SUCCESS; +} + + +signed int Xilinx::Microblaze::Mmu::set_entry(Entry_id i, Cpu::addr_t const & pb, Cpu::addr_t const & vb, + Protection_id const & pid, unsigned int const & size_log2, + bool const & writeable, bool const & executable) +{ + Protection_id opid; + protection_id(opid, pid); + + /** + * Create TLBLO register value + */ + Register tlblo = (Register)Math::round_down(pb, size_log2); + tlblo |= writeable << TLBLO_WRITEABLE_LSHIFT; + tlblo |= executable << TLBLO_EXECUTABLE_LSHIFT; + + /** + * Create TLBHI register value + */ + Register tlbhi = Math::round_down(vb, size_log2); + tlbhi |= 1 << TLBHI_VALID_LSHIFT; + + Page::Size_id s = Page::size_log2_to_size_id(size_log2); + if(s == Page::INVALID_SIZE_ID) { return INVALID_PAGE_SIZE; } + tlbhi |= ((s & TLBHI_SIZE_MASK) << TLBHI_SIZE_LSHIFT); + + /* Write TLB entry */ + asm volatile ("mts rtlbx, %[i] \n" + "bri 4 \n" + "mts rtlblo, %[tlblo] \n" + "bri 4 \n" + "mts rtlbhi, %[tlbhi] \n" + "bri 4" + : [i] "+r" (i), + [tlblo] "+r" (tlblo), + [tlbhi] "+r" (tlbhi) ::); + + if(VERBOSE) + { + PINF("TLB + %2u[0x%8X..0x%8X) r%c%c\n" + " [0x%8X..0x%8X) 2**%i\n", + pid, Math::round_down(vb, size_log2), + Math::round_down(vb+(1<(pb, size_log2), + Math::round_down(pb+(1<MAX_ENTRY_ID) { return; }; + + Protection_id pid = protection_id(); + + if(VERBOSE) { + Cpu::addr_t page; + Protection_id pid; + unsigned size_log2; + + if(!get_entry(i, page, pid, size_log2)) { + PINF("TLB - %i[0x%8X..0x%8X] 2**%i", + pid, (Cpu::uint32_t)page, + (Cpu::uint32_t)page+(1<MAX_ARRAY_ID(_size_log2_to_size_id)) { return INVALID_SIZE_ID; } + return (unsigned int)_size_log2_to_size_id[size_log2]; +} + + +unsigned int Xilinx::Microblaze::Mmu::Page::size_id_to_size_log2(Size_id const & i) +{ + static unsigned const _size_id_to_size_log2 [MAX_SIZE_ID+1] = + { 10, 12, 14, 16, 18, 20, 22, 24 }; + + if(i>ARRAY_SIZE(_size_id_to_size_log2)-1) { return 0; } + return (unsigned int)_size_id_to_size_log2[i]; +} + +#endif /* _INCLUDE__DEVICES__XILINX_MICROBLAZE_H_ */ + diff --git a/base-mb/src/core/io_mem_session_support.cc b/base-mb/src/core/io_mem_session_support.cc new file mode 100755 index 000000000..6157040a3 --- /dev/null +++ b/base-mb/src/core/io_mem_session_support.cc @@ -0,0 +1,28 @@ +/* + * \brief Implementation of the IO_MEM session interface + * \author Norman Feske + * \author Martin Stein + * \date 2010-09-09 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ } + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ + /* Core memory gets mapped 1:1 except of the context area */ + return base; +} diff --git a/base-mb/src/core/io_port_session_component.cc b/base-mb/src/core/io_port_session_component.cc new file mode 100755 index 000000000..7ded5f2de --- /dev/null +++ b/base-mb/src/core/io_port_session_component.cc @@ -0,0 +1,41 @@ +/* + * \brief Implementation of the IO_PORT session interface + * \author Martin Stein + * \date 2010-09-09 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "io_port_session_component.h" + +using namespace Genode; + + +/************** + ** Port API ** + **************/ + +unsigned char Io_port_session_component::inb(unsigned short) { return 0; } +unsigned short Io_port_session_component::inw(unsigned short) { return 0; } +unsigned Io_port_session_component::inl(unsigned short) { return 0; } + +void Io_port_session_component::outb(unsigned short, unsigned char) { } +void Io_port_session_component::outw(unsigned short, unsigned short) { } +void Io_port_session_component::outl(unsigned short, unsigned) { } + + +/****************************** + ** Constructor / destructor ** + ******************************/ + +Io_port_session_component::Io_port_session_component(Range_allocator *io_port_alloc, + const char *args) +: _io_port_alloc(io_port_alloc) { } + + +Io_port_session_component::~Io_port_session_component() { } diff --git a/base-mb/src/core/irq_session_component.cc b/base-mb/src/core/irq_session_component.cc new file mode 100644 index 000000000..88ef862e4 --- /dev/null +++ b/base-mb/src/core/irq_session_component.cc @@ -0,0 +1,68 @@ +/* + * \brief Implementation of IRQ session component + * \author Norman Feske + * \author Martin Stein + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +using namespace Genode; + +void Irq_session_component::wait_for_irq() +{ + using namespace Xilinx; + if (!_attached) { + if(Kernel::irq_allocate(_irq_number)) { + PERR("Kernel::irq_allocate(%i) failed", _irq_number); + sleep_forever(); + } + _attached = true; + } + Kernel::irq_wait(); +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _entrypoint(cap_session, STACK_SIZE, "irq"), + _attached(false) +{ + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + if (!_irq_alloc || (irq_number == -1)|| + _irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK) + { + PERR("unavailable IRQ %lx requested", irq_number); + return; + } + _irq_number = irq_number; + _entrypoint.activate(); + _cap = Irq_session_capability(_entrypoint.manage(this)); +} + + +Irq_session_component::~Irq_session_component() +{ + _irq_alloc->free((void*)_irq_number, 1); + if (_attached) { + if(Kernel::irq_free(_irq_number)){ + PERR("Kernel::irq_free failed"); + } + } +} + diff --git a/base-mb/src/core/platform.cc b/base-mb/src/core/platform.cc new file mode 100644 index 000000000..b4b817a7b --- /dev/null +++ b/base-mb/src/core/platform.cc @@ -0,0 +1,312 @@ +/* + * \brief Platform interface implementation + * \author Martin Stein + * \date 2010-09-08 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include + +static bool const verbose = 0; + +extern unsigned _program_image_begin; +extern unsigned _program_image_end; + +extern unsigned _boot_modules_meta_start; +extern unsigned _boot_modules_meta_end; + +namespace Roottask +{ + /** + * Entry for the core-pager-thread that handles all + * pagefaults belonging to core-threads. Itself has + * to be paged 1:1 by the kernel. Core pager maps all + * 1:1 except of the thread-context-area + */ + static void pager(); + + static Kernel::Utcb pager_utcb; + static Cpu::word_t pager_stack[Cpu::_4KB_SIZE]; +} + + +Genode::Thread_base::Context * Roottask::physical_context(Genode::Native_thread_id tid) +{ + using namespace Cpu; + using Genode::Thread_base; + + static const unsigned int aligned_size = + Math::round_up(CONTEXT_SIZE, + CONTEXT_PAGE_SIZE_LOG2); + static Thread_base::Context * _context[User::MAX_THREAD_ID]; + + if (tid >= sizeof(_context)/sizeof(_context[0])) { + PERR("Native thread ID out of range"); + return 0; + } + + if(!_context[tid]) { + + /* Allocate new context */ + if(!Genode::platform_specific()-> + core_mem_alloc()-> + alloc_aligned(aligned_size, + (void**)&_context[tid], + CONTEXT_PAGE_SIZE_LOG2)) + { + PERR("Allocate memory for a new stack- and misc-area failed"); + return 0; + } + _context[tid] = (Thread_base::Context*)((addr_t)_context[tid] + + aligned_size - sizeof(Thread_base::Context)); + + /* Synchronize output of 'Genode::physical_utcb' if alignment fits */ + if(Math::round_up((addr_t)&_context[tid]->utcb, + Kernel::Utcb::ALIGNMENT_LOG2)!= + (addr_t)&_context[tid]->utcb) + { + PINF("%8X, %8X", (unsigned)Math::round_up((addr_t)&_context[tid]->utcb, + Kernel::Utcb::ALIGNMENT_LOG2), (unsigned)&_context[tid]->utcb); + + PWRN("Wrong UTCB alignment in context"); + } else { + Genode::physical_utcb(tid, (Kernel::Utcb*)&_context[tid]->utcb); + } + if(verbose) { + PDBG("Context %i: [%p|%p|%p|%p]", tid, + (void*)((addr_t)_context[tid] + sizeof(Thread_base::Context) - aligned_size), + (Thread_base::Context*)((addr_t)_context[tid] - STACK_SIZE), + _context[tid], &_context[tid]->utcb); + } + } + return _context[tid]; +} + + +void Roottask::pager() +{ + using namespace Genode; + using namespace Roottask; + + typedef Platform_pd::Context_part Context_part; + typedef Kernel::Paging::Request Request; + typedef Kernel::Paging::Physical_page Physical_page; + + static Physical_page::size_t context_page_size; + if(Physical_page::size_by_size_log2(context_page_size, CONTEXT_PAGE_SIZE_LOG2)){ + PERR("Invalid page size for thread context area"); + } + + Request *r = (Request*)&pager_utcb; + + while (1) { + unsigned request_length = Kernel::ipc_serve(0); + if (request_length != sizeof(Request)) { + PERR("Invalid request"); + continue; + } + + addr_t pa = 0; + + Physical_page::size_t ps = Physical_page::INVALID_SIZE; + addr_t va = r->virtual_page.address(); + + Native_thread_id context_owner = 0; + Context_part context_part = Platform_pd::NO_CONTEXT_PART; + unsigned stack_offset = 0; + + if (platform_pd()->metadata_if_context_address(va, &context_owner, + &context_part, + &stack_offset)) + { + switch (context_part) { + + case Platform_pd::STACK_AREA: + { + Cpu::word_t* pstack = (Cpu::word_t*)physical_context(context_owner); + pa = (addr_t)(pstack-(stack_offset/sizeof(Cpu::word_t))); + break; + } + + case Platform_pd::UTCB_AREA: + pa = (addr_t)physical_utcb(context_owner); + break; + + case Platform_pd::MISC_AREA: + pa = (addr_t)physical_context(context_owner)->stack; + break; + + default: + PERR("No roottask mapping, " + "vaddr=0x%p, tid=%i, ip=%p\n", + (void*)r->virtual_page.address(), + r->source.tid, + (void*)r->source.ip); + break; + } + ps = context_page_size; + } else { + pa = va; + ps = Physical_page::MAX_VALID_SIZE; + } + + Kernel::tlb_load(pa, va, r->virtual_page.protection_id(), + ps, Physical_page::RWX); + + Kernel::thread_wake(r->source.tid); + } +} + + +void Genode::Platform::_optimize_init_img_rom(long int & base, size_t const & size) +{ + enum { + INIT_TEXT_SEGM_ALIGN_LOG2 = Cpu::_64KB_SIZE_LOG2, + INIT_TEXT_SEGM_ALIGN = 1 << INIT_TEXT_SEGM_ALIGN_LOG2, + ELF_HEADER_SIZE = Cpu::_4KB_SIZE + }; + + /* Preserve old location for now */ + long int const old_base = base; + _core_mem_alloc.remove_range((addr_t)old_base, size); + + /* Search for location where text-segment would be mapable + * with pages of size INIT_TEXT_SEGM_ALIGN */ + if (_core_mem_alloc.alloc_aligned(size + 2*INIT_TEXT_SEGM_ALIGN, + (void**)&base, INIT_TEXT_SEGM_ALIGN_LOG2)) + { + /* Found better location so move */ + base = base + INIT_TEXT_SEGM_ALIGN - ELF_HEADER_SIZE; + memcpy((void*)base, (void*)old_base, size); + _core_mem_alloc.add_range((addr_t)old_base, size); + return; + } + /* Keep old location */ + base = old_base; +} + + +Genode::Platform::Platform() : + _core_mem_alloc(0), + _io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()), + _irq_alloc(core_mem_alloc()), _vm_base(0), _vm_size(0) +{ + + using namespace Roottask; + using namespace Genode; + + _core_mem_alloc.add_range((addr_t)Cpu::RAM_BASE, (size_t)Cpu::RAM_SIZE); + + /*************************************************** + * Avoid allocations on '_core_mem_alloc' since it * + * contains space yet that is in use * + ***************************************************/ + + /* Preserve core's program image range with page-granularity from allocation */ + addr_t const img_base = trunc_page((addr_t)&_program_image_begin); + size_t const img_size = round_page((addr_t)&_program_image_end) - img_base; + _core_mem_alloc.remove_range(img_base, img_size); + + /* Preserve core's context area with page-granularity from allocation */ + addr_t const ctxt_area_base = trunc_page((addr_t)Thread_base::CONTEXT_AREA_VIRTUAL_BASE); + size_t const ctxt_area_size = round_page((addr_t)Thread_base::CONTEXT_AREA_VIRTUAL_BASE); + _core_mem_alloc.remove_range(ctxt_area_base, ctxt_area_size); + + /* Preserve UART MMIO with page-granularity from allocation */ + addr_t const uart_base = trunc_page(User::UART_BASE); + _core_mem_alloc.remove_range(uart_base, get_page_size()); + + /* Format of module meta-data as found in the ROM module image */ + struct Boot_module + { + long name; /* physical address of null-terminated string */ + long base; /* physical address of module data */ + long size; /* size of module data in bytes */ + }; + + addr_t const md_base = (addr_t)&_boot_modules_meta_start; + addr_t const md_top = (addr_t)&_boot_modules_meta_end; + size_t const meta_size = md_top - md_base; + + if (meta_size > get_page_size()) { + PERR("Boot modules header is larger than supported"); + sleep_forever(); + } + Boot_module * module = (Boot_module *)md_base; + Boot_module * init_module=0; + + /* Preserve boot modules from allocation */ + for (; (addr_t)module < md_top; module++) { + const char *name = (const char*)module->name; + + /* Init's module will need allocation because we optimize its location */ + if (!strcmp(name, "init")) + { + init_module = module; + continue; + } + _core_mem_alloc.remove_range(trunc_page(module->base), + round_page(module->size)); + } + _optimize_init_img_rom(init_module->base, init_module->size); + _core_mem_alloc.remove_range(trunc_page(init_module->base), + round_page(init_module->size)); + + /***************************************************************** + * from now on it's save to allocate memory on '_core_mem_alloc' * + *****************************************************************/ + + /* Initialize ROM FS with the given boot modules */ + module = (Boot_module *)md_base; + for (; (addr_t)module < md_top; module++) { + Rom_module *rom_module = new (core_mem_alloc()) + Rom_module(module->base, module->size, (const char*)module->name); + _rom_fs.insert(rom_module); + } + + /* Start the core-pager */ + if(Kernel::thread_create(PAGER_TID, PROTECTION_ID, + Kernel::INVALID_THREAD_ID, + &pager_utcb, + (addr_t)pager, + (addr_t)&pager_stack[sizeof(pager_stack)/sizeof(pager_stack[0])], + true << Kernel::THREAD_CREATE__PARAM__IS_ROOT_LSHIFT)) + { + PERR("Couldn't start cores pager"); + sleep_forever(); + } + + /* Core's mainthread shall be paged by the core-pager */ + Kernel::thread_pager(MAIN_THREAD_ID, PAGER_TID); + + /* Initialze core's remaining allocators */ + _irq_alloc.add_range(User::MIN_IRQ, User::MAX_IRQ-User::MIN_IRQ); + _io_mem_alloc.add_range(User::IO_MEM_BASE, User::IO_MEM_SIZE); + + /* Setup virtual memory for common programs */ + _vm_base=User::VADDR_BASE; + _vm_size=User::VADDR_SIZE - get_page_size(); + + if (verbose) { + PINF("Printing core memory layout summary"); + printf("[_core_mem_alloc]\n"); _core_mem_alloc.raw()->dump_addr_tree(); + printf("[_io_mem_alloc]\n"); _io_mem_alloc.raw()->dump_addr_tree(); + printf("[_irq_alloc]\n"); _irq_alloc.raw()->dump_addr_tree(); + } +} + +void Genode::Core_parent::exit(int exit_value) { } + + diff --git a/base-mb/src/core/platform_thread.cc b/base-mb/src/core/platform_thread.cc new file mode 100755 index 000000000..8ec811109 --- /dev/null +++ b/base-mb/src/core/platform_thread.cc @@ -0,0 +1,183 @@ +/* + * \brief Thread facility + * \author Norman Feske + * \author Martin Stein + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include "include/platform.h" + +static bool const verbose = 0; + +using namespace Genode; + + +Tid_allocator* Genode::tid_allocator() +{ + static Tid_allocator _tida(User::MIN_THREAD_ID+1, User::MAX_THREAD_ID); + return &_tida; +} + + +Pid_allocator* Genode::pid_allocator() +{ + static Pid_allocator _pida(User::MIN_PROTECTION_ID, User::MAX_PROTECTION_ID); + return &_pida; +} + + +namespace Genode +{ + static Kernel::Utcb * phys_utcb[User::MAX_THREAD_ID]; +} + + +int Genode::physical_utcb(Native_thread_id const &tid, Kernel::Utcb * const &utcb) +{ + if (tid < sizeof(phys_utcb)/sizeof(phys_utcb[0])) { + Genode::phys_utcb[tid] = utcb; + return true; + } + return false; +} + + +Kernel::Utcb* Genode::physical_utcb(Native_thread_id tid) +{ + + if (tid >= sizeof(phys_utcb)/sizeof(phys_utcb[0])) { + PERR("Native thread ID out of range"); + return 0; + } + + if(!phys_utcb[tid]) { + if (!platform_specific()-> + core_mem_alloc()-> + alloc_aligned(sizeof(Kernel::Utcb), + (void**)&phys_utcb[tid], + Kernel::Utcb::ALIGNMENT_LOG2)) + { + PERR("Allocate memory for a new UTCB failed"); + return 0; + } + if(verbose) { + PDBG("UTCB %i: [%p|%p]", tid, phys_utcb[tid], + (void*)((addr_t)phys_utcb[tid] + sizeof(Kernel::Utcb))); + } + } + return phys_utcb[tid]; +} + + +void Platform_thread::set_cpu(unsigned int cpu_no) { PERR("not implemented"); } + + +void Platform_thread::cancel_blocking() { PERR("not implemented"); } + + +int Platform_thread::state(Thread_state *state_dst) +{ + PERR("not implemented"); + return -1; +} + + +int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no) +{ + Native_thread_id pager_tid = _pager ? _pager->cap().tid() : 0; + Kernel::Utcb* putcb = physical_utcb(_tid); + + /* Hand over arguments for the thread's bootstrap */ + *((Native_thread_id*)&putcb->byte[0]) = _tid; + if (verbose) { + PDBG("Start Thread, tid=%i, pid=%i, pager=%i", _tid, _pid, pager_tid); + PDBG("vip=0x%p, vsp=%p, vutcb=0x%p", ip, sp, _utcb); + } + int const error = Kernel::thread_create(_tid, _pid, pager_tid, + putcb, (addr_t)ip, (addr_t)sp, 0); + if (error) { + PERR("Kernel::thread_create failed, error=%di", error); + return -1; + } + return 0; +} + + +void Platform_thread::pause() +{ + PDBG("not implemented"); +} + + +void Platform_thread::resume() +{ + PDBG("not implemented"); +} + + +Platform_thread::Platform_thread(const char *name, unsigned, int thread_id, + uint32_t params) +: _tid(0), _utcb(0), _params(params) +{ + if (!_tid) { + if (!(_tid = tid_allocator()->allocate(this))) { + PERR("TID allocation failed"); + return; + } + } + else if (!tid_allocator()->allocate(this, (Native_thread_id)thread_id)) { + PERR("TID allocation failed"); + return; + } +} + + +Platform_thread::~Platform_thread() { + _pd->unbind_thread(this); + if(Kernel::thread_kill(_tid)){ + PERR("Kernel::thread_kill(%i) failed", (unsigned)_tid); + } + tid_allocator()->free(_tid); +} + + +bool Ipc_pager::resolved() +{ + typedef Platform_pd::Context_part Context_part; + typedef Mapping::Physical_page Physical_page; + + addr_t va = (addr_t)_request.virtual_page.address(); + Native_thread_id context_owner = 0; + Context_part context_part = Platform_pd::NO_CONTEXT_PART; + unsigned stack_offset = 0; + + Platform_pd* pd = + pid_allocator()->holder(_request.virtual_page.protection_id()); + + if (!pd) { return false; } + if (!pd->metadata_if_context_address(va, &context_owner, &context_part, + &stack_offset)) + { + return false; + } + + if (context_part != Platform_pd::UTCB_AREA) { return false; } + + Native_utcb * const putcb = physical_utcb(context_owner); + set_reply_mapping(Genode::Mapping(va, (addr_t)putcb, false, + Native_utcb::size_log2(), true)); + + return true; +} diff --git a/base-mb/src/core/ram_session_support.cc b/base-mb/src/core/ram_session_support.cc new file mode 100755 index 000000000..ce03b6fbf --- /dev/null +++ b/base-mb/src/core/ram_session_support.cc @@ -0,0 +1,45 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + + +using namespace Genode; + +static bool const verbose = false; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) +{ + if (verbose) PERR("not implemented"); +} + + +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) +{ + if (verbose) PERR("not implemented"); +} + + +void Ram_session_component::_clear_ds(Dataspace_component *ds) +{ + /* + * We don't have to allocate a core local dataspace to get + * virtual access because core is mapped 1-to-1. (except for + * its context-area) + */ + memset((void *)ds->phys_addr(), 0, ds->size()); +} diff --git a/base-mb/src/core/rm_session_support.cc b/base-mb/src/core/rm_session_support.cc new file mode 100755 index 000000000..9ba4d2254 --- /dev/null +++ b/base-mb/src/core/rm_session_support.cc @@ -0,0 +1,38 @@ +/* + * \brief RM-session implementation + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include + +using namespace Genode; + +static bool const verbose = false; + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + if (verbose) { + PDBG("Flush %i B from [%p,%p)", + (unsigned int)size, + (void*)virt_base, + (void*)((unsigned int)virt_base+size)); + } + + Kernel::Protection_id pid = + tid_allocator()->holder(this->badge())->pid(); + + Kernel::tlb_flush(pid, virt_base, (unsigned int)size); +} diff --git a/base-mb/src/core/target.inc b/base-mb/src/core/target.inc new file mode 100755 index 000000000..407603b9a --- /dev/null +++ b/base-mb/src/core/target.inc @@ -0,0 +1,52 @@ +GEN_CORE_DIR = $(BASE_DIR)/src/core +SPEC_CORE_DIR = $(REP_DIR)/src/core +SPEC_BASE_DIR = $(REP_DIR)/src/base + + +SRC_CC = \ + main.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_roottask.cc \ + thread_bootstrap.cc \ + platform_thread.cc \ + platform.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + core_rm_session.cc \ + dump_alloc.cc \ + context_area.cc + +INC_DIR = $(SPEC_CORE_DIR)/include \ + $(GEN_CORE_DIR)/include + +vpath main.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(SPEC_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath signal_source_component.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath context_area.cc $(SPEC_CORE_DIR) +vpath platform.cc $(GEN_CORE_DIR) +vpath platform_thread.cc $(GEN_CORE_DIR) +vpath thread_roottask.cc $(GEN_CORE_DIR) +vpath thread_bootstrap.cc $(SPEC_BASE_DIR)/thread +vpath thread.cc $(SPEC_BASE_DIR)/thread +vpath irq_session_component.cc $(SPEC_CORE_DIR) diff --git a/base-mb/src/core/target.mk b/base-mb/src/core/target.mk new file mode 100755 index 000000000..88de7e109 --- /dev/null +++ b/base-mb/src/core/target.mk @@ -0,0 +1,10 @@ +TARGET = core +LIBS = kernel_core cxx ipc heap printf_microblaze process pager lock \ + raw_signal raw_server + +STARTUP_LIB = kernel_core +LD_SCRIPT = $(REP_DIR)/src/platform/genode.ld + +SRC_S += boot_modules.s + +include $(PRG_DIR)/target.inc diff --git a/base-mb/src/core/thread_roottask.cc b/base-mb/src/core/thread_roottask.cc new file mode 100755 index 000000000..978fb0edb --- /dev/null +++ b/base-mb/src/core/thread_roottask.cc @@ -0,0 +1,110 @@ +/* + * \brief Implementation of Thread API for roottask + * \author Norman Feske + * \date 2010-09-01 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + +static bool const verbose = 0; + +namespace Roottask { + + Platform_pd* platform_pd() + { + static Platform_pd _pd(PROTECTION_ID); + return &_pd; + } +} + + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() +{ + Platform_pd *pd = Roottask::platform_pd(); + Platform_pd::Context_id cid; + + if (pd->cid_if_context_address((addr_t)_context, &cid)){ + pd->free_context(cid); + } + + if(Kernel::thread_kill(_tid)){ + PERR("Kernel::thread_kill(%i) failed", (unsigned)_tid); + } + tid_allocator()->free(_tid); +} + + +void Thread_base::_thread_start() +{ + myself()->entry(); + PDBG("Thread returned, tid=%i, pid=%i", + myself()->tid(), Roottask::PROTECTION_ID); + + Genode::sleep_forever(); +} + + +void Thread_base::_init_context(Context* context) +{ + _tid=tid_allocator()->allocate(); + Platform_pd *pd = Roottask::platform_pd(); + + Platform_pd::Context_id cid; + if (!pd->cid_if_context_address((addr_t)context, &cid)){ + PERR("Invalid context address 0x%p", context); + return; + } + if (!pd->allocate_context(_tid, cid)){ + PERR("Allocating context %i failed", cid); + return; + } +} + + +void Thread_base::start() +{ + using namespace Genode; + + Native_process_id const pid = Roottask::PROTECTION_ID; + Native_thread_id const pager_tid = Roottask::PAGER_TID; + void * const vsp = &_context->stack; + Native_utcb * const vutcb = &_context->utcb; + Kernel::Utcb * const putcb = physical_utcb(_tid); + void * const vip = (void *)_thread_start; + + if(verbose) { + PDBG("Start Thread, tid=%i, pid=%i, pager=%i", _tid, pid, pager_tid); + PDBG(" vip=0x%p, vsp=%p, vutcb=0x%p", vip, vsp, vutcb); + PDBG(" pip=0x%p, psp=%p, putcb=0x%p", + vip, (void*)Roottask::physical_context(_tid)->stack, putcb); + } + + if (Kernel::thread_create(_tid, pid, pager_tid, + putcb, (addr_t)vip, (addr_t)vsp, + 1 << Kernel::THREAD_CREATE__PARAM__IS_ROOT_LSHIFT)) + { + PERR("Kernel::thread_create failed"); + } +} + + +void Thread_base::cancel_blocking() { PERR("not implemented"); } + diff --git a/base-mb/src/kernel/generic/blocking.cc b/base-mb/src/kernel/generic/blocking.cc new file mode 100755 index 000000000..a5216c9e8 --- /dev/null +++ b/base-mb/src/kernel/generic/blocking.cc @@ -0,0 +1,268 @@ +/* + * \brief Blockings that can prevent a thread from beeing executed + * \author Martin Stein + * \date 2011-02-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include "include/thread.h" +#include +#include + +Kernel::Data_tlb_miss::On_occurence__result Kernel::Data_tlb_miss::on_occurence() +{ + if (!_missing_resolution.virtual_page.valid()) { + _on_occurence__error__virtual_page_invalid(); } + + Event::_populate(); + if (!_missing_resolution.physical_page.valid()) { + _on_occurence__verbose__waiting_for_resolution(); + return EVENT_PENDING; } + + tlb()->add(&_missing_resolution); + _missing_resolution.invalidate(); + return EVENT_PROCESSED; +} + + +void Kernel::Data_tlb_miss::Listener::_resolve_identically(Physical_page::size_t s, + Physical_page::Permissions p) +{ + new (&_resolution->physical_page) + Physical_page(_resolution->virtual_page.address(), s, p); +} + + +Kernel::Instruction_tlb_miss::On_occurence__result Kernel::Instruction_tlb_miss::on_occurence() +{ + if (!_missing_resolution.virtual_page.valid()) + _on_occurence__error__virtual_page_invalid(); + + Event::_populate(); + if (!_missing_resolution.physical_page.valid()) { + _on_occerence__verbose__waiting_for_resolution(); + return EVENT_PENDING; + } + + tlb()->add(&_missing_resolution); + _missing_resolution.invalidate(); + return EVENT_PROCESSED; +} + + +void Kernel::Instruction_tlb_miss::Listener::_resolve_identically(Physical_page::size_t s, + Physical_page::Permissions p) +{ + new (&_resolution->physical_page) + Physical_page(_resolution->virtual_page.address(), s, p); +} + + +void Kernel::Data_tlb_miss::Listener::_on_event() +{ + _on_data_tlb_miss(&_resolution->virtual_page, + _resolution->write_access); +} + + +void Kernel::Instruction_tlb_miss::Listener::_on_event() +{ + _on_instruction_tlb_miss(&_resolution->virtual_page); +} + +extern bool irq_occured[]; + +bool Kernel::Irq::unblock() +{ + Thread * const h = irq_allocator()->holder(_id); + if (!h) { + irq_controller()->ack_irq(_id); + return true; + } + h->handle(_id); + return true; +} + + +bool Kernel::Exception::unblock() +{ + int result = false; + + switch (_id) { + + case INSTRUCTION_TLB_MISS: + + new (&Instruction_tlb_miss::_missing_resolution.virtual_page) + Virtual_page(address(), protection_id()); + + if (Instruction_tlb_miss::on_occurence() == EVENT_PROCESSED) + result = true; + + break; + + case DATA_TLB_MISS: + + new (&Data_tlb_miss::_missing_resolution.virtual_page) + Virtual_page(address(), protection_id()); + + Data_tlb_miss::_missing_resolution.write_access = attempted_write_access(); + + if (Data_tlb_miss::on_occurence() == EVENT_PROCESSED) + result = true; + + break; + + default: + PERR("Unexpected exception %i\n", _id); + halt(); + } + + return result; +} + + +bool Kernel::Syscall::unblock() +{ + switch (_id){ + case PRINT_CHAR: + { + return _source->on_print_char(*_argument_0) == Event::EVENT_PROCESSED ? + true : false; + } + + case THREAD_CREATE: + { + Thread_create::Argument a; + a.tid = (Thread_id)*_argument_0; + a.pid = (Protection_id)*_argument_1; + a.pager_tid = (Thread_id)*_argument_2; + a.utcb = (Utcb*)*_argument_3; + a.vip = (addr_t)*_argument_4; + a.vsp = (addr_t)*_argument_5; + a.is_privileged = + (bool)(*_argument_6&(1<on_thread_create(&a, (Thread_create::Result*)_result_0) == Event::EVENT_PROCESSED ? + true : false; + } + + case THREAD_KILL: + { + Thread_kill::Argument a; + a.tid = (Thread_id)*_argument_0; + + return _source->on_thread_kill(&a, (Thread_kill::Result*)_result_0) == Event::EVENT_PROCESSED ? + true : false; + } + + case THREAD_WAKE: + { + Thread_wake::Argument a; + a.tid = (Thread_id)*_argument_0; + + return _source->on_thread_wake(&a, (Thread_wake::Result*)_result_0) == Event::EVENT_PROCESSED ? + true : false; + } + + case THREAD_SLEEP: + { + return _source->on_thread_sleep() == Event::EVENT_PROCESSED ? + true : false; + } + + case IPC_SERVE: + { + return _source->can_reply_and_get_next_request(*_argument_0, _argument_0); + } + + case IPC_REQUEST: + { + return _source->can_get_reply(thread_factory()->get(*_argument_0), + *_argument_1, + _argument_0); + } + + case TLB_LOAD: + { + using namespace Paging; + + Virtual_page vp((addr_t) *_argument_1, + (Protection_id)*_argument_2); + + Physical_page pp((addr_t) *_argument_0, + (Physical_page::size_t) *_argument_3, + (Physical_page::Permissions)*_argument_4); + + Paging::Resolution r(&vp, &pp); + _source->on_tlb_load(&r); + return true; + } + + case IRQ_ALLOCATE: + { + return _source->irq_allocate((Irq_id)*_argument_0, (int *)_argument_0); + } + + case IRQ_FREE: + { + return _source->irq_free((Irq_id)*_argument_0, (int *)_argument_0); + } + + case IRQ_WAIT: + { + return _source->irq_wait(); + } + + case THREAD_PAGER: + { + _source->on_thread_pager(*_argument_0, *_argument_1); + return true; + } + + case THREAD_YIELD: + { + _source->yield(); + return true; + } + + case TLB_FLUSH: + { + using namespace Paging; + + Virtual_page vp((addr_t) *_argument_1, + (Protection_id)*_argument_0); + _source->on_tlb_flush(&vp, (unsigned)*_argument_2); + return true; + } + + case PRINT_INFO: + { + Thread* t; + if((Thread_id)*_argument_0) { + t=thread_factory()->get((Thread_id)*_argument_0); + } else { + t=thread_factory()->get(_source->tid); + } + if(!t) { return true; } + t->print_state(); + return true; + } + + default: + { + _unblock__warning__unknown_id(); + return false; + } + } +} + + diff --git a/base-mb/src/kernel/generic/include/exception.h b/base-mb/src/kernel/generic/include/exception.h new file mode 100755 index 000000000..d79f5f3c8 --- /dev/null +++ b/base-mb/src/kernel/generic/include/exception.h @@ -0,0 +1,82 @@ +/* + * \brief Handling of concrete set of hardware-exceptions + * \author Martin stein + * \date 2010-06-23 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__CORE__KERNEL__INCLUDE__EXCEPTION_H_ +#define _SRC__CORE__KERNEL__INCLUDE__EXCEPTION_H_ + +#include + +#include "scheduler.h" + +using namespace Genode; + + +/** + * Exception metadata structure + */ +struct Exception +{ + uint32_t cause; + uint32_t status; + uint32_t address; +}; + + +/** + * Virtual class that qualifies heirs be exception handler + */ +class Exception_handler +{ + protected: + + /** + * Enable all hw exceptions and let us be the handler for them + */ + void _alloc_exceptions(); + + /** + * Relieve us of handling any exception + * + * Dissable all exceptions if we are the current handler. + */ + void _free_exceptions(); + + public: + + /** + * Destructor + */ + virtual ~Exception_handler() {} + + /** + * Handle occured exception + * + * \param type type of exception - see xmb/include/config.h + */ + virtual void handle_exception(uint32_t type, uint32_t status, uint32_t address) = 0; +}; + + +/** + * C exception handling, after assembler entry + */ +void handle_exception(); + + +/** + * Clear exception if one is in progress + */ +void _exception_clear(); + + +#endif /* _SRC__CORE__KERNEL__INCLUDE__EXCEPTION_H_ */ diff --git a/base-mb/src/kernel/generic/include/interrupt.h b/base-mb/src/kernel/generic/include/interrupt.h new file mode 100755 index 000000000..bc2b2c1c6 --- /dev/null +++ b/base-mb/src/kernel/generic/include/interrupt.h @@ -0,0 +1,50 @@ +/* + * \brief Common lowlevel interrupt handling + * \author Martin stein + * \date 2010-06-23 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__CORE__KERNEL__INCLUDE__INTERRUPT_H_ +#define _SRC__CORE__KERNEL__INCLUDE__INTERRUPT_H_ + +/* OS includes */ +#include + +/* kernel includes */ +#include + +/* platform includes */ +#include + + +using namespace Genode; + + +/** + * Interrupt handling level 2, calls handler if possible or nop and return + */ +void handle_interrupt(); + + +/** + * Globally enable all interrupts + * + * \param controller interrupt controller that shall be used by handlings + */ +void enable_interrupts(Irq_controller* const controller); + + +/** + * Globally disable all irq's + */ +void disable_interrupt(); + + +#endif /* _SRC__CORE__KERNEL__INCLUDE__INTERRUPT_H_ */ diff --git a/base-mb/src/kernel/generic/include/thread.h b/base-mb/src/kernel/generic/include/thread.h new file mode 100755 index 000000000..2f100bcfe --- /dev/null +++ b/base-mb/src/kernel/generic/include/thread.h @@ -0,0 +1,360 @@ +/* + * \brief Declaration of physical backend to userland thread + * \author Martin stein + * \date 2010-06-24 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__GENERIC__INCLUDE__THREAD_H_ +#define _KERNEL__GENERIC__INCLUDE__THREAD_H_ + + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +extern bool irq_occured[Kernel::MAX_IRQ_ID]; + +namespace Kernel { + + enum { THREAD__VERBOSE = 0, + THREAD__WARNING = 1 }; + + enum { ROOTTASK_PAGE_SIZE = Paging::Physical_page::_4KB }; + + + class Thread : public Scheduler::Client, + public Instruction_tlb_miss::Listener, + public Data_tlb_miss::Listener, + public Syscall::Source, + public Paging::Request::Source + { + public: + + typedef Tlb::Virtual_page Virtual_page; + typedef Tlb::Physical_page Physical_page; + typedef Tlb::Resolution Resolution; + typedef Thread_create::Argument Constructor_argument; + typedef Kernel::Thread_id Thread_id; + typedef Kernel::Protection_id Protection_id; + + void* operator new(size_t, void *addr) { return addr; } + + enum State { INVALID = 0, + READY, + WAIT, + WAIT_IPC_REPLY, + WAIT_IPC_REQUEST }; + + enum Print_mode { BRIEF_STATE, DETAILED_STATE }; + + private: + + Platform_thread _platform_thread; + Thread_id _id; + bool _is_privileged; + Thread_id _pager_tid; + Thread_id _substitute_tid; + State _state; + Paging::Request _paging_request; + bool _waits_for_irq; + bool _any_irq_pending; + bool _irq_pending[Kernel::MAX_IRQ_ID]; + + void _unblock() { _platform_thread.unblock(); } + + void _invalidate() { _state = INVALID; } + + Protection_id _protection_id() { return _platform_thread.protection_id(); } + + addr_t _instruction_pointer() { return _platform_thread.instruction_pointer(); } + + void _sleep() { scheduler()->remove(this); } + + void _yield_after_atomic_operation() + { + _platform_thread.yield_after_atomic_operation(); + } + + inline void _clear_pending_irqs(); + + public: + + inline bool irq_allocate(Irq_id i, int * const result); + inline bool irq_free(Irq_id i, int * const result); + inline bool irq_wait(); + inline void handle(Irq_id const & i); + + void pager_tid(Thread_id ptid){ _pager_tid=ptid; } + + /** + * Constructor + */ + Thread(Constructor_argument* a); + + /** + * Constructing invalid thread without parameters + */ + Thread(); + + /** + * Shows several infos about thread depending on print mode argument + */ + void print_state(); + + + /********************** + ** Simple Accessors ** + **********************/ + + Thread_id thread_id() { return _id; } + bool valid() { return _state != INVALID; } + + + /********************************* + ** Scheduler::Client interface ** + *********************************/ + + int label() { return (int)_id; } + + void _on_instruction_tlb_miss(Virtual_page* accessed_page); + + void _on_data_tlb_miss(Virtual_page* accessed_page, bool write_access); + + void yield() { scheduler()->skip_next_time(this); } + + protected: + + void ipc_sleep() { Scheduler::Client::_sleep(); } + + void ipc_wake() { Scheduler::Client::_wake(); } + + bool _preemptable() + { + if (!platform()->is_atomic_operation((void*)_instruction_pointer())) + return true; + + _yield_after_atomic_operation(); + return false; + } + + bool _permission_to_do_print_char() { return true; } + + bool _permission_to_do_thread_create() + { + return _is_privileged; + } + + bool _permission_to_do_thread_kill() + { + return _is_privileged; + } + +// bool _print_info(){ +// _platform_thread.exec_context()->print_content(2); +// return true; +// } + + bool _permission_to_do_tlb_load(){ return _is_privileged; } + + bool _permission_to_do_tlb_flush(){ return _is_privileged; } + + bool _permission_to_do_thread_pager(Thread_id target_tid) + { + return _is_privileged; + } + + bool _permission_to_do_thread_wake(Thread* target) + { + return _is_privileged + || target->_protection_id()==_protection_id(); + } + + Context *_context() + { + return _platform_thread.unblocked_exec_context(); + } + + + enum { CONSTRUCTOR__VERBOSE__SUCCESS = THREAD__VERBOSE }; + + void _on_data_tlb_miss__warning__invalid_pager_tid(Thread_id pager_tid) + { + if (!THREAD__WARNING) return; + + printf("Warning in Kernel::Thread::_on_data_tlb_miss, invalid pager_tid=%i\n", pager_tid); + } + + void _constructor__verbose__success() + { + if (!CONSTRUCTOR__VERBOSE__SUCCESS) return; + + printf("Kernel::Thread::Thread, new valid thread created, printing state\n"); + Verbose::indent(2); + printf("_utcb=0x%8X, _platform_thread(", (uint32_t)utcb()); + _platform_thread.print_state(); + printf(")\n"); + } + + void _on_instruction_tlb_miss__verbose__roottask_resolution(addr_t v) + { + if (!THREAD__VERBOSE) return; + + printf("Kernel::Thread::_on_instruction_tlb_miss, resoluted 0x%p identically\n", + (void*)v); + } + + void _on_data_tlb_miss__verbose__roottask_resolution(addr_t v) + { + if (!THREAD__VERBOSE) return; + + printf("Kernel::Thread::_on_data_tlb_miss, resoluted 0x%p identically\n", + (void*)v); + } + }; + + + class Thread_factory + { + enum { THREAD_ID_RANGE = 1 << (Platform::BYTE_WIDTH*sizeof(Thread_id)) }; + + Thread thread[THREAD_ID_RANGE]; + bool _steady[THREAD_ID_RANGE]; + + public: + + enum Error { + NO_ERROR = 0, + CANT_KILL_STEADY_THREAD = -1 + }; + + typedef Thread::Constructor_argument Create_argument; + + Thread *create(Create_argument *a, bool steady) + { + if(thread[a->tid].valid()) { return 0; } + _steady[a->tid] = steady; + return new(&thread[a->tid])Thread(a); + } + + Error kill(Thread_id tid) + { + if(_steady[tid]) return CANT_KILL_STEADY_THREAD; + thread[tid].~Thread(); + new (&thread[tid]) Thread(); + return NO_ERROR; + } + + Thread *get(Thread_id id) + { + return thread[id].valid() ? &thread[id] : (Thread*)0; + } + }; + + + Thread_factory* thread_factory(); +} + + +void Kernel::Thread::handle(Irq_id const & i) +{ + if(i>sizeof(_irq_pending)/sizeof(_irq_pending[0])){ + printf("Kernel::Thread::handles(Irq_id i): Error"); + halt(); + } + _irq_pending[i]=true; + _any_irq_pending = true; + + if(!_waits_for_irq) { return; } + + Scheduler::Client::_wake(); + _clear_pending_irqs(); + _waits_for_irq=false; + return; +} + + +bool Kernel::Thread::irq_allocate(Irq_id i, int * const result) +{ + if(!_is_privileged){ + *result = -1; + return true; + }; + + if(i == platform()->timer()->irq_id()){ + *result = -3; + return true; + }; + + if(irq_allocator()->allocate(this, i)){ + *result = -2; + return true; + }; + + *result = 0; + return true; +} + + +bool Kernel::Thread::irq_free(Irq_id i, int * const result) +{ + if(!_is_privileged){ + *result = -1; + return true; + }; + + if(irq_allocator()->free(this, i)){ + *result = -2; + return true; + }; + + *result = 0; + return true; +} + + +bool Kernel::Thread::irq_wait() +{ + if(!_any_irq_pending){ + _waits_for_irq = true; + Scheduler::Client::_sleep(); + return true; + } + _clear_pending_irqs(); + return true; +} + + +void Kernel::Thread::_clear_pending_irqs() +{ + for(unsigned int i=0; iack_irq(i); + _irq_pending[i]=false; + } + } + _any_irq_pending=false; +} + +#endif /* _KERNEL__GENERIC__INCLUDE__THREAD_H_ */ + + + + + + + diff --git a/base-mb/src/kernel/generic/kernel.cc b/base-mb/src/kernel/generic/kernel.cc new file mode 100755 index 000000000..1672c5615 --- /dev/null +++ b/base-mb/src/kernel/generic/kernel.cc @@ -0,0 +1,189 @@ +/* + * \brief Kernel initialization + * \author Martin Stein + * \date 2010-07-22 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include "include/thread.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace Cpu; +using namespace Kernel; + +enum { KERNEL__VERBOSE = 0, + KERNEL__WARNING = 1, + KERNEL__ERROR = 1 }; + + +/* Can be defined via compiler options */ +extern "C" void ROOTTASK_ENTRY(); + +extern Kernel::Exec_context* _userland_context; +extern Utcb* _main_utcb_addr; + +extern int _exit_kernel; + + +Kernel::Thread_factory* Kernel::thread_factory() +{ + static Thread_factory _tf; + return &_tf; +} + + +namespace Kernel { + + static Utcb _roottask_utcb, _idle_utcb; + + void _roottask_thread__verbose__creation(addr_t vip, addr_t vsp, Utcb* vutcb) + { + if (!KERNEL__VERBOSE) return; + printf("Kernel::roottask_thread, roottask thread created, " + "printing constraints\n"); + printf(" vip=0x%8X, vsp=0x%8X, vutcb=0x%8X\n", + (uint32_t)vip, (uint32_t)vsp, (uint32_t)vutcb); + } + + + void idle() + { + while(1); + } + + + Thread *idle_thread() + { + enum{ + IDLE_STACK_WORD_SIZE=32, + IDLE_TID=1, + }; + + static word_t _it_stack[IDLE_STACK_WORD_SIZE]; + static Thread *_it = thread_factory()->get(IDLE_TID); + + if (!_it) { + + Thread_factory::Create_argument itca; + + itca.tid = (Thread_id)IDLE_TID; + itca.pid = (Protection_id)Roottask::PROTECTION_ID; + itca.utcb = &_idle_utcb; + itca.pager_tid = INVALID_THREAD_ID; + itca.vsp = (addr_t)&LAST_ARRAY_ELEM(_it_stack); + itca.vip = (addr_t)&idle; + itca.is_privileged = true; + + _it = thread_factory()->create(&itca, true); + } + return _it; + } + + + Thread *roottask_thread() + { + static word_t _rt_stack[Roottask::MAIN_STACK_SIZE/WORD_SIZE]; + static Thread *_rt = thread_factory()->get(Roottask::MAIN_THREAD_ID); + + if (!_rt) { + + Thread_factory::Create_argument rtca; + + rtca.tid = (Thread_id)Roottask::MAIN_THREAD_ID; + rtca.pid = (Protection_id)Roottask::PROTECTION_ID; + rtca.utcb = &_roottask_utcb; + rtca.pager_tid = INVALID_THREAD_ID; + rtca.vsp = (addr_t)&LAST_ARRAY_ELEM(_rt_stack); + rtca.vip = (addr_t)&ROOTTASK_ENTRY; + rtca.is_privileged = true; + _main_utcb_addr = rtca.utcb; + + _rt = thread_factory()->create(&rtca, false); + if (_rt) + _roottask_thread__verbose__creation( + rtca.vip, rtca.vsp, rtca.utcb); + } + return _rt; + } +} + + +Platform *Kernel::platform() { static Platform _p; return &_p; } + + +Scheduler *Kernel::scheduler() +{ + static bool _init_scheduler = false; + static Scheduler _s = Scheduler(platform(), + platform()->timer(), + idle_thread()); + if(_init_scheduler){ return &_s; } + _s.add(roottask_thread()); + _init_scheduler = true; + return &_s; +} + + +Tlb *Kernel::tlb() { return platform()->tlb(); } + + +Irq_controller * const Kernel::irq_controller() +{ + return platform()->irq_controller(); +} + + +Irq_allocator * const Kernel::irq_allocator() +{ + static Irq_allocator _ia = + Irq_allocator(platform()->irq_controller()); + return &_ia; +} + + +unsigned Kernel::word_width() { return Platform::WORD_WIDTH; } + + +void Kernel::halt() { platform()->halt(); } + + +Kernel::Kernel_entry *Kernel::kernel_entry_event() +{ + static Kernel_entry _ke; + return &_ke; +} + + +Kernel::Kernel_exit *Kernel::kernel_exit_event() +{ + static Kernel_exit _kx; + return &_kx; +} + + +/** + * Kernel main routine, gets called by crt0_kernel.s + */ +extern "C" void _kernel() +{ + kernel_entry_event()->on_occurence(); + + scheduler()->run(); + + kernel_exit_event()->on_occurence(); +} + diff --git a/base-mb/src/kernel/generic/scheduler.cc b/base-mb/src/kernel/generic/scheduler.cc new file mode 100755 index 000000000..f434dea6f --- /dev/null +++ b/base-mb/src/kernel/generic/scheduler.cc @@ -0,0 +1,130 @@ +/* + * \brief Implementation of a round robin scheduler + * \author Martin stein + * \date 2010-06-22 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Generic includes */ +#include +#include +#include +#include "generic/verbose.h" + +/* Platform includes */ +#include + +using namespace Kernel; + +extern unsigned int _current_context_label; + + +Scheduler::Scheduler(Ressource * const r, Scheduling_timer * const t, + Client * const idle_client) +: + _timer(t), + _quota_per_round_per_client(_ms_to_quota(MS_PER_ROUND_PER_CLIENT)), + _ressource(r), + _current_client(0), + _idle_client(idle_client) +{ + _idle_client->_scheduler=this; +} + + +void Scheduler::_schedule() +{ + _last_client=_current_client; + if (_last_client && _last_client != _idle_client) { + _client_queue.enqueue(_last_client); + } + + _current_client=_client_queue.dequeue(); + if (!_current_client){ + _current_client=_idle_client; + } +} + + +Kernel::Scheduler::Client::~Client() +{ + if (_scheduler) + _scheduler->remove(this); +} + + +void Scheduler::run() +{ + if (!_current_client){ + _schedule(); + if (!_current_client) + _run__error__no_ready_client(); + } + + _new_clients = false; + Client* first_client = _current_client; + Client::Context *c = 0; + + while (1) { + + _run__trace__client_checks(); + c = _current_client->_schedulable_context(); + + if (c && _current_client) { + if (_current_client->_quota) { + break; + } else { + _current_client->_earn_quota(_quota_per_round_per_client); + _new_clients = true; + } + } + _schedule(); + + if (_new_clients) { + first_client = _current_client; + _new_clients = false; + } + else if (_current_client == first_client){ + _prep_idle_round(); + } + } + + _current_context_label = (unsigned int)_current_client->label(); + _timer->track_time(_current_client->_quota, this); + _ressource->lock(c); + _run__verbose__success(); +} + + +void Scheduler::add(Client *c) +{ + if (!c) { return; } + if (c == _idle_client) { return; } + if (c->_scheduler == this) { return; } + if (c->_scheduler) { c->_scheduler->remove(c); } + + c->_quota = _quota_per_round_per_client; + c->_scheduler = this; + _client_queue.enqueue(c); + _new_clients = true; +} + + +void Scheduler::remove(Client* c) +{ + if (!c) { return; } + if (c->_scheduler != this) { return; } + if (c == _idle_client) { return; } + + if (_current_client == c) { _current_client = 0; } + else _client_queue.remove(c); + c->_scheduler = 0; + _new_clients = true; +} + diff --git a/base-mb/src/kernel/generic/syscall_events.cc b/base-mb/src/kernel/generic/syscall_events.cc new file mode 100755 index 000000000..fcb702c02 --- /dev/null +++ b/base-mb/src/kernel/generic/syscall_events.cc @@ -0,0 +1,187 @@ +/* + * \brief Syscall handling implementation + * \author Martin Stein + * \date 2011-02-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include "include/thread.h" +#include +#include + +using namespace Kernel; + +namespace Kernel{ + extern Thread* idle_thread(); +} + + +Print_char::On_occurence__result Print_char::on_print_char(char c) +{ + printf("%c", c); + return EVENT_PROCESSED; +} + + +Thread_create::On_occurence__result +Thread_create::on_thread_create(Argument* a, Result* r) +{ + using namespace Thread_create_types; + + if (!_permission_to_do_thread_create()) { + _on_thread_create__warning__failed(); + *r = INSUFFICIENT_PERMISSIONS; + } else { + Thread *t = thread_factory()->create(a, false); + + if (!t) { + _on_thread_create__warning__failed(); + *r=INAPPROPRIATE_THREAD_ID; + } else{ + scheduler()->add(t); + _on_thread_create__verbose__success(t); + *r=SUCCESS; + } + } + return EVENT_PROCESSED; +} + + +Thread_kill::On_occurence__result +Thread_kill::on_thread_kill(Argument *a, Result *r) +{ + using namespace Thread_kill_types; + + if (!_permission_to_do_thread_kill()) { + *r = INSUFFICIENT_PERMISSIONS; + _on_thread_kill__warning__failed(); + } else { + Thread_factory *tf = thread_factory(); + + if (tf->get(a->tid) == this) { + *r = SUICIDAL; + _on_thread_kill__warning__failed(); + } else { + if(tf->kill(a->tid)){ + printf("Warning in Thread_kill::on_thread_kill: Can't kill thread\n"); + }; + *r=SUCCESS; + _on_thread_kill__verbose__success(a->tid); + } + } + return EVENT_PROCESSED; +} + + +Thread_sleep::On_occurence__result Thread_sleep::on_thread_sleep() +{ + Scheduler *s = scheduler(); + + s->remove(s->current_client()); + _on_thread_sleep__verbose__success(); + + return EVENT_PROCESSED; +} + + +Thread_wake::On_occurence__result +Thread_wake::on_thread_wake(Argument *a, Result *r) +{ + using namespace Thread_wake_types; + Thread* t = thread_factory()->get(a->tid); + + if (!t) { + *r = INAPPROPRIATE_THREAD_ID; + _on_thread_wake__warning__failed(); + } else{ + if (!_permission_to_do_thread_wake(t)) { + *r = INSUFFICIENT_PERMISSIONS; + _on_thread_wake__warning__failed(); + } else { + scheduler()->add(t); + *r = SUCCESS; + _on_thread_wake__verbose__success(a->tid); + } + } + return EVENT_PROCESSED; +} + + +//void Print_info::on_print_info(){ +// _print_info(); +//} + + +void Tlb_flush::on_tlb_flush(Paging::Virtual_page* first_page, unsigned size) +{ + if (!_permission_to_do_tlb_flush()) return; + + tlb()->flush(first_page, size); +} + + +void Tlb_load::on_tlb_load(Paging::Resolution* r) +{ + if (!_permission_to_do_tlb_load()) return; + + tlb()->add(r); +} + + +void Thread_pager::on_thread_pager(Thread_id target_tid, Thread_id pager_tid) +{ + if (!_permission_to_do_thread_pager(target_tid)) { + printf("Warning in Kernel::Thread_pager::on_thread_pager, " + "insufficient permissions\n"); + return; + } + + Thread *target = thread_factory()->get(target_tid); + if (target && target != idle_thread()) { + target->pager_tid(pager_tid); + } else { + printf("Warning in Kernel::Thread_pager::on_thread_pager, " + "invalid target thread id\n"); + } +} + + +void Thread_wake::_on_thread_wake__verbose__success(Thread_id tid) +{ + if (!SYSCALL_EVENT__VERBOSE) return; + + printf("Kernel::Thread_wake::on_thread_wake, success, tid=%i\n", tid); +} + + +void Thread_sleep::_on_thread_sleep__verbose__success() +{ + if (!SYSCALL_EVENT__VERBOSE) return; + + printf("Kernel::Thread_sleep::on_thread_sleep, success\n"); +} + + +void Thread_kill::_on_thread_kill__verbose__success(Thread_id tid) +{ + if (!SYSCALL_EVENT__VERBOSE) return; + + printf("Kernel::Thread_kill::on_thread_kill, success, tid=%i\n", tid); +} + + +void Thread_create::_on_thread_create__verbose__success(Thread* t) +{ + if (!SYSCALL_EVENT__VERBOSE) return; + + printf("Kernel::Thread_create::on_thread_create, success, printing constraints\n"); + t->print_state(); +} + diff --git a/base-mb/src/kernel/generic/thread.cc b/base-mb/src/kernel/generic/thread.cc new file mode 100755 index 000000000..660f307b7 --- /dev/null +++ b/base-mb/src/kernel/generic/thread.cc @@ -0,0 +1,117 @@ +/* + * \brief Kernels Userland Thread representation + * \author Martin stein + * \date 2010-06-24 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "include/thread.h" + + +using namespace Kernel; + +extern bool trace_me; + +Thread::Thread(Constructor_argument* a) +: + Syscall::Source(a->utcb, a->tid), + _platform_thread(a->vip, a->vsp, a->pid, this), + _id(a->tid), + _is_privileged(a->is_privileged), + _pager_tid(a->pager_tid), + _substitute_tid(INVALID_THREAD_ID), + _state(READY), + _waits_for_irq(false), + _any_irq_pending(false) +{ + _platform_thread.bootstrap_argument_0( (word_t)utcb() ); + + Instruction_tlb_miss::Listener::_event(_platform_thread.exception()->instruction_tlb_miss()); + Data_tlb_miss::Listener::_event(_platform_thread.exception()->data_tlb_miss()); + + _constructor__verbose__success(); +} + + +Thread::Thread() : Syscall::Source(0, 0) +{ + _invalidate(); +} + + +void Thread::print_state() +{ + printf("Thread ID: %i, pager: %i, substitute: %i, privileged: %c, state: %i\n" + "Context:\n", + _id, _pager_tid, _substitute_tid, _is_privileged ? 'y' : 'n', + _state); + _platform_thread.print_state(); +} + + +void Thread::_on_data_tlb_miss(Virtual_page *accessed_page, bool write_access) +{ + typedef Kernel::Data_tlb_miss::Listener Listener; + using namespace Paging; + + if (_protection_id()==Roottask::PROTECTION_ID & !_pager_tid) { + + Listener::_resolve_identically((Physical_page::size_t)ROOTTASK_PAGE_SIZE, + Physical_page::RW); + _on_data_tlb_miss__verbose__roottask_resolution(accessed_page->address()); + + } else { + Ipc::Participates_dialog * pager = thread_factory()->get(_pager_tid); + if (!pager) { + _on_data_tlb_miss__warning__invalid_pager_tid(_pager_tid); + return; + } else { + Request::Source s = {_id, _instruction_pointer() }; + Request::Access a = write_access ? Request::RW : Request::R; + + _paging_request = Request(accessed_page, s, a); + _send_message(pager, (void*)&_paging_request, + sizeof(_paging_request)); + + _sleep(); + _unblock(); + } + } +} + + +void Thread::_on_instruction_tlb_miss(Virtual_page *accessed_page) +{ + typedef Kernel::Instruction_tlb_miss::Listener Listener; + using namespace Paging; + + if (_protection_id() == Roottask::PROTECTION_ID & !_pager_tid) { + + Listener::_resolve_identically((Physical_page::size_t)ROOTTASK_PAGE_SIZE, + Physical_page::RX); + + _on_instruction_tlb_miss__verbose__roottask_resolution(accessed_page->address()); + + } else { + Ipc::Participates_dialog *pager = thread_factory()->get(_pager_tid); + if (!pager) { + _on_data_tlb_miss__warning__invalid_pager_tid(_pager_tid); + return; + } else{ + Paging::Request::Source s = {_id, _instruction_pointer()}; + _paging_request = Request(accessed_page, s, Request::RX); + + _send_message(pager, (void*)&_paging_request, + sizeof(_paging_request)); + _sleep(); + _unblock(); + } + } +} + diff --git a/base-mb/src/kernel/include/generic/blocking.h b/base-mb/src/kernel/include/generic/blocking.h new file mode 100755 index 000000000..490cc0098 --- /dev/null +++ b/base-mb/src/kernel/include/generic/blocking.h @@ -0,0 +1,417 @@ +/* + * \brief Generic userland execution blockings + * \author Martin Stein + * \date 2010-10-27 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__BLOCKING_H_ +#define _KERNEL__INCLUDE__GENERIC__BLOCKING_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + + enum { DATA_TLB_MISS__VERBOSE = 0, + INSTRUCTION_TLB_MISS__VERBOSE = 0 }; + + + struct Blocking + { + virtual bool unblock() = 0; + virtual ~Blocking() { } + }; + + + class Kernel_exit; + + + /** + * This event is triggered everytime kernels main + * routine is done and returns + */ + Kernel_exit* kernel_exit_event(); + + + /** + * Event occuring when kernel exits execution + */ + struct Kernel_exit : public Event + { + class Listener : public Event::Listener + { + Kernel_exit* _event; + + protected: + + Listener() : _event(kernel_exit_event()) { _event->add(this); } + + virtual ~Listener(){} + + virtual void _on_kernel_exit() = 0; + void _on_event() { _on_kernel_exit(); } + }; + + void add(Listener *l) { Event::_add(l); } + + On_occurence__result on_occurence() + { + _populate(); + return EVENT_PROCESSED; + } + }; + + + class Kernel_entry; + + /** + * This Event triggers everytime kernels main + * routine starts execution + */ + Kernel_entry* kernel_entry_event(); + + /** + * Event occuring when kernel starts to be executed + */ + struct Kernel_entry : public Event + { + class Listener : public Event::Listener + { + Kernel_entry* _event; + + protected: + + Listener() : _event(kernel_entry_event()) { _event->add(this); } + + virtual ~Listener(){} + + virtual void _on_kernel_entry() = 0; + void _on_event() { _on_kernel_entry(); } + }; + + void add(Listener *l) { Event::_add(l); } + + On_occurence__result on_occurence() + { + _populate(); + return EVENT_PROCESSED; + } + }; + + + class Instruction_tlb_miss : public Event + { + friend class Exception; + friend class Listener; + + void _add(Listener *l) { Event::_add(l); } + + public: + + typedef Tlb::Virtual_page Virtual_page; + typedef Tlb::Physical_page Physical_page; + typedef Tlb::Resolution Resolution; + + class Listener : public Event::Listener + { + typedef Instruction_tlb_miss::Resolution Resolution; + + Resolution* _resolution; + + protected: + + typedef Instruction_tlb_miss::Virtual_page Virtual_page; + typedef Instruction_tlb_miss::Physical_page Physical_page; + + virtual ~Listener(){} + + void _resolve_identically(Physical_page::size_t s, + Physical_page::Permissions p); + + virtual void _on_instruction_tlb_miss(Virtual_page* accessed_page) = 0; + + void _on_event(); + + void _event(Instruction_tlb_miss* itm) + { + itm->_add(this); + _resolution = itm->missing_resolution(); + } + + /** + * Write read access for listeners + */ + Physical_page* physical_page() + { + return &_resolution->physical_page; + } + }; + + Resolution *missing_resolution() { return &_missing_resolution; } + + protected: + + Resolution _missing_resolution; + + void _on_occurence__error__virtual_page_invalid() + { + printf("Error in Kernel::Instruction_tlb_miss::on_occurence, " + "virtual page invalid, halt\n"); + halt(); + } + + void _on_occerence__verbose__waiting_for_resolution() + { + if (!INSTRUCTION_TLB_MISS__VERBOSE) return; + + printf("Kernel::Instruction_tlb_miss::on_occurence, " + "leaving unresoluted virtual page, address=0x%p, pid=%i\n", + (void*)_missing_resolution.virtual_page.address(), + (int)_missing_resolution.virtual_page.protection_id() ); +} + + public: + + On_occurence__result on_occurence(); + }; + + + class Data_tlb_miss : public Event + { + friend class Exception; + friend class Listener; + + void _add(Listener* l) {Event::_add(l); } + + public: + + typedef Tlb::Virtual_page Virtual_page; + typedef Tlb::Physical_page Physical_page; + typedef Tlb::Resolution Resolution; + + class Listener : public Event::Listener + { + typedef Data_tlb_miss::Resolution Resolution; + + Resolution* _resolution; + + protected: + + typedef Data_tlb_miss::Virtual_page Virtual_page; + typedef Data_tlb_miss::Physical_page Physical_page; + + virtual ~Listener(){} + + void _resolve_identically(Physical_page::size_t s, + Physical_page::Permissions p); + + virtual void _on_data_tlb_miss(Virtual_page* accessed_page, + bool write_access) = 0; + + void _on_event(); + + void _event(Data_tlb_miss* dtm) + { + dtm->_add(this); + _resolution = dtm->missing_resolution(); + } + + /** + * Write read access for listeners + */ + Physical_page* physical_page() + { + return &_resolution->physical_page; + } + }; + + Resolution* missing_resolution() { return &_missing_resolution; } + + protected: + + Resolution _missing_resolution; + + enum{ ON_OCCURENCE__ERROR = 1 }; + + void _on_occurence__error__virtual_page_invalid() + { + printf("Error in Kernel::Data_tlb_miss::on_occurence, " + "virtual page invalid, halt\n"); + halt(); + } + + void _on_occurence__verbose__waiting_for_resolution() + { + if (!DATA_TLB_MISS__VERBOSE) return; + + printf("Kernel::Data_tlb_miss::on_occurence, " + "leaving unresoluted virtual page, address=0x%p, pid=%i)\n", + (void*)_missing_resolution.virtual_page.address(), + (int)_missing_resolution.virtual_page.protection_id() ); +} + + public: + + On_occurence__result on_occurence(); + }; + + + class Exception : public Instruction_tlb_miss, + public Data_tlb_miss, + public Blocking + { + typedef Kernel::Tlb::Virtual_page Virtual_page; + + protected: + + Exception_id _id; + + virtual Protection_id protection_id()=0; + virtual addr_t address()=0; + virtual bool attempted_write_access()=0; + + public: + + enum { UNBLOCK__WARNING = 1 }; + + enum { UNBLOCK__RETURN__SUCCESS = 0, + UNBLOCK__RETURN__FAILED= - 1}; + + bool unblock(); + + Instruction_tlb_miss *instruction_tlb_miss() { return this; } + + Data_tlb_miss *data_tlb_miss() { return this; } + }; + + + class Irq : public Blocking + { + public: + + enum{ UNBLOCK__WARNING=1 }; + + enum { UNBLOCK__RETURN__SUCCESS = 0, + UNBLOCK__RETURN__FAILED = -1}; + + bool unblock(); + + protected: + + Irq_id _id; + + void _unblock__error__release_irq_failed() + { + printf("Error in Kernel::Irq::unblock, failed to release IRQ, halt\n"); + halt(); + } + + void _unblock__warning__unknown_id() + { + if (!UNBLOCK__WARNING) return; + + printf("Warning in Kernel::Irq::unblock, unexpected _id=%i\n", _id); + } + }; + + + class Syscall : public Blocking + { + public: + + class Source; + + private: + + word_t *_argument_0, + *_argument_1, + *_argument_2, + *_argument_3, + *_argument_4, + *_argument_5, + *_argument_6, + *_result_0; + + Source* _source; + + protected: + + Syscall_id _id; + + enum{ UNBLOCK__WARNING = 1 }; + + void _unblock__warning__unknown_id() + { + if (!UNBLOCK__WARNING) return; + + printf("Warning in Kernel::Syscall::unblock, unexpected _id=%i\n", _id); + } + + public: + + class Source : public Print_char, + public Thread_create, + public Thread_sleep, + public Thread_kill, + public Thread_wake, + public Thread_pager, + public Ipc::Participates_dialog, + public Tlb_load, + public Tlb_flush, + public Thread_yield +// public Print_info + { + protected: + + Source(Utcb *utcb, Thread_id i) : + Ipc::Participates_dialog(utcb), + tid(i) + { } + + public: + + Thread_id tid; + + virtual bool irq_allocate(Irq_id i, int * const result)=0; + virtual bool irq_free(Irq_id i, int * const result)=0; + virtual bool irq_wait()=0; + }; + + bool unblock(); + + Syscall(word_t* argument_0, + word_t* argument_1, + word_t* argument_2, + word_t* argument_3, + word_t* argument_4, + word_t* argument_5, + word_t* argument_6, + word_t* result_0, + Source* s) + : + _argument_0(argument_0), + _argument_1(argument_1), + _argument_2(argument_2), + _argument_3(argument_3), + _argument_4(argument_4), + _argument_5(argument_5), + _argument_6(argument_6), + _result_0(result_0), + _source(s) + { } + }; +} + +#endif /* _KERNEL__INCLUDE__GENERIC__BLOCKING_H_ */ diff --git a/base-mb/src/kernel/include/generic/event.h b/base-mb/src/kernel/include/generic/event.h new file mode 100755 index 000000000..201bb1fb5 --- /dev/null +++ b/base-mb/src/kernel/include/generic/event.h @@ -0,0 +1,103 @@ +/* + * \brief Event throwers and listeners + * \author Martin Stein + * \date 2010-10-27 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__EVENT_H_ +#define _KERNEL__INCLUDE__GENERIC__EVENT_H_ + +#include +#include + +namespace Kernel { + + class Event + { + public: + + class Listener; + typedef Kernel::Queue Listener_queue; + + enum On_occurence__result{ EVENT_PROCESSED, EVENT_PENDING }; + + private: + + Listener_queue _listeners; + Listener *_first; + + protected: + + void _populate() + { + if (!_first) { + _first = _listeners.dequeue(); + if (!_first) + return; + } + Listener *i = _first; + + while (1) { + i->_on_event(); + _listeners.enqueue(i); + i = _listeners.dequeue(); + if (i == _first) break; + } + } + + void _add(Listener* l) { _listeners.enqueue(l); } + + void _remove(Listener* l) { _listeners.remove(l); } + + void print_listeners() + { + printf("print_listeners\n"); + + if (_listeners.empty()) { + printf(" empty\n"); + return; } + + Listener *current; + Listener *first; + first = _listeners.head(); + current = _listeners.dequeue(); + printf(" "); + + while (1) { + printf("0x%p", current); + _listeners.enqueue(current); + if (first == _listeners.head()) + break; + current = _listeners.dequeue(); + printf(" → "); } + + printf("\n"); + } + + public: + + virtual ~Event() { } + + class Listener : public Listener_queue::Item + { + friend class Event; + + protected: + + virtual void _on_event() = 0; + + public: + + virtual ~Listener(){} + }; + }; +} + +#endif /*_KERNEL__INCLUDE__GENERIC__EVENT_H_*/ diff --git a/base-mb/src/kernel/include/generic/ipc.h b/base-mb/src/kernel/include/generic/ipc.h new file mode 100755 index 000000000..ca94a3492 --- /dev/null +++ b/base-mb/src/kernel/include/generic/ipc.h @@ -0,0 +1,323 @@ +/* + * \brief IPC Framework inside kernel + * \author Martin Stein + * \date 2011-02-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__IPC_H_ +#define _KERNEL__INCLUDE__GENERIC__IPC_H_ + +#include + + +namespace Kernel { + + enum { IPC__VERBOSE = 0 }; + + namespace Ipc { + + class Participates_dialog; + typedef Kernel::Queue Participant_queue; + + class Participates_dialog : + public Participant_queue::Item + { + typedef Participates_dialog Participant; + + public: + + typedef Kernel::Utcb Utcb; + + inline unsigned message_size() { return _message_size; } + + inline byte_t message(unsigned i) { return _message[i]; } + + inline void print_message(); + + byte_t *_message; + + private: + + Participant_queue _announced_clients; + Participant* _current_client; + Utcb* _utcb; + unsigned _message_size; + bool _waiting_for_reply; + bool _recieved_reply; + + inline void _recieve_message(Participant* sender); + + protected: + + inline void _send_message(Participant* server, + void* message, + unsigned size); + + inline Participates_dialog(Utcb* utcb); + + inline Utcb* utcb() { return _utcb; } + + inline void recieve_reply(Participant* server); + + inline void announce_client(Participant* client); + + virtual ~Participates_dialog() { } + + virtual void ipc_sleep() = 0; + + virtual void ipc_wake() = 0; + + public: + + inline bool can_get_reply(Participant *server, + unsigned request_size, + unsigned *reply_size); + + inline bool can_reply_and_get_next_request(unsigned reply_size, + unsigned* request_size); + + protected: + + inline void _can_get_reply__error__invalid_server(); + + inline void _recieve_message__error__invalid_message_size(); + + inline void _can_reply_and_get_request__verbose__replied_to_request(); + + inline void _can_reply_and_get_request__verbose__recieved_request(); + + inline void _can_reply_and_get_request__verbose__waiting_for_request(); + + inline void _send_message__verbose__success(Participant* server); + + inline void _can_get_reply__verbose__waiting_for_reply(Participant* server); + + inline void _can_get_reply__verbose__recieved_reply(Participant* server); + }; + } +} + + +Kernel::Ipc::Participates_dialog::Participates_dialog(Utcb* utcb) : + _current_client(0), + _utcb(utcb), + _waiting_for_reply(false), + _recieved_reply(false) +{ } + + +void Kernel::Ipc::Participates_dialog::print_message() +{ + printf(" _message=0x%p\n", _message); + + for (unsigned current_byte=0;;){ + printf(" offset 0x%2X: 0x%p -> 0x", + current_byte, &_message[current_byte]); + + printf("%2X", _message[current_byte]); + if (++current_byte>=_message_size) break; + + printf("%2X", _message[current_byte]); + if (++current_byte>=_message_size) break; + + printf("%2X", _message[current_byte]); + if (++current_byte>=_message_size) break; + + printf("%2X", _message[current_byte]); + printf("\n"); + if (++current_byte>=_message_size) break; + } +} + + +void Kernel::Ipc::Participates_dialog::_can_get_reply__error__invalid_server() +{ + printf("Error in Kernel::Ipc::Participates_dialog::can_get_reply, " + "invalid server, halt\n"); + halt(); +} + + +void Kernel::Ipc::Participates_dialog::_recieve_message__error__invalid_message_size() +{ + printf("Error in Kernel::Ipc::Participates_dialog::recieve_message, " + "invalid message size, halt"); + Kernel::halt(); +} + + +void Kernel::Ipc::Participates_dialog::_can_reply_and_get_request__verbose__replied_to_request() +{ + if (!IPC__VERBOSE) return; + + + printf("Kernel::Ipc::Participates_dialog::can_reply_and_get_request, " + "replied to request, this=0x%p, _current_client=0x%p, " + "_message_size=%i\n", + this, _current_client, _message_size); +} + + +void Kernel::Ipc::Participates_dialog::_can_reply_and_get_request__verbose__recieved_request() +{ + if (!IPC__VERBOSE) return; + + printf("Kernel::Ipc::Participates_dialog::can_reply_and_get_request, " + "recieved request, this=0x%p, _current_client=0x%p, " + "_message_size=%i\n", + this, _current_client, _message_size); + + if (IPC__VERBOSE >= 2) + print_message(); +} + + +void Kernel::Ipc::Participates_dialog::_can_reply_and_get_request__verbose__waiting_for_request() +{ + if (!IPC__VERBOSE) return; + + printf("Kernel::Ipc::Participates_dialog::can_reply_and_get_request, " + "waiting for request, this=0x%p\n", + this); +} + + +void Kernel::Ipc::Participates_dialog::_send_message__verbose__success(Participant* server) +{ + if (!IPC__VERBOSE) return; + + printf("Kernel::Ipc::Participates_dialog::send_message, " + "this=0x%p, server=0x%p, _message_size=%i, print message\n", + this, server, _message_size); + + if (IPC__VERBOSE >= 2) + print_message(); +} + + +void Kernel::Ipc::Participates_dialog::_can_get_reply__verbose__waiting_for_reply(Participant* server) +{ + if (!IPC__VERBOSE) return; + + printf("Kernel::Ipc::Participates_dialog::can_get_reply, waiting for reply, " + "this=0x%p, server=0x%p, _message_size=%i\n", + this, server, _message_size); +} + + +void Kernel::Ipc::Participates_dialog::_can_get_reply__verbose__recieved_reply(Participant* server) +{ + if (!IPC__VERBOSE) return; + + printf("Kernel::Ipc::Participates_dialog::can_get_reply, recieved reply, " + "this=0x%p, server=0x%p, _message_size=%i\n", + this, server, _message_size); +} + + +void Kernel::Ipc::Participates_dialog::_send_message(Participant* server, + void* message, + unsigned size) +{ + _message_size = size; + _message = (byte_t*)message; + + server->announce_client(this); + _send_message__verbose__success(server); +} + + +bool Kernel::Ipc::Participates_dialog::can_reply_and_get_next_request(unsigned reply_size, + unsigned* request_size) +{ + if (_current_client) { + _message_size = reply_size; + _message = (byte_t*)&_utcb->byte[0]; + + _can_reply_and_get_request__verbose__replied_to_request(); + + _current_client->recieve_reply(this); + _current_client = 0; + } + + _current_client=_announced_clients.dequeue(); + if (!_current_client) { + _can_reply_and_get_request__verbose__waiting_for_request(); + return false; + } else{ + _recieve_message(_current_client); + *request_size = _message_size; + _can_reply_and_get_request__verbose__recieved_request(); + return true; + } +} + + +void Kernel::Ipc::Participates_dialog::_recieve_message(Participant* sender) +{ + if (sender->message_size() > sizeof(Utcb)) + _recieve_message__error__invalid_message_size(); + + _message_size = sender->message_size(); + _message = (byte_t*)&_utcb->byte[0]; + + for (unsigned current_byte = 0; current_byte < _message_size; current_byte++) + _message[current_byte] = + sender->message(current_byte); +} + + +void Kernel::Ipc::Participates_dialog::announce_client(Participant* client) +{ + _announced_clients.enqueue(client); +} + + +void Kernel::Ipc::Participates_dialog::recieve_reply(Participant* server) +{ + if (!_waiting_for_reply || _recieved_reply) + return; + + _recieve_message(server); + _recieved_reply = true; +} + + +bool Kernel::Ipc::Participates_dialog::can_get_reply(Participant * server, + unsigned request_size, + unsigned * reply_size) +{ + if (!_waiting_for_reply) { + + if (!server) + _can_get_reply__error__invalid_server(); + + _message_size = request_size; + _message = (byte_t*)&_utcb->byte[0]; + _recieved_reply = false; + _waiting_for_reply = true; + + server->announce_client(this); + } + + if (!_recieved_reply) { + _can_get_reply__verbose__waiting_for_reply(server); + return false; + } else { + _can_get_reply__verbose__recieved_reply(server); + + _waiting_for_reply = false; + *reply_size = _message_size; + return true; + } +} + + +#endif /* _KERNEL__INCLUDE__GENERIC__IPC_H_ */ diff --git a/base-mb/src/kernel/include/generic/irq_controller.h b/base-mb/src/kernel/include/generic/irq_controller.h new file mode 100755 index 000000000..1fbcf1d6b --- /dev/null +++ b/base-mb/src/kernel/include/generic/irq_controller.h @@ -0,0 +1,153 @@ +/* + * \brief Interface for irq controllers + * \author Martin stein + * \date 2010-06-21 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__IRQ_CONTROLLER_H_ +#define _KERNEL__INCLUDE__GENERIC__IRQ_CONTROLLER_H_ + +#include +#include +#include +#include + + +namespace Kernel { + + enum { + IRQ_CONTROLLER__VERBOSE = 0, + BYTE_WIDTH=8, + }; + + + template + class Irq_controller_tpl : public DEVICE_T + { + protected: + + /************************* + * Kernel_exit interface * + *************************/ + + inline void _on_kernel_exit(); + + public: + + /** + * Returns occured IRQ ID with highest priority and masks it + */ + inline Irq_id get_irq(); + + /** + * Release IRQ and unmask it + */ + inline void ack_irq(Irq_id const & i); + + Irq_controller_tpl(typename DEVICE_T::Constr_arg const & dca); + }; + + typedef Irq_controller_tpl Irq_controller; + + class Irq_allocator : + public Id_allocator + { + typedef Id_allocator Allocator; + + Irq_controller * const _controller; + + public: + + /** + * Error-codes that are returned by members + */ + enum Error { + NO_ERROR = 0, + HOLDER_DOESNT_OWN_IRQ = -1, + IRQ_IS_PENDING_YET = -2, + ALLOCATOR_ERROR = -3 + }; + + /** + * Constructor + */ + Irq_allocator(Irq_controller * const ic) : + _controller(ic) + { } + + /** + * Free IRQ if the TID-according thread owns it + */ + inline Error free(Thread * const t, Irq_id irq); + + /** + * Free IRQ if the TID-according thread owns it + */ + inline Error allocate(Thread * const t, Irq_id irq); + }; + + /** + * Pointer to kernels static IRQ allocator + */ + Irq_allocator * const irq_allocator(); + + /** + * Pointer to kernels static IRQ controller + */ + Irq_controller * const irq_controller(); +} + + +template +Kernel::Irq_controller_tpl::Irq_controller_tpl(typename DEVICE_T::Constr_arg const & dca) : + DEVICE_T(dca) +{ } + + +template +Kernel::Irq_id Kernel::Irq_controller_tpl::get_irq() +{ + Irq_id const i = DEVICE_T::next_irq(); + DEVICE_T::mask(i); + return i; +} + + +template +void Kernel::Irq_controller_tpl::ack_irq(Irq_id const & i) +{ + DEVICE_T::release(i); + DEVICE_T::unmask(i); +} + + +Kernel::Irq_allocator::Error +Kernel::Irq_allocator::allocate(Thread * const t, Irq_id irq) +{ + if (_controller->pending(irq)) { return IRQ_IS_PENDING_YET; } + + if (!Allocator::allocate(t, irq)) { return ALLOCATOR_ERROR; } + + _controller->unmask(irq); + return NO_ERROR; +}; + + +Kernel::Irq_allocator::Error +Kernel::Irq_allocator::free(Thread * const t, Irq_id irq) +{ + if (_controller->pending(irq)) { return IRQ_IS_PENDING_YET; } + + Allocator::free(irq); + _controller->mask(irq); + return NO_ERROR; +}; + +#endif /* _KERNEL__INCLUDE__GENERIC__IRQ_CONTROLLER_H_ */ diff --git a/base-mb/src/kernel/include/generic/printf.h b/base-mb/src/kernel/include/generic/printf.h new file mode 100755 index 000000000..072371023 --- /dev/null +++ b/base-mb/src/kernel/include/generic/printf.h @@ -0,0 +1,21 @@ +/* + * \brief Import printf function + * \author Martin Stein + * \date 2010-10-12 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__PRINTF_H_ +#define _KERNEL__INCLUDE__GENERIC__PRINTF_H_ + +#include + +using Genode::printf; + +#endif /* _KERNEL__INCLUDE__GENERIC__PRINTF_H_ */ diff --git a/base-mb/src/kernel/include/generic/scheduler.h b/base-mb/src/kernel/include/generic/scheduler.h new file mode 100755 index 000000000..931f81cd4 --- /dev/null +++ b/base-mb/src/kernel/include/generic/scheduler.h @@ -0,0 +1,372 @@ +/* + * \brief Declaration of a round robin scheduler + * \author Martin stein + * \date 2010-06-25 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__GENERIC__INCLUDE__SCHEDULER_H_ +#define _KERNEL__GENERIC__INCLUDE__SCHEDULER_H_ + +/* generic includes */ +#include +#include + +/* util includes */ +#include + +namespace Kernel { + + enum { SHOW_SCHEDULING = 0 }; + + enum { SCHEDULER__TRACE = 1, + SCHEDULER__VERBOSE = 0, + SCHEDULER__ERROR = 1, + SCHEDULER__WARNING = 1 }; + + class Exec_context; + class Platform; + + class Scheduler : public Tracks_time + { + enum { MS_PER_ROUND_PER_CLIENT = SCHEDULING_MS_INTERVAL, + SCHEDULE__VERBOSE__SUCCESS = SCHEDULER__VERBOSE, + SCHEDULE__ERROR = SCHEDULER__ERROR }; + + public: + + typedef Scheduling_timer Timer; + typedef unsigned int Quota; + typedef Kernel::Platform Ressource; + + private: + + Timer * const _timer; + const Quota _quota_per_round_per_client; + Ressource* _ressource; + bool _new_clients; + + void _schedule(); + + inline Quota _ms_to_quota(unsigned int const &ms); + + /** + * Utilise idle client as current client + */ + inline void _prep_idle_round(); + + public: + + inline void time_consumed(Quota const & q); + + enum { CLIENT__WARNING = 1, + CLIENT__VERBOSE = SCHEDULER__VERBOSE }; + + class Client_queue; + + class Client : public Kernel::Queue::Item + { + friend class Scheduler; + friend class Client_queue; + + typedef Kernel::Scheduler::Quota Quota; + + Quota _quota; + Scheduler *_scheduler; + bool _sleeping; + + protected: + + typedef Kernel::Exec_context Context; + + private: + + inline Quota _consume(Quota const &consumed); + inline void _earn_quota(Quota const &q); + inline Context *_schedulable_context(); + + protected: + + enum{ SCHEDULABLE_context__VERBOSE = 1 }; + + inline Client(); + + virtual ~Client(); + + inline void _sleep(); + inline void _wake(); + + virtual Context *_context() = 0; + virtual bool _preemptable() = 0; + + public: + + virtual int label() = 0; + }; + + + struct Client_queue : public Kernel::Queue + { + inline void print_state(); + }; + + private: + + Client_queue _client_queue; + Client* _current_client; + Client* _last_client; + Client* _idle_client; + + public: + + enum{ ADD__VERBOSE = SCHEDULER__VERBOSE||SHOW_SCHEDULING, + REMOVE__VERBOSE = SCHEDULER__VERBOSE||SHOW_SCHEDULING, + RUN__VERBOSE = SCHEDULER__VERBOSE||SHOW_SCHEDULING }; + + /** + * Constructor + * \param r Ressource that is shared by the clients + * \param t Timer to measure exclusive access duration + * \param idle_client this client gets scheduled if there's + * no other client, it can't be removed + */ + Scheduler(Ressource* r, Scheduling_timer * const t, Client* idle_client); + + void add(Client* c); + void remove(Client* c); + void run(); + + inline Client* current_client(); + + inline void skip_next_time(Client* c); + + protected: + + /* debugging */ + inline void _print_clients_via_labels(); + inline void _run__verbose__success(); + inline void _run__error__no_ready_client(); + inline void _run__trace__client_checks(); + inline void _schedule__error__no_clients(); + inline void _schedule__verbose__success() ; + inline void _remove__warning__invalid_client(); + inline void _remove__verbose__success(Client* c); + inline void _remove__trace(Client *c); + inline void _add__warning__invalid_client(); + inline void _add__verbose__success(); + }; + + /** + * Pointer to kernels static scheduler for execution time + */ + Scheduler *scheduler(); +} + + +/*********************************** + ** Kernel::Scheduler definitions ** + ***********************************/ + + +void Kernel::Scheduler::_prep_idle_round() +{ + if(_current_client) { _client_queue.enqueue(_current_client); } + _current_client=_idle_client; +} + + +void Kernel::Scheduler::time_consumed(Quota const & q) +{ + _current_client->_consume(q); +} + + +Kernel::Scheduler::Client* Kernel::Scheduler::current_client() +{ + return _current_client; +} + + +void Kernel::Scheduler::skip_next_time(Client *c) { c->_quota=0; } + + +Kernel::Scheduler::Quota Kernel::Scheduler::_ms_to_quota(unsigned int const & ms) +{ + return _timer->msec_to_native(ms); +} + + +void Kernel::Scheduler::_print_clients_via_labels() +{ + printf("scheduled "); + _last_client ? printf("%i", _last_client->label()) + : printf("ø"); + printf("→"); + _current_client ? printf("%i", _current_client->label()) + : printf("ø"); + printf(", queue "); + _client_queue.print_state(); +} + + +void Kernel::Scheduler::_run__verbose__success() +{ + if (!RUN__VERBOSE) + return; + + printf("Kernel::Scheduler::run, "); + _print_clients_via_labels(); + printf("\n"); +} + + +void Kernel::Scheduler::_run__error__no_ready_client() +{ + if (!SCHEDULER__ERROR) return; + + printf("Error in Kernel::Scheduler::run, no client is ready, halt\n"); + halt(); +} + + +void Kernel::Scheduler::_remove__trace(Client* c) +{ + if (SCHEDULER__TRACE && Verbose::trace_current_kernel_pass()) + printf("rm(%i) ", c->label()); +} + + +void Kernel::Scheduler::_run__trace__client_checks() +{ + if (SCHEDULER__TRACE && Verbose::trace_current_kernel_pass()) + printf("ask(%i,%i) ", + _current_client->label(), _current_client->_quota); +} + + +void Kernel::Scheduler::_schedule__error__no_clients() +{ + if (!SCHEDULER__ERROR) return; + + printf("Error in Kernel::Scheduler::_schedule, no clients registered, halt\n"); + halt(); +} + + +void Kernel::Scheduler::_remove__warning__invalid_client() +{ + if (!SCHEDULER__WARNING) return; + + printf("Warning in Kernel::Scheduler::remove, client invalid, skip\n"); +} + + +void Kernel::Scheduler::_add__warning__invalid_client() +{ + if (!SCHEDULER__WARNING) return; + + printf("Warning in Kernel::Scheduler::add, client invalid, skip\n"); +} + + +void Kernel::Scheduler::_add__verbose__success() +{ + if (!ADD__VERBOSE) return; + + printf("Kernel::Scheduler::add, "); + _print_clients_via_labels(); + printf(" ↠)\n"); +} + + +void Kernel::Scheduler::_remove__verbose__success(Client* c) +{ + if (!REMOVE__VERBOSE) return; + + printf("Kernel::Scheduler::remove, "); + _print_clients_via_labels(); + printf(" → %i\n", c->label()); +} + + +void Kernel::Scheduler::_schedule__verbose__success() +{ + if (!SCHEDULER__VERBOSE) return; + + Client* const a = _last_client; + Client* const b = _current_client; + + Verbose::indent(10); + if (a) printf("from %i", a->label()); + else printf("from NULL"); + + Verbose::indent(10); + printf("to %i\n", b->label()); +} + + +/******************************************* + ** Kernel::Scheduler::Client definitions ** + *******************************************/ + +Kernel::Scheduler::Client::Context * +Kernel::Scheduler::Client::_schedulable_context() +{ + Context *result = 0; + if (!_sleeping) { + result = _context(); + if (_sleeping) + result = 0; + } + return result; +} + + +Kernel::Scheduler::Client::Quota +Kernel::Scheduler::Client::_consume(Quota const &consumed) +{ + if (consumed > _quota) { + _quota = 0; + } else{ + _quota = _quota - consumed; + } + return _quota; +} + + +Kernel::Scheduler::Client::Client() +: _quota(0), _scheduler(0), _sleeping(false) { } + + +void Kernel::Scheduler::Client::_earn_quota(Quota const &q) { _quota += q; } + + +void Kernel::Scheduler::Client::_sleep() { _sleeping = true; } + + +void Kernel::Scheduler::Client::_wake(){ _sleeping = false; } + + +/************************************************* + ** Kernel::Scheduler::Client_queue definitions ** + *************************************************/ + +void Kernel::Scheduler::Client_queue::print_state() +{ + Client *i = _head; + if (!i) printf("ø"); + while (i) { + printf("%i", i->label()); + if (i != _tail) printf("→"); + i = i->_next; + } +} + +#endif /* _KERNEL__INCLUDE__SCHEDULER_H_ */ + diff --git a/base-mb/src/kernel/include/generic/syscall_events.h b/base-mb/src/kernel/include/generic/syscall_events.h new file mode 100755 index 000000000..942fd2325 --- /dev/null +++ b/base-mb/src/kernel/include/generic/syscall_events.h @@ -0,0 +1,190 @@ +/* + * \brief Syscall event handling behaviors + * \author Martin Stein + * \date 2010-11-18 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__SYSCALL_EVENTS_H_ +#define _KERNEL__INCLUDE__GENERIC__SYSCALL_EVENTS_H_ + +#include +#include + +namespace Kernel { + + enum { SYSCALL_EVENT__ERROR = 1, + SYSCALL_EVENT__WARNING = 1, + SYSCALL_EVENT__VERBOSE = 0 }; + + + class Thread; + + + class Syscall_event : public Event { }; + + + class Print_char : public Syscall_event { + + protected: + + virtual bool _permission_to_do_print_char() = 0; + + public: + + typedef On_occurence__result On_print_char__result; + + On_print_char__result on_print_char(char c); + }; + + + class Thread_create : public Syscall_event { + + protected: + + virtual bool _permission_to_do_thread_create()=0; + + void _on_thread_create__warning__failed() + { + if (SYSCALL_EVENT__WARNING) + printf("Warning in Kernel::Thread_create::on_thread_create, syscall failed\n"); + } + + void _on_thread_create__verbose__success(Thread *t); + + public: + + struct Argument + { + Thread_id tid; + Protection_id pid; + Thread_id pager_tid; + Utcb* utcb; + addr_t vip; + addr_t vsp; + bool is_privileged; + }; + + typedef Thread_create_types::Result Result; + typedef On_occurence__result On_thread_create__result; + + On_thread_create__result on_thread_create(Argument *a, Result *r); + }; + + + class Thread_kill : public Syscall_event + { + protected: + + virtual bool _permission_to_do_thread_kill() = 0; + + void _on_thread_kill__warning__failed() + { + if (SYSCALL_EVENT__WARNING) + printf("Warning in Kernel::Thread_kill::on_thread_kill, syscall failed\n"); + } + + void _on_thread_kill__verbose__success(Thread_id tid); + + public: + + struct Argument { Thread_id tid; }; + + typedef Thread_kill_types::Result Result; + typedef On_occurence__result On_thread_kill__result; + + On_thread_kill__result on_thread_kill(Argument *a, Result *r); + }; + + + class Thread_sleep : public Syscall_event + { + protected: + + void _on_thread_sleep__verbose__success(); + + public: + + typedef On_occurence__result On_thread_sleep__result; + + On_thread_sleep__result on_thread_sleep(); + }; + + + class Thread_wake : public Syscall_event + { + protected: + + virtual bool _permission_to_do_thread_wake(Thread *t) = 0; + + void _on_thread_wake__warning__failed() + { + if (SYSCALL_EVENT__WARNING) + printf("Warning in Kernel::Thread_wake::on_thread_wake, syscall failed\n"); + } + + void _on_thread_wake__verbose__success(Thread_id tid); + + public: + + struct Argument { Thread_id tid; }; + + typedef Thread_wake_types::Result Result; + typedef On_occurence__result On_thread_wake__result; + + On_thread_wake__result on_thread_wake(Argument* a, Result* r); + }; + + + class Tlb_load : public Syscall_event + { + protected: + + virtual bool _permission_to_do_tlb_load() = 0; + + public: + + void on_tlb_load(Paging::Resolution* r); + }; + + + class Thread_pager : public Syscall_event { + + protected: + + virtual bool _permission_to_do_thread_pager(Thread_id tid) = 0; + + public: + + void on_thread_pager(Thread_id target_tid, Thread_id pager_tid); + }; + + + class Tlb_flush : public Syscall_event { + + protected: + + virtual bool _permission_to_do_tlb_flush() = 0; + + public: + + void on_tlb_flush(Paging::Virtual_page *first_page, unsigned size); + }; + + + class Thread_yield: public Syscall_event + { + public: + + virtual void yield()=0; + }; +} + +#endif /* _KERNEL__INCLUDE__GENERIC__SYSCALL_EVENTS_H_ */ + diff --git a/base-mb/src/kernel/include/generic/timer.h b/base-mb/src/kernel/include/generic/timer.h new file mode 100755 index 000000000..bff850f76 --- /dev/null +++ b/base-mb/src/kernel/include/generic/timer.h @@ -0,0 +1,197 @@ +/* + * \brief Declaration of gecoh timer device interface + * \author Martin stein + * \date 2010-06-23 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__TIMER_H_ +#define _KERNEL__INCLUDE__GENERIC__TIMER_H_ + +#include +#include +#include +#include +#include +#include + +extern Cpu::uint32_t volatile * _kernel_timer_ctrl; +extern Cpu::uint32_t _kernel_timer_ctrl_start; + +namespace Kernel { + + + enum { + TIMER__ERROR = 1, + TIMER__WARNING = 1, + TIMER__VERBOSE = 0, + TIMER__TRACE = 1, + }; + + + struct Tracks_time { + virtual ~Tracks_time(){} + virtual void time_consumed(unsigned int const & t) = 0; + }; + + + template + class Timer : public Kernel_entry::Listener, + public Kernel_exit::Listener, + public DEVICE_T + { + private: + + Irq_id const _irq_id; + unsigned int _start_value, _stop_value; + Tracks_time * _client; + + protected: + + /* Kernel::Kernel_entry_event::Listener interface */ + void _on_kernel_entry(); + + /* Kernel::Kernel_exit_event::Listener interface */ + void _on_kernel_exit(); + + /* debugging */ + inline void _on_kernel_exit__error__start_value_invalid(); + inline void _on_kernel_entry__verbose__success(); + inline void _on_kernel_exit__verbose__success(); + + public: + + Timer(Irq_id const & i, addr_t const & dca); + + inline bool is_busy(); + inline void track_time(unsigned int const & v, Tracks_time * const c); + + inline Irq_id irq_id(); + + inline unsigned int stop_value(); + inline unsigned int start_value(); + + /* debugging */ + void inline _start__trace(unsigned int const &v); + void inline _stop__trace(unsigned int const &v); + }; + + + typedef Timer Scheduling_timer; +} + + +/******************* + ** Kernel::Timer ** + *******************/ + + +template +Kernel::Timer::Timer(Irq_id const & i, + addr_t const & dca) : + DEVICE_T(dca), + _irq_id(i), + _start_value(0), + _stop_value(0) +{ + irq_controller()->unmask(_irq_id); +} + + + +template +void Kernel::Timer::_start__trace(unsigned int const &v) +{ + if (TIMER__TRACE && Verbose::trace_current_kernel_pass()) + printf("start(%i) ", v); +} + + +template +void Kernel::Timer::_stop__trace(unsigned int const& v) +{ + if (TIMER__TRACE && Verbose::trace_current_kernel_pass()) + printf("stop(%i) ", v); +} + + +template +unsigned int Kernel::Timer::stop_value() { return _stop_value; } + + +template +void Kernel::Timer::_on_kernel_exit__error__start_value_invalid() +{ + if (TIMER__ERROR) + printf("Error in Kernel::Timer::_on_kernel_exit," + "_start_value=%i invalid\n", _start_value); + halt(); +} + + +template +void Kernel::Timer::_on_kernel_entry__verbose__success() +{ + if (!TIMER__VERBOSE) return; + + printf("Kernel::Timer::_on_kernel_entry," + "_stop_value=%i\n", _stop_value); +} + + +template +void Kernel::Timer::_on_kernel_exit__verbose__success() +{ + if (!TIMER__VERBOSE) return; + + printf("Kernel::Timer::_on_kernel_exit," + "_start_value=%i\n", _start_value); +} + + +template +Kernel::Irq_id Kernel::Timer::irq_id() { return _irq_id; } + + +template +unsigned int Kernel::Timer::start_value() { return _start_value; } + + +template +void Kernel::Timer::track_time(unsigned int const & v, Tracks_time * const c) { + _start_value=v; + _client = c; +} + + +template +void Kernel::Timer::_on_kernel_entry() +{ + _stop_value = DEVICE_T::value(); + _stop__trace(_stop_value); + + unsigned int t = start_value()- stop_value(); + _client->time_consumed(t); + _on_kernel_entry__verbose__success(); +} + + +template +void Kernel::Timer::_on_kernel_exit() +{ + if (!_start_value) + _on_kernel_exit__error__start_value_invalid(); + + _start__trace(_start_value); + DEVICE_T::prepare_oneshot(_start_value, _kernel_timer_ctrl, _kernel_timer_ctrl_start); +} + + +#endif /* _KERNEL__INCLUDE__GENERIC__TIMER_H_ */ + diff --git a/base-mb/src/kernel/include/generic/tlb.h b/base-mb/src/kernel/include/generic/tlb.h new file mode 100755 index 000000000..3d69f9ccd --- /dev/null +++ b/base-mb/src/kernel/include/generic/tlb.h @@ -0,0 +1,152 @@ +/* + * \brief Generic Translation lookaside buffer interface + * \author Martin Stein + * \date 2010-11-08 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__GENERIC__TLB_H_ +#define _KERNEL__INCLUDE__GENERIC__TLB_H_ + +#include +#include + +namespace Kernel { + + template + class Tlb_tpl : public DEV_T + { + + private: + + typedef typename DEV_T::Entry_id Entry_id; + + Entry_id _current_entry_id; + + static Entry_id const fixed_entry_id_1 = 0; + static Entry_id const fixed_entry_id_2 = 1; + + void _next_entry_id() + { + _current_entry_id++; + if (_current_entry_id >= DEV_T::max_entry_id()) { + _current_entry_id = 0; + } + } + + public: + + typedef Paging::Virtual_page Virtual_page; + typedef Paging::Physical_page Physical_page; + typedef Paging::Resolution Resolution; + + Tlb_tpl() : _current_entry_id(0) { } + + /** + * Add resolution to the tlb (not persistent) + */ + void add(Resolution* r) + { + if (!r->valid()) { + printf("Error in Kernel::Tlb::add, invalid page\n"); + } + + while (1) { + if (!fixed(_current_entry_id)) { break; } + else { _next_entry_id(); } + } + + if(DEV_T::set_entry(_current_entry_id, + r->physical_page.address(), r->virtual_page.address(), + r->virtual_page.protection_id(), + Paging::size_log2_by_physical_page_size[r->physical_page.size()], + r->physical_page.permissions() == Paging::Physical_page::RW || + r->physical_page.permissions() == Paging::Physical_page::RWX, + r->physical_page.permissions() == Paging::Physical_page::RX || + r->physical_page.permissions() == Paging::Physical_page::RWX)) + { + PERR("Writing to TLB failed"); + } + _next_entry_id(); + } + + /** + * Add fixed resolution to the tlb (persistent till overwritten by + * fixed resolution) + */ + void add_fixed(Resolution* r1, Resolution* r2) + { + if(DEV_T::set_entry(fixed_entry_id_1, + r1->physical_page.address(), r1->virtual_page.address(), + r1->virtual_page.protection_id(), + Paging::size_log2_by_physical_page_size[r1->physical_page.size()], + r1->physical_page.permissions() == Paging::Physical_page::RW || + r1->physical_page.permissions() == Paging::Physical_page::RWX, + r1->physical_page.permissions() == Paging::Physical_page::RX || + r1->physical_page.permissions() == Paging::Physical_page::RWX)) + { + PERR("Writing to TLB failed"); + } + + if(DEV_T::set_entry(fixed_entry_id_2, + r2->physical_page.address(), r2->virtual_page.address(), + r2->virtual_page.protection_id(), + Paging::size_log2_by_physical_page_size[r2->physical_page.size()], + r2->physical_page.permissions() == Paging::Physical_page::RW || + r2->physical_page.permissions() == Paging::Physical_page::RWX, + r2->physical_page.permissions() == Paging::Physical_page::RX || + r2->physical_page.permissions() == Paging::Physical_page::RWX)) + { + PERR("Writing to TLB failed"); + } + } + + bool fixed(Entry_id i) { + return (i == fixed_entry_id_1) || (i == fixed_entry_id_2); + } + + void flush(Virtual_page *base, unsigned size); + }; + + typedef Tlb_tpl Tlb; + + /** + * Pointer to kernels static translation lookaside buffer + */ + Tlb * tlb(); +} + + +template +void Kernel::Tlb_tpl::flush(Virtual_page *base, unsigned size) +{ + addr_t area_base = base->address(); + addr_t area_top = area_base + size; + + for (Entry_id i=0; i <= DEV_T::MAX_ENTRY_ID; i++) { + + if (fixed(i)) { continue; } + + Cpu::addr_t page; + Protection_id pid; + unsigned size_log2; + + if(DEV_T::get_entry(i, page, pid, size_log2)) { + PERR("Reading TLB entry failed"); + } + if (base->protection_id() != pid) { continue; } + + if(page < area_top && (page + (1< area_base) { + DEV_T::clear_entry(i); + } + } +} + + +#endif /* _KERNEL__INCLUDE__GENERIC__TLB_H_ */ diff --git a/base-mb/src/kernel/include/generic/verbose.h b/base-mb/src/kernel/include/generic/verbose.h new file mode 100755 index 000000000..136aa6387 --- /dev/null +++ b/base-mb/src/kernel/include/generic/verbose.h @@ -0,0 +1,107 @@ +/* + * \brief Macros for errors, warnings, debugging + * \author Martin stein + * \date 2010-06-22 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _KERNEL__INCLUDE__VERBOSE_H_ +#define _KERNEL__INCLUDE__VERBOSE_H_ + +/* kernel includes */ +#include + +/* OS includes */ +#include +#include + +using Genode::printf; + +namespace Kernel { + + using namespace Cpu; + + /** + * Halt all executions (uninteruptable endless loop) + */ + void halt(); + + unsigned word_width(); + + /** + * Implementing verbose helper methods + */ + namespace Verbose { + + enum { + TRACE_KERNEL_PASSES = 0, + TRACE_ALL_THREAD_IDS = 1, + TRACE_ALL_PROTECTION_IDS = 1, + TRACE_ALL_SYSCALL_IDS = 1, + TRACE_ALL_EXCEPTION_IDS = 1, + TRACE_ALL_IRQ_IDS = 1 + }; + + Kernel::Thread_id const trace_these_thread_ids[]= { 0 }; + + Kernel::Protection_id const trace_these_protection_ids[] = { + Roottask::PROTECTION_ID, Kernel::INVALID_PROTECTION_ID }; + + Kernel::Syscall_id const trace_these_syscall_ids[] = { INVALID_SYSCALL_ID }; +// TLB_LOAD , +// TLB_FLUSH , +// THREAD_CREATE, +// THREAD_KILL , +// THREAD_SLEEP , +// THREAD_WAKE , +// THREAD_YIELD , +// THREAD_PAGER , +// IPC_REQUEST , +// IPC_SERVE , +// PRINT_CHAR , +// PRINT_INFO , + + Kernel::Exception_id const trace_these_exception_ids[] = { INVALID_EXCEPTION_ID }; +// FAST_SIMPLEX_LINK , +// UNALIGNED , +// ILLEGAL_OPCODE , +// INSTRUCTION_BUS , +// DATA_BUS , +// DIV_BY_ZERO_EXCEPTON , +// FPU , +// PRIVILEGED_INSTRUCTION, +// INTERRUPT , +// EXTERNAL_NON_MASKABLE_BREAK, +// EXTERNAL_MASKABLE_BREAK , +// DATA_STORAGE , +// INSTRUCTION_STORAGE , +// DATA_TLB_MISS , +// INSTRUCTION_TLB_MISS, + + /* + * Tracing for specific kernel-entry causes can be configured in + * 'platform.cc'. + */ + bool trace_current_kernel_pass(); + + void begin__trace_current_kernel_pass(); + + void inline indent(unsigned int const &i); + } +} + + +void Kernel::Verbose::indent(unsigned int const &indent) +{ + for (unsigned int i = 0; i < indent; i++) + _prints_chr1(' '); +} + + +#endif /* _KERNEL__INCLUDE__VERBOSE_H_ */ diff --git a/base-mb/src/kernel/include/petalogix_s3adsp1800_mmu/platform/platform.h b/base-mb/src/kernel/include/petalogix_s3adsp1800_mmu/platform/platform.h new file mode 100755 index 000000000..76a6ac3c5 --- /dev/null +++ b/base-mb/src/kernel/include/petalogix_s3adsp1800_mmu/platform/platform.h @@ -0,0 +1,729 @@ +/* + * \brief Implementations for kernels platform class + * \author Martin Stein + * \date 2010-10-01 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PETALOGIX_S3ADSP1800_MMU__PLATFORM__PLATFORM_H_ +#define _INCLUDE__PETALOGIX_S3ADSP1800_MMU__PLATFORM__PLATFORM_H_ + +/* Device includes */ +#include +#include +#include + +/* Kernel includes */ +#include +#include +#include +#include + + +/* + * Asm labels where to enter an irq/exception/syscall from userland, and vice + * versa the userland from inside the kernel + */ +extern Kernel::word_t _syscall_entry; +extern Kernel::word_t _exception_entry; +extern Kernel::word_t _interrupt_entry; +extern Kernel::word_t _userland_entry; +extern Kernel::word_t _atomic_ops_begin; +extern Kernel::word_t _atomic_ops_end; +extern Kernel::addr_t _call_after_kernel; + +namespace Kernel { + + enum { + PLATFORM__TRACE = 1, + PLATFORM__VERBOSE = 0, + PLATFORM__VERBOSE__THREAD_TRACING = 1, + + PLATFORM_THREAD__ERROR = 1, + PLATFORM_THREAD__WARNING = 1, + PLATFORM_THREAD__VERBOSE = 0, + + PLATFORM_IRQ__VERBOSE = 0, + PLATFORM_EXCEPTION__VERBOSE = 0, + PLATFORM_SYSCALL__VERBOSE = 0, + + WORD_WIDTH_LOG2 = 5, + BYTE_WIDTH_LOG2 = 3 + }; + + class Platform_thread; + class Platform; + + + /** + * Get kernel's static platform representation + */ + Platform* platform(); + + + /** + * Platform specific execution context + */ + struct Exec_context + { + typedef Kernel::Protection_id Protection_id; + typedef Kernel::word_t word_t; + + /** + * Type constraints + */ + enum{ + WORD_WIDTH_LOG2 = Kernel::WORD_WIDTH_LOG2, + BYTE_WIDTH_LOG2 = Kernel::BYTE_WIDTH_LOG2, + WORD_SIZE = 1 << (WORD_WIDTH_LOG2-BYTE_WIDTH_LOG2) }; + + /** + * Blocking types + */ + enum{ + NO_BLOCKING = 0, + IRQ_BLOCKING = 1, + EXCEPTION_BLOCKING = 2, + SYSCALL_BLOCKING = 3, + BLOCKING_TYPE_RANGE = 4 + }; + + /** + * Register constraints + */ + enum { + /* rmsr */ + RMSR_BE_LSHIFT = 0, RMSR_BE_MASK = 1 << RMSR_BE_LSHIFT, + RMSR_IE_LSHIFT = 1, RMSR_IE_MASK = 1 << RMSR_IE_LSHIFT, + RMSR_C_LSHIFT = 2, RMSR_C_MASK = 1 << RMSR_C_LSHIFT, + RMSR_BIP_LSHIFT = 3, RMSR_BIP_MASK = 1 << RMSR_BIP_LSHIFT, + RMSR_FSL_LSHIFT = 4, RMSR_FSL_MASK = 1 << RMSR_FSL_LSHIFT, + RMSR_ICE_LSHIFT = 5, RMSR_ICE_MASK = 1 << RMSR_ICE_LSHIFT, + RMSR_DZ_LSHIFT = 6, RMSR_DZ_MASK = 1 << RMSR_DZ_LSHIFT, + RMSR_DCE_LSHIFT = 7, RMSR_DCE_MASK = 1 << RMSR_DCE_LSHIFT, + RMSR_EE_LSHIFT = 8, RMSR_EE_MASK = 1 << RMSR_EE_LSHIFT, + RMSR_EIP_LSHIFT = 9, RMSR_EIP_MASK = 1 << RMSR_EIP_LSHIFT, + RMSR_PVR_LSHIFT = 10, RMSR_PVR_MASK = 1 << RMSR_PVR_LSHIFT, + RMSR_UM_LSHIFT = 11, RMSR_UM_MASK = 1 << RMSR_UM_LSHIFT, + RMSR_UMS_LSHIFT = 12, RMSR_UMS_MASK = 1 << RMSR_UMS_LSHIFT, + RMSR_VM_LSHIFT = 13, RMSR_VM_MASK = 1 << RMSR_VM_LSHIFT, + RMSR_VMS_LSHIFT = 14, RMSR_VMS_MASK = 1 << RMSR_VMS_LSHIFT, + RMSR_CC_LSHIFT = 31, RMSR_CC_MASK = 1 << RMSR_CC_LSHIFT, + + /* resr */ + RESR_EC_LSHIFT = 0, RESR_EC_MASK = 0x1F<> RESR_EC_LSHIFT; } + }; +} + + +extern Kernel::Exec_context* _userland_context; + + +namespace Kernel { + + /** + * Platform representation + */ + class Platform : public Kernel_entry::Listener + { + public: + + /** + * General configuration + */ + enum { + ATOMIC_OPS_PAGE_SIZE_LOG2 = DEFAULT_PAGE_SIZE_LOG2, + KERNEL_ENTRY_SIZE_LOG2 = DEFAULT_PAGE_SIZE_LOG2 + }; + + /** + * Verbose, errors, warnings + */ + enum { + VERBOSE__CONSTRUCTOR = PLATFORM__VERBOSE, + VERBOSE__ENTER_USERLAND = PLATFORM__VERBOSE + }; + + /** + * General platform constraints + */ + enum { + WORD_WIDTH_LOG2 = Kernel::WORD_WIDTH_LOG2, + BYTE_WIDTH_LOG2 = Kernel::BYTE_WIDTH_LOG2, + BYTE_WIDTH = 1 << BYTE_WIDTH_LOG2, + WORD_WIDTH = 1 << WORD_WIDTH_LOG2, + WORD_SIZE = 1 << (WORD_WIDTH_LOG2-BYTE_WIDTH_LOG2), + + WORD_HALFWIDTH = WORD_WIDTH >> 1, + + WORD_LEFTHALF_MASK = ~0 << WORD_HALFWIDTH , + WORD_RIGHTHALF_MASK = ~WORD_LEFTHALF_MASK + }; + + typedef uint32_t word_t; + typedef uint32_t Register; + + private: + + Kernel::Tlb _tlb; + + /** + * Processor specific + */ + enum { + ASM_IMM = 0xb0000000, + ASM_BRAI = 0xb8080000, + ASM_RTSD = 0xb6000000, + ASM_NOP = 0x80000000, + + SYSCALL_ENTRY = 0x00000008, + INTERRUPT_ENTRY = 0x00000010, + EXCEPTION_ENTRY = 0x00000020 + }; + + void _initial_tlb_entries() + { + using namespace Paging; + + Physical_page::size_t atomic_ops_pps, kernel_entry_pps; + + if (Physical_page::size_by_size_log2( + atomic_ops_pps, ATOMIC_OPS_PAGE_SIZE_LOG2) || + Physical_page::size_by_size_log2( + kernel_entry_pps, KERNEL_ENTRY_SIZE_LOG2)) + { + printf("Error in Kernel::Platform::_initial_tlb_entries"); + return; + }; + + Physical_page atomic_ops_pp((addr_t)&_atomic_ops_begin, + atomic_ops_pps, Physical_page::RX); + + Virtual_page atomic_ops_vp(atomic_ops_pp.address(), + UNIVERSAL_PROTECTION_ID); + + Resolution atomic_ops_res(&atomic_ops_vp, &atomic_ops_pp); + + Physical_page kernel_entry_pp((addr_t)0, kernel_entry_pps, + Physical_page::RX); + + Virtual_page kernel_entry_vp(kernel_entry_pp.address(), + UNIVERSAL_PROTECTION_ID); + + Resolution kernel_entry_res(&kernel_entry_vp, &kernel_entry_pp); + + tlb()->add_fixed(&atomic_ops_res, &kernel_entry_res); + } + + /** + * Initialize the ability to enter userland + */ + inline void _init_userland_entry() { + _call_after_kernel=(addr_t)&_userland_entry; } + + /** + * Fill in jump to CPU's 2-word-width exception entry + */ + inline void _init_exception_entry() + { + *(word_t*)EXCEPTION_ENTRY = + ASM_IMM | ((word_t)&_exception_entry & WORD_LEFTHALF_MASK) >> WORD_HALFWIDTH; + + *(word_t*)(EXCEPTION_ENTRY + WORD_SIZE) = + ASM_BRAI | ((word_t)&_exception_entry & WORD_RIGHTHALF_MASK); + } + + /** + * Fill in jump to CPU's 2-word-width syscall entry + */ + inline void _init_syscall_entry() + { + *(word_t*)SYSCALL_ENTRY = + ASM_IMM | ((word_t)&_syscall_entry & WORD_LEFTHALF_MASK) >> WORD_HALFWIDTH; + + *(word_t*)(SYSCALL_ENTRY + WORD_SIZE) = + ASM_BRAI | (word_t) &_syscall_entry & WORD_RIGHTHALF_MASK; } + + /** + * Fill in jump to CPU's 2-word-width interrupt entry + */ + inline void _init_interrupt_entry() + { + *((word_t*) INTERRUPT_ENTRY) = + ASM_IMM | ((word_t)&_interrupt_entry & WORD_LEFTHALF_MASK) >> WORD_HALFWIDTH; + + *((word_t*)(INTERRUPT_ENTRY + WORD_SIZE)) = + ASM_BRAI | (word_t) &_interrupt_entry & WORD_RIGHTHALF_MASK; + } + + public: + + /** + * Constructor + */ + Platform(); + + bool is_atomic_operation(void* ip) + { + enum { + SIZE = (1 << ATOMIC_OPS_PAGE_SIZE_LOG2), + SIZE_WORDS = SIZE/sizeof(word_t) + }; + + static word_t *const _first_atomic_op = &_atomic_ops_begin; + static word_t *const _last_atomic_op = + _first_atomic_op + (SIZE_WORDS-1); + + return ((ip >=_first_atomic_op) & (ip <=_last_atomic_op)); + } + + /** + * Set execution context loaded at next userland entry + */ + inline int userland_context(Exec_context* c) + { + _userland_context = c; + _userland_context__verbose__set(c); + return 0; + } + + /** + * Lock the platforms execution ability to one execution context + */ + inline void lock(Exec_context *c){ userland_context(c); } + + /** + * Set return address register + * + * It is essential that this function is always inline! + */ + inline int return_address(addr_t a) + { + asm volatile("add r15, %0, r0"::"r"((Register)a):); + return 0; + } + + /** + * Halt whole system + */ + inline void halt() { asm volatile ("bri 0"); }; + + /** + * Get the platforms general IRQ-controller + */ + inline Irq_controller * const irq_controller(); + + /** + * Get the timer that is reserved for kernels schedulinge + */ + inline Scheduling_timer * const timer(); + + Tlb *tlb() { return &_tlb; }; + + protected: + + void _on_kernel_entry__trace__thread_interrupts(); + + void _on_kernel_entry__verbose__called() + { + if (PLATFORM__VERBOSE) + printf("Kernel::Platform::_on_kernel_entry\n"); + } + + void _on_kernel_entry(); + + void _userland_context__verbose__set(Exec_context* c) + { + if (!PLATFORM__VERBOSE) return; + if (_userland_context) { + printf("Kernel::Platform::_userland_context, new userland context c=0x%8X, printing contents", (uint32_t)_userland_context); + c->print_content(2); + } else + printf("Kernel::Platform::_userland_context, no userland context"); + + printf("\n"); + } + }; + + + class Platform_blocking + { + protected: + + typedef Kernel::Exec_context Context; + typedef Kernel::Platform_thread Owner; + + Owner* _owner; + Context* _context; + + public: + + /** + * Constructor + */ + Platform_blocking(Owner* o, Context* c) + : _owner(o), _context(c) {} + }; + + + /** + * Platform-specific IRQ + */ + class Platform_irq : public Platform_blocking, public Irq + { + public: + + /** + * Constructor + */ + Platform_irq(Owner* o, Context* c) : Platform_blocking(o,c) {} + + void block(); + + protected: + + void _block__verbose__success() + { + if (PLATFORM_IRQ__VERBOSE) + printf("Platform_irq::block(), _id=%i\n", _id); + } + }; + + + class Platform_exception : public Platform_blocking, public Exception + { + protected: + + Protection_id protection_id(); + addr_t address(); + bool attempted_write_access(); + + public: + + /** + * Constructor + */ + Platform_exception(Owner* o, Context* c) : Platform_blocking(o, c) { } + + void block(Exec_context * c); + }; + + + class Platform_syscall : public Platform_blocking, public Syscall + { + public: + + /** + * Constructor + */ + Platform_syscall(Owner *o, Context *c, Source *s) : + Platform_blocking(o,c), + Syscall(&c->r30, &c->r29, &c->r28, + &c->r27, &c->r26, &c->r25, + &c->r24, &c->r30, s) + { } + + void block(); + + protected: + + void _block__verbose__success() + { + if (PLATFORM_IRQ__VERBOSE) + printf("Platform_syscall::block(), _id=%i\n", _id); + } + }; + + + /** + * Platform-specific thread implementations + */ + class Platform_thread + { + typedef Kernel::Blocking Blocking; + + enum { + INITIAL_RMSR = 1 << Exec_context::RMSR_PVR_LSHIFT + | 1 << Exec_context::RMSR_UMS_LSHIFT + | 1 << Exec_context::RMSR_VMS_LSHIFT, + + INITIAL_BLOCKING_TYPE = Exec_context::NO_BLOCKING + }; + + Platform_irq _irq; + Platform_exception _exception; + Platform_syscall _syscall; + + Exec_context _exec_context; + + /* if not zero, this thread is blocked */ + Blocking* _blocking; + + public: + + bool timer_interrupted() + { + return false; + } + + void yield_after_atomic_operation() { _exec_context.r31 = 1; } + + void unblock() { _blocking = 0; } + + typedef Kernel::Protection_id Protection_id; + + Platform_thread() : + _irq(this, &_exec_context), + _exception(this, &_exec_context), + _syscall(this, &_exec_context, 0), + _exec_context(this), + _blocking(0) + { } + + Platform_thread(addr_t ip, + addr_t sp, + Protection_id pid, + Syscall::Source *sc) + : + _irq(this, &_exec_context), + _exception(this, &_exec_context), + _syscall(this, &_exec_context, sc), + _exec_context(this), + _blocking(0) + { + _exec_context.rpc = ip; + _exec_context.r1 = sp; + _exec_context.rpid = pid; + _exec_context.blocking_type= INITIAL_BLOCKING_TYPE; + _exec_context.rmsr = INITIAL_RMSR; } + + enum { BLOCK__ERROR = 1, + BLOCK__WARNING = 1}; + + /** + * Get thread blocked if there is a blocking at execution context + */ + void on_kernel_entry() + { + using Kernel::Exec_context; + + switch (_exec_context.blocking_type) { + + case Exec_context::NO_BLOCKING: + _blocking = 0; + break; + + case Exec_context::IRQ_BLOCKING: + _irq.block(); + _blocking = &_irq; + break; + + case Exec_context::EXCEPTION_BLOCKING: + _exception.block(&_exec_context); + _blocking = &_exception; + break; + + case Exec_context::SYSCALL_BLOCKING: + _syscall.block(); + _blocking = &_syscall; + break; + + default: + _block__error__unknown_blocking_type(); + } + + _block__verbose__success(); + } + + Protection_id protection_id() { + return (Protection_id)_exec_context.rpid; } + + addr_t instruction_pointer() { + return (addr_t)_exec_context.rpc; } + + Exec_context* exec_context() { + return &_exec_context; } + + Exec_context* unblocked_exec_context() + { + Exec_context* result=&_exec_context; + + if (_blocking) { + if (!_blocking->unblock()) + result = 0; + else + _blocking = 0; + } + return result; + } + + void call_argument_0(word_t value){ + _exec_context.r5=value;} + + void bootstrap_argument_0(word_t value){ + _exec_context.r31=value;} + + void print_state() { + _exec_context.print_content(2); + printf("\n"); + }; + + Exception *exception() { return &_exception; } + Syscall *syscall() { return &_syscall; } + Irq *irq() { return &_irq; } + + protected: + + void _block__error__unknown_blocking_type() + { + if (!PLATFORM_THREAD__ERROR) + return; + + printf("Error in Kernel::Platform_thread::block: " + "unknown blocking_type=%i, printing state\n", + _exec_context.blocking_type); + + _exec_context.print_content(2); + printf("halt\n"); + halt(); + } + + void _block__warning__no_blocking() + { + if (!PLATFORM_THREAD__WARNING) + return; + + printf("Warning Kernel::Platform_thread::_no_blocking called\n"); + halt(); + } + + void _block__verbose__success() + { + if (!PLATFORM_THREAD__VERBOSE) + return; + + printf("Kernel::Platform_thread::block, blocked " + "this=0x%p, blocking_type=%i\n", + this, _exec_context.blocking_type); + } + }; +} + + +Kernel::Irq_controller * const Kernel::Platform::irq_controller() +{ + using namespace Xilinx; + static Irq_controller _ic = Irq_controller(Xps_intc::Constr_arg(Cpu::XPS_INTC_BASE)); + return &_ic; +} + + +Kernel::Scheduling_timer * const Kernel::Platform::timer() +{ + using namespace Xilinx; + static Scheduling_timer _st = Scheduling_timer(SCHEDULING_TIMER_IRQ, + (addr_t)SCHEDULING_TIMER_BASE); + return &_st; +} + + +#endif /* _INCLUDE__PETALOGIX_S3ADSP1800_MMU__PLATFORM__PLATFORM_H_ */ + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/atomic.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/atomic.s new file mode 100755 index 000000000..696b2a533 --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/atomic.s @@ -0,0 +1,92 @@ +/* + * \brief Simulated Atomic Operations on Xilinx Microblaze + * \author Martin Stein + * \date 2010-08-30 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +.include "linker_commands.s" + +.global _atomic_cmpxchg + + +/** + * the virtual area to the following code is interrupt save and + * executable-only in virtual mode. the code have to be position + * independent, so it can be linked to the according virtual area + * in any image that is executed above kernel to import the labels. + * it is bothsides aligned - so that no common functions could gain + * interruptsave status because it were linked inside this page too + */ +_BEGIN_ATOMIC_OPS + +/** + * Atomic compare and exchange, see cmpxchg + * + * should only be used wrapped by cmpxchg + * otherwise this operation may won't behave as wished + * and leads to unnesscessary yielding of thread! + * + * Parameters dest, cmp_val, new_val and *dest are assumed in r30, r29, r28 and r27 + * R15 is assumed to hold return pointer + * Return value is stored in r28 + */ +_atomic_cmpxchg: + + xor r29, r29, r27 /* diff=cmp_val-*dest */ + bnei r29, _atomic_cmpxchg_notequal_1 /* if(!diff) { */ + swi r28, r30, 0 /* *dest=new_val */ + _atomic_cmpxchg_notequal_1: /* } */ + or r28, r0, r0 /* result=0 */ + bnei r29, _atomic_cmpxchg_notequal_2 /* if(!diff) { */ + addi r28, r0, 1 /* result=1 */ + _atomic_cmpxchg_notequal_2: /* } */ + +/* idle a while in interrupt save state, enable this for testing atomicity of atomic ops */ +/* + addi r29, r0, 0x04000000 + _atomic_cmpxchg_idle: + addi r29, r29, -1 + bnei r29, _atomic_cmpxchg_idle +*/ + + beqi r31, _atomic_cmpxchg_yield_return /* if(interrupt occured) { */ + bri _atomic_syscall_yield /* yield thread */ + _atomic_cmpxchg_yield_return: /* }else{ */ + rtsd r15, +2*4 /* return to nonatomic */ + or r0, r0, r0 /* } */ + + +/** + * Yield Thread (for atomic ops, if interrupt has occured during execution) + */ +_atomic_syscall_yield: + + /* backup registers */ + addik r1, r1, -2*4 + swi r15, r1, +0*4 + swi r31, r1, +1*4 + + /* set syscall type (see include/kernel/syscalls.h) */ + addi r31, r0, 7 + + /* jump to cpus syscall-handler if it's enabled */ + brki r15, 0x8 + or r0, r0, r0 + + /* recover & return */ + lwi r15, r1, +0*4 + lwi r31, r1, +1*4 + addik r1, r1, +2*4 + bri _atomic_cmpxchg_yield_return + +_END_ATOMIC_OPS + + + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0.s new file mode 100755 index 000000000..488afdd47 --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0.s @@ -0,0 +1,45 @@ +/* + * \brief Startup code for programs on microblaze + * \author Martin Stein + * \date 21.06.2010 + */ + +.include "linker_commands.s" +.include "errors.s" + +.extern _main + +.global _main_utcb_addr + + +.macro _INIT_MAIN_UTCB + swi r31, r0, _main_utcb_addr +.endm + + +.macro _INIT_MAIN_STACK + la r1, r0, _main_stack_base +.endm + + +_BEGIN_ELF_ENTRY_CODE + + _INIT_MAIN_UTCB + _INIT_MAIN_STACK + + bralid r15, _main + or r0, r0, r0 + + _ERROR_NOTHING_LEFT_TO_CALL_BY_CRT0 + + +_BEGIN_READABLE_WRITEABLE + + .align 4 + _main_utcb_addr: .space 1*4 + .align 4 + _main_stack_top: .space 1024*1024 + _main_stack_base: + + + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0_kernel.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0_kernel.s new file mode 100755 index 000000000..359cc7031 --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/crt0_kernel.s @@ -0,0 +1,93 @@ +/* + * \brief Startup code for kernel main routine on microblaze + * \author Martin Stein + * \date 2010-06-21 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* platform includes */ +.include "errors.s" +.include "linker_commands.s" + +/* + * To be compatible to crt0.s for common programs, + * the following labels are used for roottasks main-thread + */ +.global _main_utcb_addr + +/* + * Here you can denote an instruction pointer to + * call after kernel execution returns (e.g. userland entry) + */ +.global _call_after_kernel + +/* top of kernel stack is used when we have to reset the kernel + * context, for example when we enter the kernel from userland + */ +.global _kernel_stack_top + +/* kernel returns to this label after execution + */ +.global _exit_kernel + +/* pointer to kernel main routine */ +.extern _kernel + + + +/******************************* + ** Macros for _start routine ** + *******************************/ + +.macro _CALL_AFTER_KERNEL__USES_R3_R15 + lwi r3, r0, _call_after_kernel + beqi r3, _no_call_after_kernel + addi r15, r0, _exit_call_after_kernel-2*4 + bra r3 + + _exit_call_after_kernel: + _no_call_after_kernel: +.endm + + +.macro _CALL_KERNEL__USES_R15 + addi r1, r0, _kernel_stack_top + addi r15, r0, _exit_kernel-2*4 + brai _kernel + or r0, r0, r0 + _exit_kernel: +.endm + + + +/******************** + ** _start_routine ** + ******************** + + +/* linker links this section to kernelbase + offset 0 */ + +_BEGIN_ELF_ENTRY_CODE + + _CALL_KERNEL__USES_R15 + _CALL_AFTER_KERNEL__USES_R3_R15 + _ERROR_NOTHING_LEFT_TO_CALL_BY_CRT0 + + + +_BEGIN_READABLE_WRITEABLE + + .align 4 + _main_utcb_addr: .space 4*1 + .align 4 + _call_after_kernel: .space 4*1 + .align 4 + _kernel_stack_base: .space 1024*1024 + _kernel_stack_top: + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/errors.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/errors.s new file mode 100755 index 000000000..7e10866ac --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/errors.s @@ -0,0 +1,23 @@ +/* + * \brief Assembler Errors + * \author Martin Stein + * \date 2010-10-06 + * + * Grouped into one include file for better management and identification + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +.macro _ERROR_NOTHING_LEFT_TO_CALL_BY_CRT0 + brai 0x99000001 +.endm + + +.macro _ERROR_UNKNOWN_USERLAND_BLOCKING_TYPE + brai 0x99000003 +.endm diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/exec_context.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/exec_context.s new file mode 100755 index 000000000..b7aaff2bd --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/exec_context.s @@ -0,0 +1,527 @@ +/* + * \brief Access to execution context internals + * \author Martin Stein + * \date 2010-10-06 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + + +/********************************************************** + ** Overwrite specific parts of the execution context ** + ** ** + ** \param r15 base address of the execution context ** + ** \param (rx) value that shall be written - except ** + ** r1, rpc and r15 - the register itself, ** + ** otherwise use "prestore" labels or r3 ** + **********************************************************/ + +.macro _SAVE_R2_TO_R13_TO_CONTEXT__USES_R2_TO_R13_R15 + swi r2, r15, 2*4 + swi r3, r15, 3*4 + swi r4, r15, 4*4 + swi r5, r15, 5*4 + swi r6, r15, 6*4 + swi r7, r15, 7*4 + swi r8, r15, 8*4 + swi r9, r15, 9*4 + swi r10, r15, 10*4 + swi r11, r15, 11*4 + swi r12, r15, 12*4 + swi r13, r15, 13*4 +.endm + + +.macro _SAVE_R18_TO_R31_TO_CONTEXT__TO_R15_R18_TO_R31 + swi r18, r15, 18*4 + swi r19, r15, 19*4 + swi r20, r15, 20*4 + swi r21, r15, 21*4 + swi r22, r15, 22*4 + swi r23, r15, 23*4 + swi r24, r15, 24*4 + swi r25, r15, 25*4 + swi r26, r15, 26*4 + swi r27, r15, 27*4 + swi r28, r15, 28*4 + swi r29, r15, 29*4 + swi r30, r15, 30*4 + swi r31, r15, 31*4 +.endm + + +.macro _SAVE_PRESTORED_R1_R15_RPC_TO_CONTEXT__USES_R3_R15 + lwi r3, r0, _prestored_r1 + swi r3, r15, 1*4 + lwi r3, r0, _prestored_r15 + swi r3, r15, 15*4 + lwi r3, r0, _prestored_rpc + swi r3, r15, 32*4 +.endm + + +.macro _SAVE_RMSR_TO_CONTEXT__USES_R3_R15 + mfs r3, rmsr + swi r3, r15, 33*4 +.endm + + +.macro _SAVE_RPID_TO_CONTEXT__USES_R3_R15 + mfs r3, rpid + swi r3, r15, 36*4 +.endm + + +.macro _SAVE_BLOCKING_TYPE_TO_CONTEXT__USES_R3_R15 + swi r3, r15, 37*4 +.endm + + +.macro _SAVE_RESR_TO_CONTEXT__USES_R3_R15 + mfs r3, resr + swi r3, r15, 35*4 +.endm + + +.macro _SAVE_REAR_TO_CONTEXT__USES_R3_R15 + mfs r3, rear + swi r3, r15, 34*4 +.endm + + + +/*********************************************************** + ** Load a specifics values from the execution context ** + ** ** + ** \param r15 base address of the execution context ** + ** \return (rx) value that has been loaded - except ** + ** r1, rpc and r15 - the register itself, ** + ** otherwise use "preload" labels or r3 ** + ***********************************************************/ + +.macro _LOAD_R2_TO_R13_FROM_CONTEXT__USES_R2_TO_R13_R15 + lwi r2, r15, 2*4 + lwi r3, r15, 3*4 + lwi r4, r15, 4*4 + lwi r5, r15, 5*4 + lwi r6, r15, 6*4 + lwi r7, r15, 7*4 + lwi r8, r15, 8*4 + lwi r9, r15, 9*4 + lwi r10, r15, 10*4 + lwi r11, r15, 11*4 + lwi r12, r15, 12*4 + lwi r13, r15, 13*4 +.endm + + +.macro _LOAD_R18_TO_R31_FROM_CONTEXT__TO_R15_R18_TO_R31 + lwi r18, r15, 18*4 + lwi r19, r15, 19*4 + lwi r20, r15, 20*4 + lwi r21, r15, 21*4 + lwi r22, r15, 22*4 + lwi r23, r15, 23*4 + lwi r24, r15, 24*4 + lwi r25, r15, 25*4 + lwi r26, r15, 26*4 + lwi r27, r15, 27*4 + lwi r28, r15, 28*4 + lwi r29, r15, 29*4 + lwi r30, r15, 30*4 + lwi r31, r15, 31*4 +.endm + + +.macro _PRELOAD_R1_R15_RPC_FROM_CONTEXT__USES_R3_R15 + lwi r3, r15, 1*4 + swi r3, r0, _preloaded_r1 + lwi r3, r15, 15*4 + swi r3, r0, _preloaded_r15 + lwi r3, r15, 32*4 + swi r3, r0, _preloaded_rpc +.endm + + +.macro _LOAD_RMSR_FROM_CONTEXT__USES_R3_R4_R15 + lwi r3, r15, 33*4 + mts rmsr, r3 + _SYNCHRONIZING_OP +.endm + + +.macro _LOAD_RPID_FROM_CONTEXT__USES_R3_R15 + lwi r3, r15, 36*4 + mts rpid, r3 + _SYNCHRONIZING_OP +.endm + + +.macro _LOAD_BLOCKING_TYPE_FROM_CONTEXT__USES_R3_R15 + lwi r3, r15, 37*4 +.endm + + + +.macro _VARIABLES_TO_WRITE_EXEC_CONTEXT + .align 4 + _prestored_r1: .space 4 + .align 4 + _prestored_r15: .space 4 + .align 4 + _prestored_rpc: .space 4 +.endm + + +.macro _VARIABLES_TO_READ_EXEC_CONTEXT + .align 4 + _preloaded_r1: .space 4 + .align 4 + _preloaded_r15: .space 4 + .align 4 + _preloaded_rpc: .space 4 +.endm + + + + +/** + * Macros to print out context content + * + * (any value hexadecimal and padded with + * leading zeros to 8 digits) + * output has following format: + * + * r1 r2 r3 r4 + * ... + * ... + * r28 r29 r30 r31 + * rmsr + * + */ + + +.macro _4BIT_SHIFT_RIGHT__ARG_30__RET_30 + srl r30, r30 + srl r30, r30 + srl r30, r30 + srl r30, r30 +.endm + + +.macro _PRINT_ASCII8__ARG_30 + swi r30, r0, 0x84000004 +.endm + + +.macro _PRINT_HEX8__ARG_30 + swi r29, r0, _print_hex8__buffer_0 + + andi r30, r30, 0xf + rsubi r29, r30, 9 + addi r30, r30, 48 + + bgei r29, 8 + addi r30, r30, 39 + _PRINT_ASCII8__ARG_30 + + lwi r29, r0, _print_hex8__buffer_0 +.endm + + +.macro _PRINT_HEX32__ARG_31 + swi r31, r0, _print_hex32__buffer_1 + swi r30, r0, _print_hex32__buffer_0 + + lwi r31, r0, _print_hex32__buffer_1-3 + add r30, r31, r0 + _4BIT_SHIFT_RIGHT__ARG_30__RET_30 + _PRINT_HEX8__ARG_30 + add r30, r31, r0 + _PRINT_HEX8__ARG_30 + + lwi r31, r0, _print_hex32__buffer_1-2 + add r30, r31, r0 + _4BIT_SHIFT_RIGHT__ARG_30__RET_30 + _PRINT_HEX8__ARG_30 + add r30, r31, r0 + _PRINT_HEX8__ARG_30 + + lwi r31, r0, _print_hex32__buffer_1-1 + add r30, r31, r0 + _4BIT_SHIFT_RIGHT__ARG_30__RET_30 + _PRINT_HEX8__ARG_30 + add r30, r31, r0 + _PRINT_HEX8__ARG_30 + + lwi r31, r0, _print_hex32__buffer_1-0 + add r30, r31, r0 + _4BIT_SHIFT_RIGHT__ARG_30__RET_30 + _PRINT_HEX8__ARG_30 + add r30, r31, r0 + _PRINT_HEX8__ARG_30 + + lwi r31, r0, _print_hex32__buffer_1 + lwi r30, r0, _print_hex32__buffer_0 +.endm + + +.macro _PRINT_ASCII_SPACE + swi r30, r0, _print_ascii_space__buffer_0 + + addi r30, r0, 32 + _PRINT_ASCII8__ARG_30 + + lwi r30, r0, _print_ascii_space__buffer_0 +.endm + + +.macro _PRINT_ASCII_BREAK + swi r30, r0, _print_ascii_break__buffer_0 + + addi r30, r0, 13 + _PRINT_ASCII8__ARG_30 + addi r30, r0, 10 + _PRINT_ASCII8__ARG_30 + + lwi r30, r0, _print_ascii_break__buffer_0 +.endm + + +.macro _PRINT_ASCII_STOP + swi r30, r0, _print_ascii_stop__buffer_0 + + addi r30, r0, 115 + _PRINT_ASCII8__ARG_30 + addi r30, r0, 116 + _PRINT_ASCII8__ARG_30 + addi r30, r0, 111 + _PRINT_ASCII8__ARG_30 + addi r30, r0, 112 + _PRINT_ASCII8__ARG_30 + + _PRINT_ASCII_BREAK + + lwi r30, r0, _print_ascii_stop__buffer_0 +.endm + + +.macro _PRINT_ASCII_RUN + swi r30, r0, _print_ascii_run__buffer_0 + + addi r30, r0, 114 + _PRINT_ASCII8__ARG_30 + addi r30, r0, 117 + _PRINT_ASCII8__ARG_30 + addi r30, r0, 110 + _PRINT_ASCII8__ARG_30 + + _PRINT_ASCII_BREAK + + lwi r30, r0, _print_ascii_run__buffer_0 +.endm + + +.macro _PRINT_CONTEXT + swi r31, r0, _print_context__buffer_0 + + _PRINT_ASCII_BREAK + + + add r31, r0, r0 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r1 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r2 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r3 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r4 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r5 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r6 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r7 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_BREAK + + + add r31, r0, r8 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r9 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r10 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r11 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r12 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r13 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r14 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r15 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_BREAK + + + add r31, r0, r16 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r17 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r18 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r19 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r20 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r21 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r22 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r23 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_BREAK + + + add r31, r0, r24 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r25 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r26 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r27 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r28 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r29 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + add r31, r0, r30 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + lwi r31, r0, _print_context__buffer_0 + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_BREAK + + + mfs r31, rmsr + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + lwi r31, r0, _current_context_label + _PRINT_HEX32__ARG_31 + _PRINT_ASCII_SPACE + + + lwi r31, r0, _print_context__buffer_0 +.endm + + +.macro _PRINT_ASCII_STOP__VARIABLES + .align 4 + _print_ascii_stop__buffer_0: .space 1*4 +.endm + + +.macro _PRINT_ASCII_RUN__VARIABLES + .align 4 + _print_ascii_run__buffer_0: .space 1*4 +.endm + + +.macro _PRINT_ASCII_BREAK__VARIABLES + .align 4 + _print_ascii_break__buffer_0: .space 1*4 +.endm + + +.macro _PRINT_ASCII_SPACE__VARIABLES + .align 4 + _print_ascii_space__buffer_0: .space 1*4 +.endm + + +.macro _PRINT_HEX8__VARIABLES + .align 4 + _print_hex8__buffer_0: .space 1*4 + .align 4 + _print_hex8__buffer_1: .space 1*4 +.endm + + +.macro _PRINT_HEX32__VARIABLES + .align 4 + _print_hex32__buffer_0: .space 1*4 + .align 4 + _print_hex32__buffer_1: .space 1*4 +.endm + + +.macro _PRINT_CONTEXT__VARIABLES + .align 4 + _print_context__buffer_0: .space 1*4 +.endm + + + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/linker_commands.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/linker_commands.s new file mode 100644 index 000000000..baf1b04d9 --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/linker_commands.s @@ -0,0 +1,34 @@ +.macro _BEGIN_ELF_ENTRY_CODE + .global _start + .section ".Elf_entry" + + _start: +.endm + + +.macro _BEGIN_ATOMIC_OPS + .section ".Atomic_ops" + .global _atomic_ops_begin + .align 12 + _atomic_ops_begin: +.endm + + +.macro _END_ATOMIC_OPS + .global _atomic_ops_end + .align 12 + _atomic_ops_end: +.endm + + +.macro _BEGIN_READABLE_EXECUTABLE + .section ".text" +.endm + + +.macro _BEGIN_READABLE_WRITEABLE + .section ".bss" +.endm + + + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/special_registers.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/special_registers.s new file mode 100755 index 000000000..764a47770 --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/include/special_registers.s @@ -0,0 +1,59 @@ +/** + * Assembler macros for machine status register access + * + * \author Martin Stein + * \date 2010-10-05 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +.macro _SYNCHRONIZING_OP + bri 4 +.endm + + +.macro _AWAIT_DELAY_OP + or r0, r0, r0 +.endm + + +.macro _ENABLE_EXCEPTIONS + msrset r0, 0x100 + _SYNCHRONIZING_OP +.endm + + +.macro _DISABLE_EXCEPTIONS + msrclr r0, 0x100 + _SYNCHRONIZING_OP +.endm + + +.macro _ENABLE_INTERRUPTS + msrset r0, 0x002 + _SYNCHRONIZING_OP +.endm + + +.macro _DISABLE_INTERRUPTS + msrclr r0, 0x002 + _SYNCHRONIZING_OP +.endm + + +.macro _RELEASE_EXCEPTION + msrclr r0, 0x200 + _SYNCHRONIZING_OP +.endm + + +.macro _RELEASE_BREAK + msrclr r0, 0x008 + _SYNCHRONIZING_OP +.endm + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/kernel_entry.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/kernel_entry.s new file mode 100755 index 000000000..5954b95f9 --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/kernel_entry.s @@ -0,0 +1,209 @@ +/** + * Enter the kernel through interrupt, exception or syscall + * saves userland context state to execution context denoted at _userland_context + * + * \author Martin Stein + * \date 2010-10-06 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + + +.include "special_registers.s" +.include "errors.s" +.include "exec_context.s" +.include "linker_commands.s" + + +/* We have to know wich userland context was the last that was executed */ +.extern _userland_context + +/* We have to use the initial stack pointer of kernel to + * provide an execution context for every kernel entrance +*/ +.extern _kernel_stack_top + +/* Pointer to kernels handler routine for userland blockings */ +.extern _kernel +.extern _kernel_exit + +/* We have to use these labels when initializing cpu's entries */ +.global _syscall_entry +.global _exception_entry +.global _interrupt_entry + + + +.macro _MAY_BE_VERBOSE_KERNEL_ENTRY +/* + _PRINT_CONTEXT + _PRINT_ASCII_STOP +*/ +.endm + + +.macro _MAY_BE_VERBOSE_VARIABLES +/* + _PRINT_CONTEXT__VARIABLES + _PRINT_HEX32__VARIABLES + _PRINT_HEX8__VARIABLES + _PRINT_ASCII_SPACE__VARIABLES + _PRINT_ASCII_BREAK__VARIABLES + _PRINT_ASCII_RUN__VARIABLES + _PRINT_ASCII_STOP__VARIABLES +*/ +.endm + + +.macro _IF_BRANCH_WAS_BLOCKING_CAUSE__USES_R15 + mfs r15, resr + andi r15, r15, 0x00001000 + beqi r15, _end_if_branch_was_blocking_cause +.endm + + +.macro _SET_PRESTORED_RPC_TO_BRANCH_TARGET__USES_R15 + mfs r15, rbtr + swi r15, r0, _prestored_rpc +.endm + + +.macro _PRESTORE_CONTEXT_AT_INTERRUPT__USES_R1_R14_R15 + swi r14, r0, _prestored_rpc + swi r1, r0, _prestored_r1 + swi r15, r0, _prestored_r15 +.endm + + +.macro _BLOCKING_TYPE_IS_INTERRUPT__USES_R3_R15 + addi r3, r0, 1 + lwi r15, r0, _userland_context + _SAVE_BLOCKING_TYPE_TO_CONTEXT__USES_R3_R15 +.endm + + +.macro _PRESTORE_CONTEXT_AT_EXCEPTION__USES_R1_R15_R17 + swi r17, r0, _prestored_rpc + swi r1, r0, _prestored_r1 + swi r15, r0, _prestored_r15 + + _IF_BRANCH_WAS_BLOCKING_CAUSE__USES_R15 + + _SET_PRESTORED_RPC_TO_BRANCH_TARGET__USES_R15 + + _end_if_branch_was_blocking_cause: +.endm + + +.macro _BLOCKING_TYPE_IS_EXCEPTION__USES_R3_R15 + addi r3, r0, 2 + lwi r15, r0, _userland_context + _SAVE_BLOCKING_TYPE_TO_CONTEXT__USES_R3_R15 +.endm + + +.macro _PRESTORE_CONTEXT_AT_SYSCALL__USES_R1_R15 + swi r1, r0, _prestored_r1 + swi r0, r0, _prestored_r15 + swi r15, r0, _prestored_rpc +.endm + + +.macro _BLOCKING_TYPE_IS_SYSCALL__USES_R3_R15 + addi r3, r0, 3 + lwi r15, r0, _userland_context + _SAVE_BLOCKING_TYPE_TO_CONTEXT__USES_R3_R15 +.endm + + +.macro _BACKUP_PRESTORED_CONTEXT__USES_R2_TO_R31 + lwi r15, r0, _userland_context + + _SAVE_R2_TO_R13_TO_CONTEXT__USES_R2_TO_R13_R15 + _SAVE_R18_TO_R31_TO_CONTEXT__TO_R15_R18_TO_R31 + + _SAVE_RPID_TO_CONTEXT__USES_R3_R15 + _SAVE_RMSR_TO_CONTEXT__USES_R3_R15 + _SAVE_RESR_TO_CONTEXT__USES_R3_R15 + _SAVE_REAR_TO_CONTEXT__USES_R3_R15 + + _SAVE_PRESTORED_R1_R15_RPC_TO_CONTEXT__USES_R3_R15 +.endm + + +.macro _CALL_KERNEL__USES_R1_R15 + addi r1, r0, _kernel_stack_top + addi r15, r0, _exit_kernel-2*4 + + brai _kernel + or r0, r0, r0 +.endm + + + + +_BEGIN_READABLE_EXECUTABLE + + _interrupt_entry: + + _MAY_BE_VERBOSE_KERNEL_ENTRY + + _PRESTORE_CONTEXT_AT_INTERRUPT__USES_R1_R14_R15 + _BACKUP_PRESTORED_CONTEXT__USES_R2_TO_R31 + _BLOCKING_TYPE_IS_INTERRUPT__USES_R3_R15 + + _CALL_KERNEL__USES_R1_R15 + /* Kernel execution with return pointer set to _kernel_exit */ + + _end_interrupt_entry: + + + + + _exception_entry: + + _MAY_BE_VERBOSE_KERNEL_ENTRY + + _PRESTORE_CONTEXT_AT_EXCEPTION__USES_R1_R15_R17 + _BACKUP_PRESTORED_CONTEXT__USES_R2_TO_R31 + _BLOCKING_TYPE_IS_EXCEPTION__USES_R3_R15 + + _CALL_KERNEL__USES_R1_R15 + /* Kernel execution with return pointer set to _kernel_exit */ + + _end_exception_entry: + + + + + _syscall_entry: + + _MAY_BE_VERBOSE_KERNEL_ENTRY + + _PRESTORE_CONTEXT_AT_SYSCALL__USES_R1_R15 + _BACKUP_PRESTORED_CONTEXT__USES_R2_TO_R31 + _BLOCKING_TYPE_IS_SYSCALL__USES_R3_R15 + + _CALL_KERNEL__USES_R1_R15 + /* Kernel execution with return pointer set to _kernel_exit */ + + _end_syscall_entry: + + + + +_BEGIN_READABLE_WRITEABLE + + .global _current_context_label + .align 4 + _current_context_label: .space 1*4 + + _VARIABLES_TO_WRITE_EXEC_CONTEXT + _MAY_BE_VERBOSE_VARIABLES + + diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/platform.cc b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/platform.cc new file mode 100644 index 000000000..b22d58f54 --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/platform.cc @@ -0,0 +1,205 @@ +/* + * \brief Platform implementations + * \author Martin stein + * \date 2010-10-01 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Platform includes */ +#include +#include + +/* Kernel includes */ +#include +#include +#include + + +extern unsigned int _current_context_label; +using namespace Kernel; +using namespace Cpu; + + +void Platform::_on_kernel_entry() +{ + _on_kernel_entry__verbose__called(); + _on_kernel_entry__trace__thread_interrupts(); + _userland_context->holder->on_kernel_entry(); + platform()->userland_context(0); +} + + +Platform::Platform() +{ + _initial_tlb_entries(); + _init_userland_entry(); + _init_interrupt_entry(); + _init_syscall_entry(); + _init_exception_entry(); +} + + +void Platform_syscall::block() +{ + _id = (Syscall_id)_context->r31; + _block__verbose__success(); +} + + +void Platform_exception::block(Exec_context * c) +{ + _id = (Exception_id)((_context->resr & Context::RESR_EC_MASK) >> Context::RESR_EC_LSHIFT); +} + + +void Platform_irq::block() +{ + _id = platform()->irq_controller()->get_irq(); + +} + + +Protection_id Platform_exception::protection_id() { + return _owner->protection_id(); } + + +addr_t Platform_exception::address(){ return (addr_t)_context->rear; } + + +bool Platform_exception::attempted_write_access(){ + return (_context->resr & Exec_context::RESR_ESS_DATA_TLB_MISS_S_MASK); } + + +static bool _trace_current_kernel_pass; + +void Verbose::begin__trace_current_kernel_pass() +{ + enum { + TRACED_PROTECTION_IDS = + sizeof(trace_these_protection_ids)/ + sizeof(trace_these_protection_ids[0]), + TRACED_THREAD_IDS= + sizeof(trace_these_thread_ids)/ + sizeof(trace_these_thread_ids[0]), + TRACED_EXCEPTION_IDS= + sizeof(trace_these_exception_ids)/ + sizeof(trace_these_exception_ids[0]), + TRACED_SYSCALL_IDS= + sizeof(trace_these_syscall_ids)/ + sizeof(trace_these_syscall_ids[0]) + }; + + + if (!Verbose::TRACE_KERNEL_PASSES || !_userland_context) { + _trace_current_kernel_pass = false; + return; + } + + if (!TRACE_ALL_THREAD_IDS) { + for (unsigned int i = 0;; i++) { + if (i >= TRACED_THREAD_IDS) { + _trace_current_kernel_pass = false; + return; + } + if (trace_these_thread_ids[i] == (Thread_id)_current_context_label) + break; + } + } + + if (!TRACE_ALL_PROTECTION_IDS) { + for (unsigned int i=0;; i++) { + if (i>=TRACED_PROTECTION_IDS) { + _trace_current_kernel_pass = false; + return; + } + if (trace_these_protection_ids[i] + == (Protection_id)_userland_context->rpid) { + _trace_current_kernel_pass = true; + _prints_chr1('\n'); + return; + } + } + } + + if (_userland_context->blocking_type == Exec_context::IRQ_BLOCKING + && !TRACE_ALL_IRQ_IDS) { + + _trace_current_kernel_pass = false; + return; + } + + if (_userland_context->blocking_type == Exec_context::EXCEPTION_BLOCKING + && !TRACE_ALL_EXCEPTION_IDS) { + + for (unsigned int i=0;; i++) { + + if (i >= TRACED_EXCEPTION_IDS) { + _trace_current_kernel_pass = false; + return; + } + if (trace_these_exception_ids[i] == + (Exception_id)_userland_context->exception_cause()) { + + _trace_current_kernel_pass = true; + _prints_chr1('\n'); + return; + } + } + } + + if (_userland_context->blocking_type == Exec_context::EXCEPTION_BLOCKING + && !TRACE_ALL_EXCEPTION_IDS) { + + for (unsigned int i = 0;; i++) { + if (i >= TRACED_EXCEPTION_IDS) { + _trace_current_kernel_pass = false; + return; + } + if (trace_these_exception_ids[i] == (Exception_id)_userland_context->r31) { + _trace_current_kernel_pass=true; + _prints_chr1('\n'); + return; + } + } + } + _trace_current_kernel_pass = false; +} + + +bool Kernel::Verbose::trace_current_kernel_pass() +{ + return _trace_current_kernel_pass; +} + + +void Platform::_on_kernel_entry__trace__thread_interrupts() +{ + if (!PLATFORM__TRACE) return; + if (!Verbose::trace_current_kernel_pass()) return; + + _prints_str0("block("); + _prints_hex2((char)_userland_context->rpid); + _prints_str0(":"); + _prints_hex8(_userland_context->rpc); + _prints_str0(":"); + _prints_hex2((char)_userland_context->blocking_type); + + char subtype=0; + if (_userland_context->blocking_type == Exec_context::IRQ_BLOCKING){ + subtype=(char)platform()->irq_controller()->next_irq(); + } else if (_userland_context->blocking_type == Exec_context::EXCEPTION_BLOCKING){ + subtype=(char)_userland_context->resr; + } else if (_userland_context->blocking_type == Exec_context::SYSCALL_BLOCKING){ + subtype=(char)_userland_context->r31; + } + + _prints_str0(":"); + _prints_hex2(subtype); + _prints_str0(") "); +} diff --git a/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/userland_entry.s b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/userland_entry.s new file mode 100755 index 000000000..a9f32e42b --- /dev/null +++ b/base-mb/src/kernel/platforms/petalogix_s3adsp1800_mmu/userland_entry.s @@ -0,0 +1,223 @@ +/* + * \brief Userland entry + * \author Martin Stein + * \date 2010-10-05 + * + * Enter the userland via '_userland_entry' with execution context denoted in + * '_userland_context' + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +.include "special_registers.s" +.include "errors.s" +.include "exec_context.s" +.include "linker_commands.s" + +.global _userland_entry +.global _userland_context + +.global _kernel_timer_ctrl +.global _kernel_timer_ctrl_start + + +.macro _START_KERNEL_TIMER + swi r30, r0, _start_kernel_timer__buf_0 + swi r31, r0, _start_kernel_timer__buf_1 + + lwi r30, r0, _kernel_timer_ctrl + lwi r31, r0, _kernel_timer_ctrl_start + swi r31, r30, 0 + + lwi r30, r0, _start_kernel_timer__buf_0 + lwi r31, r0, _start_kernel_timer__buf_1 +.endm + + +.macro _START_KERNEL_TIMER__VARIABLES + .align 4 + _start_kernel_timer__buf_0: + .space 1*4 + .align 4 + _start_kernel_timer__buf_1: + .space 1*4 +.endm + + +.macro _MAY_BE_VERBOSE_USERLAND_ENTRY +/* + _PRINT_CONTEXT + _PRINT_ASCII_RUN +*/ +.endm + + +.macro _MAY_BE_VERBOSE_VARIABLES +/* + _PRINT_CONTEXT__VARIABLES + _PRINT_HEX32__VARIABLES + _PRINT_HEX8__VARIABLES + _PRINT_ASCII_SPACE__VARIABLES + _PRINT_ASCII_BREAK__VARIABLES + _PRINT_ASCII_RUN__VARIABLES + _PRINT_ASCII_STOP__VARIABLES +*/ +.endm + + +.macro _PREPARE_CONTEXT__USES_R2_TO_R31 + lwi r15, r0, _userland_context + + _PRELOAD_R1_R15_RPC_FROM_CONTEXT__USES_R3_R15 + + _LOAD_RPID_FROM_CONTEXT__USES_R3_R15 + _LOAD_RMSR_FROM_CONTEXT__USES_R3_R4_R15 + + _LOAD_R2_TO_R13_FROM_CONTEXT__USES_R2_TO_R13_R15 + _LOAD_R18_TO_R31_FROM_CONTEXT__TO_R15_R18_TO_R31 +.endm + + +.macro _EXEC_PREPARED_CONTEXT_CASE_INTERRUPT + lwi r14, r0, _preloaded_rpc + lwi r1, r0, _preloaded_r1 + lwi r15, r0, _preloaded_r15 + + _MAY_BE_VERBOSE_USERLAND_ENTRY + _START_KERNEL_TIMER + + rtid r14, 0 + or r0, r0, r0 +.endm + + +.macro _EXEC_PREPARED_CONTEXT_CASE_EXCEPTION + lwi r1, r0, _preloaded_r1 + lwi r17, r0, _preloaded_rpc + lwi r15, r0, _preloaded_r15 + + _MAY_BE_VERBOSE_USERLAND_ENTRY + _START_KERNEL_TIMER + + rted r17, 0 + or r0, r0, r0 +.endm + + +.macro _EXEC_PREPARED_CONTEXT_CASE_SYSCALL + lwi r1, r0, _preloaded_r1 + lwi r15, r0, _preloaded_rpc + + _MAY_BE_VERBOSE_USERLAND_ENTRY + _START_KERNEL_TIMER + + rtbd r15, 8 + or r0, r0, r0 +.endm + + +.macro _EXEC_PREPARED_CONTEXT_CASE_INITIAL + + lwi r14, r0, _preloaded_rpc + lwi r1, r0, _preloaded_r1 + lwi r15, r0, _preloaded_r15 + + _ENABLE_EXCEPTIONS + _START_KERNEL_TIMER + + rtid r14, 0 + or r0, r0, r0 +.endm + + +.macro _SWITCH_USERLAND_BLOCKING_TYPE__USES_R3_R15 + lwi r15, r0, _userland_context + _LOAD_BLOCKING_TYPE_FROM_CONTEXT__USES_R3_R15 +.endm + + +.macro _CASE_INITIAL__USES_R4_R3 + xori r4, r3, 0 + bnei r4, _end_case_initial +.endm + + +.macro _CASE_INTERRUPT__USES_R4_R3 + xori r4, r3, 1 + bnei r4, _end_case_interrupt +.endm + + +.macro _CASE_EXCEPTION__USES_R4_R3 + xori r4, r3, 2 + bnei r4, _end_case_exception +.endm + + +.macro _CASE_SYSCALL__USES_R4_R3 + xori r4, r3, 3 + bnei r4, _end_case_syscall +.endm + + +_BEGIN_READABLE_EXECUTABLE + + _userland_entry: + _SWITCH_USERLAND_BLOCKING_TYPE__USES_R3_R15 + _CASE_INTERRUPT__USES_R4_R3 + + _PREPARE_CONTEXT__USES_R2_TO_R31 + _EXEC_PREPARED_CONTEXT_CASE_INTERRUPT + /* userland execution */ + + _end_case_interrupt: + _CASE_EXCEPTION__USES_R4_R3 + + _PREPARE_CONTEXT__USES_R2_TO_R31 + _EXEC_PREPARED_CONTEXT_CASE_EXCEPTION + /* userland execution */ + + _end_case_exception: + _CASE_SYSCALL__USES_R4_R3 + + _PREPARE_CONTEXT__USES_R2_TO_R31 + _EXEC_PREPARED_CONTEXT_CASE_SYSCALL + /* userland execution */ + + _end_case_syscall: + _CASE_INITIAL__USES_R4_R3 + + _PREPARE_CONTEXT__USES_R2_TO_R31 + _EXEC_PREPARED_CONTEXT_CASE_INITIAL + /* userland execution */ + + _end_case_initial: + _case_default: + + _ERROR_UNKNOWN_USERLAND_BLOCKING_TYPE + /* system halted */ + + _end_case_default: + _end_switch_userland_blocking_type: + _end_userland_entry: + + +_BEGIN_READABLE_WRITEABLE + + .align 4 + _kernel_timer_ctrl: .space 1*4 + .align 4 + _kernel_timer_ctrl_start: .space 1*4 + _START_KERNEL_TIMER__VARIABLES + + _VARIABLES_TO_READ_EXEC_CONTEXT + _MAY_BE_VERBOSE_VARIABLES + + .align 4 + _userland_context: .space 1*4 + diff --git a/base-mb/src/platform/_main_helper.h b/base-mb/src/platform/_main_helper.h new file mode 100644 index 000000000..d0fc0df2c --- /dev/null +++ b/base-mb/src/platform/_main_helper.h @@ -0,0 +1,54 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Martin Stein + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__PLATFORM___MAIN_HELPER_H_ +#define _SRC__PLATFORM___MAIN_HELPER_H_ + +#include +#include +#include +#include + +using namespace Genode; + +extern Genode::Native_utcb* _main_utcb_addr; +extern Genode::Native_thread_id _main_thread_id; + + +Native_utcb* main_thread_utcb() { return _main_utcb_addr; } + + +static void main_thread_bootstrap() +{ + /* + * main thread has no Thread_base but he gets some informations about + * itself deposited by the programs parent + */ + + /* + * If we're another mainthread than that of core we overwrite the + * utcb-address with that one genode takes by convention for mainthreads on + * microblaze + */ + int volatile pid; + asm volatile ("mfs %0, rpid" : "=r"(pid) : :); + if (pid!=Roottask::PROTECTION_ID) { + _main_utcb_addr = (Native_utcb*)((Thread_base::CONTEXT_AREA_VIRTUAL_BASE + + Thread_base::CONTEXT_AREA_VIRTUAL_SIZE) + - sizeof(Native_utcb)); + } + + _main_thread_id=*((Native_thread_id*)main_thread_utcb()); +} + +#endif /* _SRC__PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-mb/src/platform/genode.ld b/base-mb/src/platform/genode.ld new file mode 100755 index 000000000..45a979bdd --- /dev/null +++ b/base-mb/src/platform/genode.ld @@ -0,0 +1,115 @@ +/* + * \brief Linker script for Genode programs + * \author Christian Helmuth + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +ENTRY(_start) + +PHDRS +{ + ro PT_LOAD FLAGS(5); + rw PT_LOAD FLAGS(6); +} + +SECTIONS +{ + . = 0x90000000; + _program_image_begin = .; + _executable_readable_begin = .; + + .text : { + *(.Atomic_ops) + + . = ALIGN(1<<12); + *(.Elf_entry) + *(.text .text.* .gnu.linkonce.t.*) + *(.fini) + *(.rodata .rodata.* .gnu.linkonce.r.*) + + . = ALIGN(1<<3); + _ctors_start = .; + KEEP (*(.ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.init_array)) /* list of constructors specific for ARM eabi */ + _ctors_end = .; + _dtors_start = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + _dtors_end = .; + + } : ro = 0x90909090 + + /* Linux: exception section for uaccess mechanism */ + __ex_table : { *(__ex_table) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } + _executable_readable_end = .; + + . = ALIGN(1<<12); + _writable_readable_begin = .; + + .data : SUBALIGN(1<<12) { + /* + * Leave space for parent capability parameters at start of data + * section. The protection domain creator is reponsible for storing + * sane values here. + */ + _parent_cap = .; + _parent_cap_thread_id = .; + LONG(0xffffffff); + _parent_cap_local_name = .; + LONG(0xffffffff); + + *(.data .data.* .gnu.linkonce.d.*) + } : rw + + /* exception frames for C++ */ + .eh_frame : { + __eh_frame_start__ = .; + KEEP (*(.eh_frame)) + LONG(0) + } : rw + + .init_array : { + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } + + .gcc_except_table : { KEEP(*(.gcc_except_table)) } + .dynamic : { *(.dynamic) } + + /* .ARM.exidx is sorted, so has to go in its own output section */ + __exidx_start = .; + .ARM.exidx : { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } + __exidx_end = .; + + .ARM.extab : { + *(.ARM.extab*) + } : rw + + .bss : { + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } + + /* end of program image -- must be after last section */ + _writeable_readable_end = .; + _program_image_end = .; + + /DISCARD/ : { + *(.note) + *(.note.ABI-tag) + *(.comment) + } +} diff --git a/base-mb/src/test/hello/main.cc b/base-mb/src/test/hello/main.cc new file mode 100644 index 000000000..f2f4d395f --- /dev/null +++ b/base-mb/src/test/hello/main.cc @@ -0,0 +1,23 @@ +/* + * \brief Hello world + * \author Norman Geske + * \date 2011-02-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +using namespace Genode; + +int main(int argc, char **argv) +{ + printf("Hello world!\n"); + return 0; +} diff --git a/base-mb/src/test/hello/target.mk b/base-mb/src/test/hello/target.mk new file mode 100644 index 000000000..ce1e067cd --- /dev/null +++ b/base-mb/src/test/hello/target.mk @@ -0,0 +1,3 @@ +TARGET = hello +SRC_CC = main.cc +LIBS = cxx env thread diff --git a/base-nova/Makefile b/base-nova/Makefile new file mode 100644 index 000000000..2e6ca23be --- /dev/null +++ b/base-nova/Makefile @@ -0,0 +1,47 @@ +# +# \brief Download, and unpack the NOVA hypervisor. +# \author Stefan Kalkowski +# \date 2011-07-20 +# + +VERBOSE ?= @ +ECHO = @echo +DOWNLOAD_DIR = download +CONTRIB_DIR = contrib +NOVA_ARCHIVE = nova-hypervisor-0.4.tar.bz2 +NOVA_URI = http://os.inf.tu-dresden.de/~us15/nova/$(NOVA_ARCHIVE) + +# +# Print help information by default +# +help: + $(ECHO) + $(ECHO) "Prepare the NOVA base repository" + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - download and extract the NOVA source code" + $(ECHO) "clean - clean everything except downloaded archives" + $(ECHO) "cleanall - clean everything including downloaded archives" + $(ECHO) + +$(DOWNLOAD_DIR)/$(NOVA_ARCHIVE): + $(ECHO) "downloading source code to '$(DOWNLOAD_DIR)/'" + $(VERBOSE)mkdir -p $(DOWNLOAD_DIR) + $(VERBOSE)wget -c $(NOVA_URI) -O $@ + +$(CONTRIB_DIR): clean + +$(CONTRIB_DIR): $(DOWNLOAD_DIR)/$(NOVA_ARCHIVE) + $(ECHO) "unpacking source code to '$(CONTRIB_DIR)/'" + $(VERBOSE)tar xjf $< + $(VERBOSE)mv hypervisor $@ + $(VERBOSE)patch -d $@ -p1 < patches/utcb.patch + $(VERBOSE)touch $@ + +prepare: $(CONTRIB_DIR) + +clean: + $(VERBOSE)rm -rf $(CONTRIB_DIR) + +cleanall: clean + $(VERBOSE)rm -rf $(DOWNLOAD_DIR) diff --git a/base-nova/README b/base-nova/README new file mode 100644 index 000000000..5222292c5 --- /dev/null +++ b/base-nova/README @@ -0,0 +1,10 @@ +This repository contains the port of Genode to the NOVA microhypervisor. + +For more information on this base platform, please refer to the official +website. + +:[http://hypervisor.org]: Official website for the NOVA microhypervisor. + +For information on using Genode on NOVA, please revisit the documentation at +'base-nova/doc/nova.txt': + diff --git a/base-nova/doc/nova.txt b/base-nova/doc/nova.txt new file mode 100644 index 000000000..6ebad393c --- /dev/null +++ b/base-nova/doc/nova.txt @@ -0,0 +1,254 @@ + + ========================================== + How to use Genode with the NOVA hypervisor + ========================================== + + Norman Feske + + +When we started the development of Genode in 2006 at the OS Group of the TU +Dresden, it was originally designated to be the user land of a next-generation +and to-be-developed new kernel called NOVA. Because the kernel was not ready at +that time, we had to rely on intermediate solutions as kernel platform such as +L4/Fiasco and Linux during development. These circumstances led us to the +extremely portable design that Genode has today and motivated us to make Genode +available on the whole family of L4 microkernels. In December 2009, the day we +waited for a long time had come. The first version of NOVA was publicly +released: + +:Official website of the NOVA hypervisor: + [http://hypervisor.org] + +Besides the novel and modern kernel interface, NOVA has a list of features that +sets it apart from most other microkernels, in particular support for +virtualization hardware, multi-processor support, and capability-based +security. + + +Why bringing Genode to NOVA? +############################ + +NOVA is an acronym for NOVA OS Virtualization Architecture. It stands for a +radically new approach of combining full x86 virtualization with microkernel +design principles. Because NOVA is a microkernelized hypervisor, the term +microhypervisor was coined. In its current form, it successfully addresses +three main challenges. First, how to consolidate a microkernel system-call API +with a hypercall API in such a way that the API remains orthogonal? The answer +to this question lies in NOVA's unique IPC interface. Second, how to implement +a virtual machine monitor outside the hypervisor without spoiling +performance? The Vancouver virtual machine monitor that runs on top NOVA proves +that a decomposition at this system level is not only feasible but can yield +high performance. Third, being a modern microkernel, NOVA set out to pursue a +capability-based security model, which is a challenge on its own. + +Up to now, the NOVA developers were most concerned about optimizing and +evaluating NOVA for the execution of virtual machines, not so much about +running a fine-grained decomposed multi-server operating system. This is where +Genode comes into play. With our port of Genode to NOVA, we contribute the +workload to evaluate NOVA's kernel API against this use case. We are happy to +report that the results so far are overly positive. + +At this point, we want to thank the main developers of NOVA Udo Steinberg and +Bernhard Kauer for making their exceptional work and documentation publicly +available, and for being so responsive to our questions. We also greatly +enjoyed the technical discussions we had and look forward to the future +evolution of NOVA. + + +How to explore Genode on NOVA? +############################## + +To download the NOVA kernel and integrate it with Genode, issue the following +command from within the 'base-nova' directory: + +! make prepare + +For creating a preconfigured build directory prepared for compiling Genode for +NOVA, use the 'create_builddir' tool: + +! /tool/create_builddir nova_x86 BUILD_DIR= + +This tool will create a fresh build directory at the location specified +as 'BUILD_DIR'. Provided that you have installed the +[http://genode.org/download/tool-chain - Genode tool chain], you can now build +the NOVA kernel via + +! make kernel + +For test driving Genode on NOVA directly from the build directory, you can use +Genode's run mechanism. For example, the following command builds and executes +Genode's graphical demo scenario on Qemu: + +! make run/demo + + +Challenges +########## + +From all currently supported base platforms of Genode, the port to NOVA was +the most venturesome effort. It is the first platform with kernel support for +capabilities and local names. That means no process except the kernel has +global knowledge. This raises a number of questions that seem extremely hard +to solve at the first sight. For example: There are no global IDs for threads +and other kernel objects. So how to address the destination for an IPC message? +Or another example: A thread does not know its own identity per se and there is +no system call similar to 'getpid' or 'l4_myself', not even a way to get a +pointer to a thread's own user-level thread-control block (UTCB). The UTCB, +however, is needed to invoke system calls. So how can a thread obtain its UTCB +in order to use system calls? The answers to these questions must be provided by +user-level concepts. Fortunately, Genode was designed for a capability kernel +right from the beginning so that we already had solutions to most of these +questions. In the following, we give a brief summary of the specifics of Genode +on NOVA: + +* We maintain our own system-call bindings for NOVA ('base-nova/include/nova/') + derived from the NOVA specification. We put the bindings under MIT license + to encourage their use outside of Genode. + +* Core runs directly as roottask on the NOVA hypervisor. On startup, core + maps the complete I/O port range to itself and implements debug output via + comport 0. + +* Because NOVA does not allow rootask to have a BSS segment, we need a slightly + modified linker script for core (see 'src/platform/roottask.ld'). + All other Genode programs use Genode's generic linker script. + +* The Genode 'Capability' type consists of a portal selector expressing the + destination of a capability invocation and a global object ID expressing + the identity of the object when the capability is specified as an invocation + argument. In the latter case, the global ID is needed because of a limitation + of the current system-call interface. In the future, we are going to entirely + remove the global ID. + +* Thread-local data such as the UTCB pointer is provided by the new thread + context management introduced with the Genode release 10.02. It enables + each thread to determine its thread-local data using the current stack + pointer. + +* NOVA provides threads without time called local execution contexts (EC). + Local ECs are intended as server-side RPC handlers. The processing time + needed to perform RPC requests is provided by the client during the RPC call. + This way, RPC semantics becomes very similar to function call semantics with + regard to the accounting of CPU time. Genode already distinguishes normal + threads (with CPU time) and server-side RPC handlers ('Server_activation') + and, therefore, can fully utilize this elegant mechanism without changing the + Genode API. + +* On NOVA, there are no IPC send or IPC receive operations. Hence, this part + of Genode's IPC framework cannot be implemented on NOVA. However, the + corresponding classes 'Ipc_istream' and 'Ipc_ostream' are never used directly + but only as building blocks for the actually used 'Ipc_client' and + 'Ipc_server' classes. Compared with the other Genode base platforms, Genode's + API for synchronous IPC communication maps more directly onto the NOVA + system-call interface. + +* The Lock implementation utilizes NOVA's semaphore as a utility to let a + thread block in the attempt to get a contended lock. In contrast to the + intuitive way of using one kernel semaphore for each user lock, we use only + one kernel semaphore per thread and the peer-to-peer wake-up mechanism we + introduced in the release 9.08. This has two advantages: First, a lock does + not consume a kernel resource, and second, the full semantics of the Genode + lock including the 'cancel-blocking' semantics are preserved. + +* NOVA does not support server-side out-of-order processing of RPC requests. + This is particularly problematic in three cases: Page-fault handling, signal + delivery, and the timer service. + + A page-fault handler can receive a page fault request only if the previous + page fault has been answered. However, if there is no answer for a + page-fault, the page-fault handler has to decide whether to reply with a + dummy answer (in this case, the faulter will immediately raise the same page + fault again) or block until the page-fault can be resolved. But in the latter + case, the page-fault handler cannot handle any other page faults. This is + unfeasible if there is only one page-fault handler in the system. Therefore, + we instantiate one pager per user thread. This way, we can block and unblock + individual threads when faulting. + + Another classical use case for out-of-order RPC processing is signal + delivery. Each process has a signal-receiver thread that blocks at core's + signal service using an RPC call. This way, core can selectively deliver + signals by replying to one of these in-flight RPCs with a zero-timeout + response (preserving the fire-and-forget signal semantics). On NOVA however, + a server cannot have multiple RPCs in flight. Hence, we use a NOVA semaphore + shared between core and the signal-receiver thread to wakeup the + signal-receiver on the occurrence of a signal. Because a semaphore-up + operation does not carry payload, the signal has to perform a non-blocking + RPC call to core to pick up the details about the signal. Thanks to Genode's + RPC framework, the use of the NOVA semaphore is hidden in NOVA-specific stub + code for the signal interface and remains completely transparent at API + level. + + For the timer service, we currently use one thread per client to avoid the need + for out-of-order RPC processing. + +* Because NOVA provides no time source, we use the x86 PIT as user-level time + source, similar as on OKL4. + +* On the current version of NOVA, kernel capabilities are delegated using IPC. + Genode supports this scheme by being able to marshal 'Capability' objects as + RPC message payload. In contrast to all other Genode base platforms where + the 'Capability' object is just plain data, the NOVA version must marshal + 'Capability' objects such that the kernel translates the sender-local name to + the receiver-local name. This special treatment is achieved by overloading + the marshalling and unmarshalling operators of Genode's RPC framework. The + transfer of capabilities is completely transparent at API level and no + modification of existing RPC stub code was needed. + + +Manually booting Genode on NOVA +############################### + +NOVA supports multi-boot-compliant boot loaders such as GRUB, Pulsar, or gPXE. +For example, a GRUB configuration entry for booting the Genode demo scenario +with NOVA looks as follows, whereas 'genode/' is a symbolic link to the 'bin/' +subdirectory of the Genode build directory and the 'config' file is a copy of +'os/config/demo'. + +! title Genode demo scenario +! kernel /hypervisor noapic +! module /genode/core +! module /genode/init +! module /config/demo/config +! module /genode/timer +! module /genode/ps2_drv +! module /genode/pci_drv +! module /genode/vesa_drv +! module /genode/launchpad +! module /genode/nitpicker +! module /genode/liquid_fb +! module /genode/nitlog +! module /genode/testnit +! module /genode/scout + + +Limitations +########### + +The current NOVA version of Genode is able to run the complete Genode demo +scenario including several device drivers (PIT, PS/2, VESA, PCI) and the GUI. +Still the NOVA support is not on par with some of the other platforms. +The current limitations are: + +* No real-time priority support: NOVA supports priority-based scheduling + but, in the current version, it allows each thread to create scheduling + contexts with arbitrary scheduling parameters. This makes it impossible + to enforce priority assignment from a central point as facilitated with + Genode's priority concept. + +* No multi-processor support: NOVA supports multi-processor CPUs through + binding each execution context (ECs) to a particular CPU. Because everyone + can create ECs, every process could use multiple CPUs. However, Genode's API + devises a more restrictive way of allocating and assigning resources. In + short, physical resource usage should be arbitrated by core and the creation + of physical ECs should be performed by core only. However, Remote EC creation + is not yet supported by NOVA. Even though, multiple CPU can be used with + Genode on NOVA right now by using NOVA system calls directly, there is no + support at the Genode API level. + +* No cancel-blocking semantics: The cancellation of locks is not support, + yet. Because of this missing functionality, applications can freeze + in situations where a subsystems that blocks for a service is attempted + to get destroyed. + + + diff --git a/base-nova/etc/specs.conf b/base-nova/etc/specs.conf new file mode 100644 index 000000000..d22297dd0 --- /dev/null +++ b/base-nova/etc/specs.conf @@ -0,0 +1,5 @@ +# +# Description of build platform +# + +SPECS ?= genode nova x86_32 diff --git a/base-nova/include/base/cap_sel_alloc.h b/base-nova/include/base/cap_sel_alloc.h new file mode 100644 index 000000000..b5c8191a1 --- /dev/null +++ b/base-nova/include/base/cap_sel_alloc.h @@ -0,0 +1,72 @@ +/* + * \brief Interface for process-local capability-selector allocation + * \author Norman Feske + * \date 2010-01-19 + * + * This interface is NOVA-specific and not part of the Genode API. It should + * only be used internally by the framework or by NOVA-specific code. The + * implementation of the interface is part of the environment library. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CAP_SEL_ALLOC_H_ +#define _INCLUDE__BASE__CAP_SEL_ALLOC_H_ + +#include + +namespace Genode { + + class Cap_selector_allocator + { + public: + + /** + * Constructor + */ + Cap_selector_allocator(); + + /** + * Allocate range of capability selectors + * + * \param num_caps_log2 number of capability selectors specified as + * as the power of two. By default, the function + * returns a single capability selector. + * \return first capability selector of allocated range, + * or 0 if allocation failed + * + * The allocated range will be naturally aligned according to the + * specified 'num_cap_log2' value. + */ + addr_t alloc(size_t num_caps_log2 = 0); + + /** + * Release range of capability selectors + * + * \param cap first capability selector of range + * \param num_caps_log2 number of capability selectors specified + * as the power of two + */ + void free(addr_t cap, size_t num_caps_log2); + + /** + * Capability selector of local protection domain + * + * \return PD selector + */ + static unsigned pd_sel(); + }; + + /** + * Return singleton instance of 'Cap_selector_allocator' + */ + Cap_selector_allocator *cap_selector_allocator(); +} + +#endif /* _INCLUDE__BASE__CAP_SEL_ALLOC_H_ */ + diff --git a/base-nova/include/base/ipc.h b/base-nova/include/base/ipc.h new file mode 100644 index 000000000..bd95d1b7a --- /dev/null +++ b/base-nova/include/base/ipc.h @@ -0,0 +1,36 @@ +/* + * \brief NOVA-specific supplements to the IPC framework + * \author Norman Feske + * \date 2010-01-27 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_H_ +#define _INCLUDE__BASE__IPC_H_ + +#include + + +inline void Genode::Ipc_ostream::_marshal_capability(Genode::Native_capability const &cap) +{ + long unique_id = cap.unique_id(); + _write_to_buf(unique_id); + _snd_msg->snd_append_pt_sel(cap.pt_sel()); +} + + +inline void Genode::Ipc_istream::_unmarshal_capability(Genode::Native_capability &cap) +{ + long unique_id = 0; + _read_from_buf(unique_id); + int pt_sel = _rcv_msg->rcv_pt_sel(); + cap = Native_capability(pt_sel, unique_id); +} + +#endif /* _INCLUDE__BASE__IPC_H_ */ diff --git a/base-nova/include/base/ipc_msgbuf.h b/base-nova/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..5da4e3f3b --- /dev/null +++ b/base-nova/include/base/ipc_msgbuf.h @@ -0,0 +1,186 @@ +/* + * \brief IPC message buffer layout for NOVA + * \author Norman Feske + * \date 2009-10-02 + * + * On NOVA, we use IPC to transmit plain data and for capability delegation. + * Therefore the message buffer contains both categories of payload. The + * capability-specific part are the members '_snd_pt*' (sending capability + * selectors) and '_rcv_pt*' (receiving capability selectors). + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +/* Genode includes */ +#include + +/* NOVA includes */ +#include + +namespace Genode { + + class Msgbuf_base + { + public: + + enum { MAX_CAP_ARGS_LOG2 = 2, MAX_CAP_ARGS = 1 << MAX_CAP_ARGS_LOG2 }; + + protected: + + size_t _size; + + /** + * Number of portal-capability selectors to send + */ + size_t _snd_pt_sel_cnt; + + /** + * Portal capability selectors to delegate + */ + int _snd_pt_sel[MAX_CAP_ARGS]; + + /** + * Base of portal receive window + */ + int _rcv_pt_base; + + /** + * Read counter for unmarshalling portal capability selectors + */ + int _rcv_pt_sel_cnt; + + /** + * Flag set to true if receive window must be re-initialized + */ + bool _rcv_dirty; + + char _msg_start[]; /* symbol marks start of message */ + + public: + + /** + * Constructor + */ + Msgbuf_base() : _rcv_dirty(true) + { + rcv_reset(); + snd_reset(); + } + + /* + * Begin of actual message buffer + */ + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; } + + /** + * Return address of message buffer + */ + inline void *addr() { return &_msg_start[0]; } + + /** + * Reset portal capability selector payload + */ + inline void snd_reset() { _snd_pt_sel_cnt = 0; } + + /** + * Append portal capability selector to message buffer + */ + inline bool snd_append_pt_sel(int pt_sel) + { + if (_snd_pt_sel_cnt >= MAX_CAP_ARGS - 1) + return false; + + _snd_pt_sel[_snd_pt_sel_cnt++] = pt_sel; + return true; + } + + /** + * Return number of marshalled portal-capability selectors + */ + inline size_t snd_pt_sel_cnt() { return _snd_pt_sel_cnt; } + + /** + * Return portal capability selector + * + * \param i index (0 ... 'pt_sel_cnt()' - 1) + * \return portal-capability selector, or + * -1 if index is invalid + */ + int snd_pt_sel(unsigned i) { return i < _snd_pt_sel_cnt ? _snd_pt_sel[i] : -1; } + + /** + * Request current portal-receive window + */ + int rcv_pt_base() { return _rcv_pt_base; } + + /** + * Reset portal-capability receive window + */ + void rcv_reset() { _rcv_pt_sel_cnt = 0; } + + /** + * Return received portal-capability selector + */ + int rcv_pt_sel() + { + _rcv_dirty = true; + return _rcv_pt_base + _rcv_pt_sel_cnt++; + } + + /** + * Return true if receive window must be re-initialized + * + * After reading portal selectors from the message buffer using + * 'rcv_pt_sel()', we assume that the IDC call populared the + * current receive window with one or more portal capabilities. + * To enable the reception of portal capability selectors for the + * next IDC, we need a fresh receive window. + */ + bool rcv_dirty() { return _rcv_dirty; } + + /** + * Initialize receive window for portal capability selectors + * + * \param utcb UTCB of designated receiver thread + * + * Depending on the 'rcv_dirty' state of the message buffer, this + * function allocates a fresh receive window and clears 'rcv_dirty'. + */ + void rcv_prepare_pt_sel_window(Nova::Utcb *utcb) + { + if (rcv_dirty()) { + _rcv_pt_base = cap_selector_allocator()->alloc(MAX_CAP_ARGS_LOG2); + _rcv_dirty = false; + } + + /* register receive window at the UTCB */ + utcb->crd_rcv = Nova::Obj_crd(rcv_pt_base(),MAX_CAP_ARGS_LOG2); + } + }; + + + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-nova/include/base/ipc_pager.h b/base-nova/include/base/ipc_pager.h new file mode 100644 index 000000000..6bdac702c --- /dev/null +++ b/base-nova/include/base/ipc_pager.h @@ -0,0 +1,137 @@ +/* + * \brief Low-level page-fault handling + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* NOVA includes */ +#include + +namespace Genode { + + class Mapping + { + private: + + addr_t _dst_addr; + addr_t _core_local_addr; + bool _write_combined; + size_t _size_log2; + bool _rw; + + enum { PAGE_SIZE_LOG2 = 12 }; + + public: + + /** + * Constructor + */ + Mapping(addr_t dst_addr, addr_t map_addr, + bool write_combined, unsigned size_log2 = PAGE_SIZE_LOG2, + bool rw = true) + : + _dst_addr(dst_addr), _core_local_addr(map_addr), + _write_combined(write_combined), _size_log2(size_log2), + _rw(rw) + { } + + /** + * Construct invalid mapping + */ + Mapping() : _size_log2(0) { } + + void prepare_map_operation() { } + + Nova::Mem_crd mem_crd() + { + return Nova::Mem_crd(_core_local_addr >> PAGE_SIZE_LOG2, + _size_log2 - PAGE_SIZE_LOG2, + Nova::Rights(true, _rw, true)); + } + + addr_t dst_addr() { return _dst_addr; } + }; + + + class Ipc_pager + { + private: + + /** + * Page-fault type + */ + enum Pf_type { + TYPE_READ = 0x4, + TYPE_WRITE = 0x2, + TYPE_EXEC = 0x1, + }; + + addr_t _fault_ip; + addr_t _fault_addr; + Pf_type _fault_type; + + public: + + /** + * Wait for page-fault info + * + * After returning from this call, 'fault_ip' and 'fault_addr' + * have a defined state. + */ + void wait_for_fault(); + + /** + * Answer current page fault + */ + void reply_and_wait_for_fault(); + + /** + * Request instruction pointer of current fault + */ + addr_t fault_ip() { return _fault_ip; } + + /** + * Request page-fault address of current fault + */ + addr_t fault_addr() { return _fault_addr; } + + /** + * Set page-fault reply parameters + */ + void set_reply_mapping(Mapping m); + + /** + * Return true if fault was a write fault + */ + bool is_write_fault() const { return _fault_type == TYPE_WRITE; } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + /* + * Reflection of exceptions is not supported on this platform. + */ + return false; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-nova/include/base/native_types.h b/base-nova/include/base/native_types.h new file mode 100644 index 000000000..9ccb8cb01 --- /dev/null +++ b/base-nova/include/base/native_types.h @@ -0,0 +1,88 @@ +/* + * \brief Platform-specific type definitions + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +namespace Genode { + + typedef volatile int Native_lock; + + struct Native_thread + { + int ec_sel; /* NOVA cap selector for execution context */ + int sc_sel; /* NOVA cap selector for scheduling context */ + int rs_sel; /* NOVA cap selector for running semaphore */ + int pd_sel; /* NOVA cap selector of protection domain */ + int exc_pt_sel; /* base of event portal window */ + }; + + typedef Native_thread Native_thread_id; + + inline bool operator == (Native_thread_id t1, Native_thread_id t2) { return t1.ec_sel == t2.ec_sel; } + inline bool operator != (Native_thread_id t1, Native_thread_id t2) { return t1.ec_sel != t2.ec_sel; } + + class Native_utcb + { + private: + + /** + * Size of the NOVA-specific user-level thread-control block + */ + enum { UTCB_SIZE = 4096 }; + + /** + * User-level thread control block + * + * The UTCB is one 4K page, shared between the kernel and the + * user process. It is not backed by a dataspace but provided + * by the kernel. + */ + long _utcb[UTCB_SIZE/sizeof(long)]; + }; + + class Native_capability + { + private: + + int _pt_sel; + int _unique_id; + + public: + + /** + * Default constructor creates an invalid capability + */ + Native_capability() : _pt_sel(0), _unique_id(0) { } + + /** + * Construct capability manually + * + * This constructor should be called only from the platform-specific + * part of the Genode framework. + */ + Native_capability(int pt_sel, int unique_id) + : _pt_sel(pt_sel), _unique_id(unique_id) { } + + bool valid() const { return _pt_sel != 0 && _unique_id != 0; } + int local_name() const { return _unique_id; } + int dst() const { return _pt_sel; } + + int unique_id() const { return _unique_id; } + int pt_sel() const { return _pt_sel; } + }; + + typedef int Native_connection_state; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-nova/include/base/pager.h b/base-nova/include/base/pager.h new file mode 100644 index 000000000..9764b2136 --- /dev/null +++ b/base-nova/include/base/pager.h @@ -0,0 +1,154 @@ +/* + * \brief Paging-server framework + * \author Norman Feske + * \date 2006-04-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__PAGER_H_ +#define _INCLUDE__BASE__PAGER_H_ + +#include +#include +#include +#include +#include +#include + +namespace Genode { + + class Pager_entrypoint; + + /* + * On NOVA, each pager object is an EC that corresponds to one user thread. + */ + class Pager_object : public Object_pool::Entry, + Thread_base + { + private: + + void entry() { } + void start() { } + + unsigned long _badge; /* used for debugging */ + + /** + * User-level signal handler registered for this pager object via + * 'Cpu_session::exception_handler()'. + */ + Signal_context_capability _exception_sigh; + + int _exc_pt_sel; /* base of event portal window */ + int _pt_sel; /* portal selector for object identity */ + + addr_t _initial_esp; + addr_t _initial_eip; + + static void _page_fault_handler(); + static void _startup_handler(); + static void _invoke_handler(); + + public: + + Pager_object(unsigned long badge); + + virtual ~Pager_object(); + + unsigned long badge() const { return _badge; } + + virtual int pager(Ipc_pager &ps) = 0; + + /** + * Assign user-level exception handler for the pager object + */ + void exception_handler(Signal_context_capability sigh) + { + _exception_sigh = sigh; + } + + /** + * Return base of initial portal window + */ + int exc_pt_sel() { return _exc_pt_sel; } + + /** + * Set initial stack pointer used by the startup handler + */ + void initial_esp(addr_t esp) { _initial_esp = esp; } + + /** + * Set initial instruction pointer used by the startup handler + */ + void initial_eip(addr_t eip) { _initial_eip = eip; } + + /** + * Return portal capability selector used for object identity + */ + int pt_sel() { return _pt_sel; } + + /** + * Continue execution of pager object + */ + void wake_up(); + + /** + * Notify exception handler about the occurrence of an exception + */ + void submit_exception_signal() + { + if (!_exception_sigh.valid()) return; + + Signal_transmitter transmitter(_exception_sigh); + transmitter.submit(); + } + }; + + + /** + * Dummy pager activation + * + * Because on NOVA each pager object can be invoked separately, + * there is no central pager activation. + */ + class Pager_activation_base { }; + + + template + class Pager_activation : public Pager_activation_base + { }; + + + /** + * Dummy pager entrypoint + */ + class Pager_entrypoint : public Object_pool + { + private: + + Cap_session *_cap_session; + + public: + + Pager_entrypoint(Cap_session *cap_session, + Pager_activation_base *a = 0) + : _cap_session(cap_session) { } + + /** + * Return capability for 'Pager_object' + */ + Pager_capability manage(Pager_object *obj); + + /** + * Dissolve 'Pager_object' from entry point + */ + void dissolve(Pager_object *obj); + }; +} + +#endif /* _INCLUDE__BASE__PAGER_H_ */ diff --git a/base-nova/include/base/sleep.h b/base-nova/include/base/sleep.h new file mode 100644 index 000000000..3b155ea47 --- /dev/null +++ b/base-nova/include/base/sleep.h @@ -0,0 +1,34 @@ +/* + * \brief Lay back and relax + * \author Norman Feske + * \date 2010-02-01 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SLEEP_H_ +#define _INCLUDE__BASE__SLEEP_H_ + +/* Genode includes */ +#include +#include + +/* NOVA includes */ +#include + +namespace Genode { + + __attribute__((noreturn)) inline void sleep_forever() + { + int sleep_sm_sel = cap_selector_allocator()->alloc(); + Nova::create_sm(sleep_sm_sel, Cap_selector_allocator::pd_sel(), 0); + while (1) Nova::sm_ctrl(sleep_sm_sel, Nova::SEMAPHORE_DOWN); + } +} + +#endif /* _INCLUDE__BASE__SLEEP_H_ */ diff --git a/base-nova/include/nova/stdint.h b/base-nova/include/nova/stdint.h new file mode 100644 index 000000000..a8d107ad1 --- /dev/null +++ b/base-nova/include/nova/stdint.h @@ -0,0 +1,28 @@ +/* + * \brief Integer type definitions used by NOVA syscall bindings + * \author Norman Feske + * \date 2010-01-15 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__NOVA_STDINT_H_ +#define _PLATFORM__NOVA_STDINT_H_ + +#include + +namespace Nova { + + typedef long mword_t; + typedef unsigned char uint8_t; + typedef Genode::uint16_t uint16_t; + typedef Genode::uint32_t uint32_t; + typedef Genode::uint64_t uint64_t; +} + +#endif /* _PLATFORM__NOVA_STDINT_H_ */ diff --git a/base-nova/include/nova/syscalls.h b/base-nova/include/nova/syscalls.h new file mode 100644 index 000000000..a73f244b0 --- /dev/null +++ b/base-nova/include/nova/syscalls.h @@ -0,0 +1,673 @@ +/* + * \brief Syscall bindings for the NOVA microhypervisor + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2009-12-27 + */ + +/* + * Copyright (c) 2009 Genode Labs + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _PLATFORM__NOVA_SYSCALLS_H_ +#define _PLATFORM__NOVA_SYSCALLS_H_ + +#include + +#include + +#define ALWAYS_INLINE __attribute__((always_inline)) + +namespace Nova { + + enum { + PAGE_SIZE_LOG2 = 12, + PAGE_SIZE = 1 << PAGE_SIZE_LOG2, + PAGE_MASK = ~(PAGE_SIZE - 1) + }; + + /** + * NOVA sytem-call IDs + */ + enum Syscall { + NOVA_CALL = 0x0, + NOVA_REPLY = 0x1, + NOVA_CREATE_PD = 0x2, + NOVA_CREATE_EC = 0x3, + NOVA_CREATE_SC = 0x4, + NOVA_CREATE_PT = 0x5, + NOVA_CREATE_SM = 0x6, + NOVA_REVOKE = 0x7, + NOVA_LOOKUP = 0x8, + NOVA_EC_CTRL = 0x9, + NOVA_SC_CTRL = 0xa, + NOVA_SM_CTRL = 0xb, + NOVA_ASSIGN_PCI = 0xc, + NOVA_ASSIGN_GSI = 0xd, + }; + + + /** + * Hypervisor information page + */ + struct Hip + { + struct Mem_desc + { + enum Type { + MULTIBOOT_MODULE = -2, + MICROHYPERVISOR = -1, + AVAILABLE_MEMORY = 1, + RESERVED_MEMORY = 2, + ACPI_RECLAIM_MEMORY = 3, + ACPI_NVS_MEMORY = 4 + }; + + uint64_t const addr; + uint64_t const size; + Type const type; + uint32_t const aux; + }; + + uint32_t const signature; /* magic value 0x41564f4e */ + uint16_t const hip_checksum; + uint16_t const hip_length; + uint16_t const cpu_desc_offset; + uint16_t const cpu_desc_size; + uint16_t const mem_desc_offset; + uint16_t const mem_desc_size; + uint32_t const feature_flags; + uint32_t const api_version; + uint32_t const sel; /* number of cap selectors */ + uint32_t const sel_exc; /* number of cap selectors for exceptions */ + uint32_t const sel_vm; /* number of cap selectors for VM handling */ + uint32_t const sel_gsi; /* number of global system interrupts */ + uint32_t const page_sizes; /* supported page sizes */ + uint32_t const utcb_sizes; /* supported utcb sizes */ + uint32_t const tsc_freq; /* time-stamp counter frequency in kHz */ + uint32_t const bus_freq; /* bus frequency in kHz */ + + bool has_feature_vmx() const { return feature_flags & (1 << 1); } + bool has_feature_svm() const { return feature_flags & (1 << 2); } + }; + + + class Descriptor + { + protected: + + unsigned _value; + + /** + * Assign bitfield to descriptor + */ + template + void _assign(unsigned new_bits) + { + _value &= ~(MASK << SHIFT); + _value |= (new_bits & MASK) << SHIFT; + } + + /** + * Query bitfield from descriptor + */ + template + unsigned _query() const { return (_value >> SHIFT) & MASK; } + + public: + + unsigned value() const { return _value; } + }; + + + /** + * Message-transfer descriptor + */ + class Mtd + { + private: + + unsigned const _value; + + public: + + enum { + ACDB = 1 << 0, /* eax, ecx, edx, ebx */ + ESP = 1 << 2, + EIP = 1 << 3, + EFL = 1 << 4, /* eflags */ + QUAL = 1 << 15, /* exit qualification */ + CTRL = 1 << 16, /* execution controls */ + INJ = 1 << 17, /* injection info */ + STA = 1 << 18, /* interruptibility state */ + TSC = 1 << 19, /* time-stamp counter */ + + IRQ = EFL | STA | INJ | TSC, + ALL = 0x000fffff & ~CTRL, + }; + + Mtd(unsigned value) : _value(value) { } + + unsigned value() const { return _value; } + }; + + + class Crd : public Descriptor + { + protected: + + /** + * Bitfield holding the descriptor type + */ + enum { + TYPE_MASK = 0x3, TYPE_SHIFT = 0, + BASE_MASK = 0xfffff, BASE_SHIFT = 12, + ORDER_MASK = 0x1f, ORDER_SHIFT = 7, + RIGHTS_MASK = 0x7c + }; + + /** + * Capability-range-descriptor types + */ + enum { + NULL_CRD_TYPE = 0, + MEM_CRD_TYPE = 1, + IO_CRD_TYPE = 2, + OBJ_CRD_TYPE = 3, + RIGHTS_ALL = 0x7c, + IO_CRD_ALL = IO_CRD_TYPE | RIGHTS_ALL, + OBJ_CRD_ALL = OBJ_CRD_TYPE | RIGHTS_ALL, + }; + + void _base(unsigned base) + { _assign(base); } + + void _order(unsigned order) + { _assign(order); } + + public: + + Crd(unsigned base, unsigned order) { + _value = 0; _base(base), _order(order); } + + Crd(unsigned value) { _value = value; } + + unsigned hotspot(unsigned sel_hotspot) const + { + if ((value() & TYPE_MASK) == MEM_CRD_TYPE) + return sel_hotspot & PAGE_MASK; + + return sel_hotspot << 12; + } + + unsigned base() const { return _query(); } + unsigned order() const { return _query(); } + bool is_null() const { return (_value & TYPE_MASK) == NULL_CRD_TYPE; } + }; + + + class Rights + { + private: + + bool const _readable, _writeable, _executable; + + public: + + Rights(bool readable, bool writeable, bool executable) + : _readable(readable), _writeable(writeable), + _executable(executable) { } + + Rights() : _readable(false), _writeable(false), _executable(false) {} + + bool readable() const { return _readable; } + bool writeable() const { return _writeable; } + bool executable() const { return _executable; } + }; + + + /** + * Memory-capability-range descriptor + */ + class Mem_crd : public Crd + { + private: + + enum { + EXEC_MASK = 0x1, EXEC_SHIFT = 4, + WRITE_MASK = 0x1, WRITE_SHIFT = 3, + READ_MASK = 0x1, READ_SHIFT = 2 + }; + + void _rights(Rights r) + { + _assign(r.executable()); + _assign(r.writeable()); + _assign(r.readable()); + } + + public: + + Mem_crd(unsigned base, unsigned order, Rights rights = Rights()) + : Crd(base, order) + { + _rights(rights); + _assign(MEM_CRD_TYPE); + } + + Rights rights() const + { + return Rights(_query(), + _query(), + _query()); + } + }; + + + /** + * I/O-capability-range descriptor + */ + class Io_crd : public Crd + { + public: + + Io_crd(unsigned base, unsigned order) + : Crd(base, order) + { + _assign(IO_CRD_ALL); + } + }; + + + class Obj_crd : public Crd + { + public: + + Obj_crd(unsigned base, unsigned order) + : Crd(base, order) + { + _assign(OBJ_CRD_ALL); + } + }; + + + /** + * Quantum-priority descriptor + */ + class Qpd : public Descriptor + { + private: + + enum { + QUANTUM_MASK = 0xfffff, QUANTUM_SHIFT = 12, + PRIORITY_MASK = 0xff, PRIORITY_SHIFT = 0 + }; + + void _quantum(unsigned quantum) + { _assign(quantum); } + + void _priority(unsigned priority) + { _assign(priority); } + + public: + + enum { DEFAULT_QUANTUM = 10000, DEFAULT_PRIORITY = 1 }; + + Qpd(unsigned quantum = DEFAULT_QUANTUM, + unsigned priority = DEFAULT_PRIORITY) + { + _value = 0; + _quantum(quantum), _priority(priority); + } + + unsigned quantum() const { return _query(); } + unsigned priority() const { return _query(); } + }; + + + /** + * User-level thread-control block + */ + struct Utcb + { + unsigned short ui; /* number of untyped items */ + unsigned short ti; /* number of typed itmes */ + Crd crd_xlt; /* receive capability-range descriptor for translation */ + Crd crd_rcv; /* receive capability-range descriptor for delegation */ + unsigned tls; + + /** + * Data area + * + * The UTCB entries following the header hold message payload (normal + * IDC operations) or architectural state (exception handling). + */ + union { + + /* message payload */ + unsigned msg[]; + + /* exception state */ + struct { + unsigned mtd, instr_len, eip, eflags; + unsigned misc[4]; + unsigned eax, ecx, edx, ebx; + unsigned esp, ebp, esi, edi; + long long qual[2]; /* exit qualification */ + unsigned misc2[4]; + unsigned cr0, cr2, cr3, cr4; + unsigned misc3[44]; + }; + }; + + struct Item { + unsigned crd; + unsigned hotspot; + }; + + /** + * Set number of untyped message words + * + * Calling this function has the side effect of removing all typed + * message items from the message buffer. + */ + void set_msg_word(unsigned num) { ui = num; ti = 0; } + + /** + * Return current number of message word in UTCB + */ + unsigned msg_words() { return ui; } + + /** + * Append message-transfer item to message buffer + * + * \param exception true to append the item to an exception reply + */ + void append_item(Crd crd, unsigned sel_hotspot, + bool kern_pd = false, + bool update_guest_pt = false) + { + /* transfer items start at the end of the UTCB */ + Item *item = reinterpret_cast(this) + (PAGE_SIZE / sizeof(struct Item)) - ++ti; + + /* map from hypervisor or current pd */ + unsigned h = kern_pd ? (1 << 11) : 0; + + /* update guest page table */ + unsigned g = update_guest_pt ? (1 << 10) : 0; + + item->hotspot = crd.hotspot(sel_hotspot) | g | h | 1; + item->crd = crd.value(); + + } + + unsigned mtd_value() const { return static_cast(mtd).value(); } + }; + + /** + * Size of event-specific portal window mapped at PD creation time + */ + enum { + NUM_INITIAL_PT_LOG2 = 5, + NUM_INITIAL_PT = 1 << NUM_INITIAL_PT_LOG2 + }; + + /** + * Event-specific capability selectors + */ + enum { + PT_SEL_PAGE_FAULT = 0xe, + PT_SEL_PARENT = 0x1a, /* convention on Genode */ + PT_SEL_STARTUP = 0x1e, + PD_SEL = 0x1b, + }; + + + ALWAYS_INLINE + inline unsigned eax(Syscall s, uint8_t flags, unsigned sel) + { + return sel << 8 | (flags & 0xf) << 4 | s; + } + + + ALWAYS_INLINE + inline uint8_t syscall_0(Syscall s, uint8_t flags, unsigned sel = 0) + { + mword_t status = eax(s, flags, sel); + + asm volatile (" mov %%esp, %%ecx;" + " call 0f;" + "0:" + " addl $(1f-0b), (%%esp);" + " mov (%%esp), %%edx;" + " sysenter;" + "1:" + : "+a" (status) + : + : "ecx", "edx", "memory"); + return status; + } + + + ALWAYS_INLINE + inline uint8_t syscall_1(Syscall s, uint8_t flags, mword_t p1) + { + mword_t status = eax(s, flags, 0); + + asm volatile (" mov %%esp, %%ecx;" + " call 0f;" + "0:" + " addl $(1f-0b), (%%esp);" + " mov (%%esp), %%edx;" + " sysenter;" + "1:" + : "+a" (status) + : "D" (p1) + : "ecx", "edx"); + return status; + } + + + ALWAYS_INLINE + inline uint8_t syscall_2(Syscall s, uint8_t flags, unsigned sel, mword_t p1, mword_t p2) + { + mword_t status = eax(s, flags, sel); + + asm volatile (" mov %%esp, %%ecx;" + " call 0f;" + "0:" + " addl $(1f-0b), (%%esp);" + " mov (%%esp), %%edx;" + " sysenter;" + "1:" + : "+a" (status) + : "D" (p1), "S" (p2) + : "ecx", "edx"); + return status; + } + + + ALWAYS_INLINE + inline uint8_t syscall_3(Syscall s, uint8_t flags, unsigned sel, + mword_t p1, mword_t p2, mword_t p3) + { + mword_t status = eax(s, flags, sel); + + asm volatile (" push %%ebx;" + " mov %%edx, %%ebx;" + " mov %%esp, %%ecx;" + " call 0f;" + "0:" + " addl $(1f-0b), (%%esp);" + " mov (%%esp), %%edx;" + " sysenter;" + "1:" + " pop %%ebx;" + : "+a" (status) + : "D" (p1), "S" (p2), "d" (p3) + : "ecx"); + return status; + } + + + ALWAYS_INLINE + inline uint8_t syscall_4(Syscall s, uint8_t flags, unsigned sel, + mword_t p1, mword_t p2, mword_t p3, mword_t p4) + { + mword_t status = eax(s, flags, sel); + + asm volatile (" push %%ebp;" + " push %%ebx;" + + " mov %%ecx, %%ebx;" + " mov %%esp, %%ecx;" + " mov %%edx, %%ebp;" + + " call 0f;" + "0:" + " addl $(1f-0b), (%%esp);" + " mov (%%esp), %%edx;" + "sysenter;" + "1:" + + " pop %%ebx;" + " pop %%ebp;" + : "+a" (status) + : "D" (p1), "S" (p2), "c" (p3), "d" (p4) + : "memory"); + return status; + } + + + ALWAYS_INLINE + inline uint8_t call(unsigned pt) + { + return syscall_0(NOVA_CALL, 0, pt); + } + + + ALWAYS_INLINE + inline void reply(void *next_sp) + { + asm volatile ("sysenter;" + : + : "a" (NOVA_REPLY), "c" (next_sp) + : "memory"); + } + + + ALWAYS_INLINE + inline uint8_t create_pd(unsigned pd0, unsigned pd, Crd crd) + { + return syscall_2(NOVA_CREATE_PD, 0, pd0, pd, crd.value()); + } + + + ALWAYS_INLINE + inline uint8_t create_ec(unsigned ec, unsigned pd, + mword_t cpu, mword_t utcb, + mword_t esp, mword_t evt, + bool global = 0) + { + return syscall_4(NOVA_CREATE_EC, global, ec, pd, + (cpu & 0xfff) | (utcb & ~0xfff), + esp, evt); + } + + + ALWAYS_INLINE + inline uint8_t ec_ctrl(unsigned ec) + { + return syscall_1(NOVA_EC_CTRL, 0, ec); + } + + ALWAYS_INLINE + inline uint8_t create_sc(unsigned sc, unsigned pd, unsigned ec, Qpd qpd) + { + return syscall_3(NOVA_CREATE_SC, 0, sc, pd, ec, qpd.value()); + } + + + ALWAYS_INLINE + inline uint8_t create_pt(unsigned pt, unsigned pd, unsigned ec, Mtd mtd, mword_t eip) + { + return syscall_4(NOVA_CREATE_PT, 0, pt, pd, ec, mtd.value(), eip); + } + + + ALWAYS_INLINE + inline uint8_t create_sm(unsigned sm, unsigned pd, mword_t cnt) + { + return syscall_2(NOVA_CREATE_SM, 0, sm, pd, cnt); + } + + + ALWAYS_INLINE + inline uint8_t revoke(Crd crd, bool self = true) + { + return syscall_1(NOVA_REVOKE, self, crd.value()); + } + + + ALWAYS_INLINE + inline uint8_t lookup(Crd *crd) + { + mword_t status = eax(NOVA_LOOKUP, 0, 0); + mword_t raw = crd->value(); + + asm volatile (" mov %%esp, %%ecx;" + " call 0f;" + "0:" + " addl $(1f-0b), (%%esp);" + " mov (%%esp), %%edx;" + " sysenter;" + "1:" + : "+a" (status), "+D" (raw) + : + : "ecx", "edx", "memory"); + + *crd = Crd(raw); + return status; + } + + /** + * Semaphore operations + */ + enum Sem_op { SEMAPHORE_UP = 0, SEMAPHORE_DOWN = 1 }; + + + ALWAYS_INLINE + inline uint8_t sm_ctrl(unsigned sm, Sem_op op) + { + return syscall_0(NOVA_SM_CTRL, op, sm); + } + + + ALWAYS_INLINE + inline uint8_t assign_gsi(unsigned sm, mword_t dev, mword_t cpu) + { + return syscall_2(NOVA_ASSIGN_GSI, 0, sm, dev, cpu); + } +} +#endif /* _PLATFORM__NOVA_SYSCALLS_H_ */ diff --git a/base-nova/include/signal_session/nova_source.h b/base-nova/include/signal_session/nova_source.h new file mode 100644 index 000000000..9f1dbcca9 --- /dev/null +++ b/base-nova/include/signal_session/nova_source.h @@ -0,0 +1,34 @@ +/* + * \brief NOVA-specific signal source RPC interface + * \author Norman Feske + * \date 2011-04-12 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__NOVA_SOURCE_H_ +#define _INCLUDE__SIGNAL_SESSION__NOVA_SOURCE_H_ + +#include +#include + +namespace Genode { + + struct Nova_signal_source : Signal_source + { + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_request_semaphore, Native_capability, _request_semaphore); + + GENODE_RPC_INTERFACE_INHERIT(Signal_source, Rpc_request_semaphore); + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__NOVA_SOURCE_H_ */ diff --git a/base-nova/include/signal_session/source_client.h b/base-nova/include/signal_session/source_client.h new file mode 100644 index 000000000..e9994c91c --- /dev/null +++ b/base-nova/include/signal_session/source_client.h @@ -0,0 +1,82 @@ +/* + * \brief NOVA-specific signal-source client interface + * \author Norman Feske + * \date 2010-02-03 + * + * On NOVA, the signal source server does not provide a blocking + * 'wait_for_signal' function because this kernel does no support + * out-of-order IPC replies. Instead, we use a shared semaphore + * to let the client block until a signal is present at the + * server. The shared semaphore gets initialized with the first + * call of 'wait_for_signal()'. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ +#define _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ + +#include +#include +#include + +namespace Genode { + + class Signal_source_client : public Rpc_client + { + private: + + /** + * Capability with 'pt_sel' referring to a NOVA semaphore + */ + Native_capability _sem; + + /** + * Request NOVA semaphore from signal-source server + */ + void _init_sem() + { + /* initialize semaphore only once */ + if (_sem.valid()) return; + + /* request mapping of semaphore capability selector */ + _sem = call(); + } + + public: + + /** + * Constructor + */ + Signal_source_client(Signal_source_capability cap) + : Rpc_client(static_cap_cast(cap)) { } + + + /***************************** + ** Signal source interface ** + *****************************/ + + Signal wait_for_signal() + { + /* make sure that we have aquired the semaphore from the server */ + _init_sem(); + + /* block on semaphore, will be unblocked if signal is available */ + Nova::sm_ctrl(_sem.pt_sel(), Nova::SEMAPHORE_DOWN); + + /* + * Now that the server has unblocked the semaphore, we are sure + * that there is a signal pending. The following 'wait_for_signal' + * request will be immediately answered. + */ + return call(); + } + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ */ diff --git a/base-nova/include/signal_session/source_rpc_object.h b/base-nova/include/signal_session/source_rpc_object.h new file mode 100644 index 000000000..3272bd67a --- /dev/null +++ b/base-nova/include/signal_session/source_rpc_object.h @@ -0,0 +1,39 @@ +/* + * \brief Signal-source server interface + * \author Norman Feske + * \date 2010-02-03 + * + * This file is only included by 'signal_session/server.h' and relies on the + * headers included there. No include guards are needed. It is a separate + * header file to make it easily replaceable by a platform-specific + * implementation. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SOURCE_SERVER_H_ +#define _INCLUDE__SIGNAL_SESSION__SOURCE_SERVER_H_ + +#include +#include + +namespace Genode { + + struct Signal_source_rpc_object : Rpc_object + { + protected: + + Native_capability _blocking_semaphore; + + public: + + Native_capability _request_semaphore() { return _blocking_semaphore; } + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SOURCE_SERVER_H_ */ diff --git a/base-nova/lib/mk/core_printf.mk b/base-nova/lib/mk/core_printf.mk new file mode 100644 index 000000000..663cf64b9 --- /dev/null +++ b/base-nova/lib/mk/core_printf.mk @@ -0,0 +1,5 @@ +SRC_CC = core_printf.cc +LIBS = cxx console +INC_DIR += $(REP_DIR)/src/base/console + +vpath core_printf.cc $(BASE_DIR)/src/base/console diff --git a/base-nova/lib/mk/env.mk b/base-nova/lib/mk/env.mk new file mode 100644 index 000000000..439e6e2d7 --- /dev/null +++ b/base-nova/lib/mk/env.mk @@ -0,0 +1,7 @@ +SRC_CC = env.cc cap_sel_alloc.cc main_thread.cc context_area.cc +LIBS = ipc heap lock log_console + +vpath env.cc $(BASE_DIR)/src/base/env +vpath cap_sel_alloc.cc $(REP_DIR)/src/base/env +vpath main_thread.cc $(REP_DIR)/src/base/env +vpath context_area.cc $(BASE_DIR)/src/base/env diff --git a/base-nova/lib/mk/ipc.mk b/base-nova/lib/mk/ipc.mk new file mode 100644 index 000000000..7116e3dd9 --- /dev/null +++ b/base-nova/lib/mk/ipc.mk @@ -0,0 +1,6 @@ +SRC_CC = ipc.cc pager.cc +LIBS = thread_context +INC_DIR += $(REP_DIR)/src/platform + +vpath ipc.cc $(REP_DIR)/src/base/ipc +vpath pager.cc $(REP_DIR)/src/base/ipc diff --git a/base-nova/lib/mk/lock.mk b/base-nova/lib/mk/lock.mk new file mode 100644 index 000000000..3b968d2b6 --- /dev/null +++ b/base-nova/lib/mk/lock.mk @@ -0,0 +1,5 @@ +SRC_CC = lock.cc +INC_DIR += $(REP_DIR)/src/base/lock +INC_DIR += $(REP_DIR)/src/platform + +vpath lock.cc $(BASE_DIR)/src/base/lock diff --git a/base-nova/lib/mk/pager.mk b/base-nova/lib/mk/pager.mk new file mode 100644 index 000000000..2c4f26b18 --- /dev/null +++ b/base-nova/lib/mk/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-nova/lib/mk/printf_stdio.mk b/base-nova/lib/mk/printf_stdio.mk new file mode 100644 index 000000000..8f910d8e9 --- /dev/null +++ b/base-nova/lib/mk/printf_stdio.mk @@ -0,0 +1,3 @@ +SRC_CC = printf_stdio.cc + +vpath printf_stdio.cc $(REP_DIR)/src/lib/printf_stdio diff --git a/base-nova/lib/mk/raw_server.mk b/base-nova/lib/mk/raw_server.mk new file mode 100644 index 000000000..3bbdca3d3 --- /dev/null +++ b/base-nova/lib/mk/raw_server.mk @@ -0,0 +1,4 @@ +SRC_CC = server.cc +INC_DIR = $(REP_DIR)/src/platform + +vpath %.cc $(REP_DIR)/src/base/server diff --git a/base-nova/lib/mk/server.mk b/base-nova/lib/mk/server.mk new file mode 100644 index 000000000..de155386e --- /dev/null +++ b/base-nova/lib/mk/server.mk @@ -0,0 +1,3 @@ +LIBS = thread + +include $(REP_DIR)/lib/mk/raw_server.mk diff --git a/base-nova/lib/mk/test_env.mk b/base-nova/lib/mk/test_env.mk new file mode 100644 index 000000000..bad92f0da --- /dev/null +++ b/base-nova/lib/mk/test_env.mk @@ -0,0 +1,11 @@ +SRC_CC = cap_sel_alloc.cc main_thread.cc context_area.cc echo.cc \ + thread_nova.cc thread.cc +LIBS += thread_context +INC_DIR += $(REP_DIR)/src/core/include + +vpath cap_sel_alloc.cc $(REP_DIR)/src/base/env +vpath main_thread.cc $(REP_DIR)/src/base/env +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath thread_nova.cc $(REP_DIR)/src/test +vpath context_area.cc $(REP_DIR)/src/test +vpath echo.cc $(REP_DIR)/src/core diff --git a/base-nova/lib/mk/thread.mk b/base-nova/lib/mk/thread.mk new file mode 100644 index 000000000..b599c112e --- /dev/null +++ b/base-nova/lib/mk/thread.mk @@ -0,0 +1,6 @@ +SRC_CC = thread.cc thread_nova.cc +LIBS = thread_context + +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath thread_nova.cc $(REP_DIR)/src/base/thread + diff --git a/base-nova/lib/mk/thread_context.mk b/base-nova/lib/mk/thread_context.mk new file mode 100644 index 000000000..55444d8bb --- /dev/null +++ b/base-nova/lib/mk/thread_context.mk @@ -0,0 +1,3 @@ +SRC_CC = thread_context.cc + +vpath thread_context.cc $(REP_DIR)/src/base/thread diff --git a/base-nova/lib/mk/x86_32/startup.mk b/base-nova/lib/mk/x86_32/startup.mk new file mode 100644 index 000000000..e8d859266 --- /dev/null +++ b/base-nova/lib/mk/x86_32/startup.mk @@ -0,0 +1,8 @@ +REQUIRES = nova x86 +LIBS = cxx lock +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(REP_DIR)/src/platform + +vpath crt0.s $(BASE_DIR)/src/platform/x86_32 +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-nova/mk/spec-nova.mk b/base-nova/mk/spec-nova.mk new file mode 100644 index 000000000..e6c5d276c --- /dev/null +++ b/base-nova/mk/spec-nova.mk @@ -0,0 +1,16 @@ +# +# Specifics for the NOVA kernel API +# + +LD_TEXT_ADDR ?= 0x01000000 + +# +# Startup code to be used when building a program +# +STARTUP_LIB ?= startup +PRG_LIBS += $(STARTUP_LIB) + +# +# NOVA only runs on x86, enable x86 devices +# +SPECS += pci ps2 vesa diff --git a/base-nova/patches/README b/base-nova/patches/README new file mode 100644 index 000000000..fb2dcfa4f --- /dev/null +++ b/base-nova/patches/README @@ -0,0 +1,21 @@ +This directory contains patches for the Nova Hypervisor prerelease 0.3 + +:'utcb.patch': + + It is not possible to destroy UTCBs in NOVA 0.3. Therefore UTCBs cannot be + re-used which may lead to the exhaustion of contexts within Genode. This patch + simply causes NOVA to ignore this issue. + + +Applying the patches +-------------------- + +To apply a patch to the NOVA hypervisor, use the 'patch' command. First check +the directory given at the header of the patch. It may contain a directory +prefix (such as 'a/'), which does not actually exist. This prefix is usually +generated by the tool used to create the patch. In this case, use the '-p' +option of the patch command. To apply the patch with the first part of the +path stripped, issue the following command (make sure that you changed to +the base directory of the NOVA hypervisor): + +! patch -p1 < /path/to/utcb.patch diff --git a/base-nova/patches/utcb.patch b/base-nova/patches/utcb.patch new file mode 100644 index 000000000..b2efe4605 --- /dev/null +++ b/base-nova/patches/utcb.patch @@ -0,0 +1,18 @@ +diff -r 11c290b5edf9 src/syscall.cpp +--- a/src/syscall.cpp Wed Nov 09 14:50:18 2011 +0100 ++++ b/src/syscall.cpp Wed Nov 09 15:07:03 2011 +0100 +@@ -240,11 +240,13 @@ + } + Pd *pd = static_cast(cap.obj()); + +- if (EXPECT_FALSE (r->utcb() >= USER_ADDR || r->utcb() & PAGE_MASK || !pd->insert_utcb (r->utcb()))) { ++ if (EXPECT_FALSE (r->utcb() >= USER_ADDR || r->utcb() & PAGE_MASK)) { + trace (TRACE_ERROR, "%s: Invalid UTCB address (%#lx)", __func__, r->utcb()); + sys_finish(); + } + ++ pd->insert_utcb (r->utcb()); ++ + Ec *ec = new Ec (Pd::current, r->sel(), pd, r->flags() & 1 ? static_cast(send_msg) : 0, r->cpu(), r->evt(), r->utcb(), r->esp()); + + if (!Space_obj::insert_root (ec)) { diff --git a/base-nova/run/env b/base-nova/run/env new file mode 100644 index 000000000..c0720c309 --- /dev/null +++ b/base-nova/run/env @@ -0,0 +1,82 @@ +# +# \brief NOVA-specific test-environment supplements +# \author Norman Feske +# \date 2010-08-31 +# +# This file is meant to be used as '--include' argument for 'tool/run'. +# + + +## +# Read the location of the Pistachio user directory from 'etc/pistachio.conf' +# +proc nova_kernel { } { + global _nova_kernel + + if {![info exists _nova_kernel]} { + if {[file exists etc/nova.conf]} { + set _nova_kernel [exec sed -n "/^NOVA_KERNEL/s/^.*=\\s*//p" etc/nova.conf] + } else { + set _nova_kernel "[pwd]/kernel/hypervisor" + } + } + return $_nova_kernel +} + +## +# Return whether nova is provided from the outside +# +proc nova_external { } { + if {[nova_kernel] == "[pwd]/kernel/hypervisor"} { return 0 } + return 1 +} + +################################## +## Test framework API functions ## +################################## + +proc create_boot_directory { } { + exec rm -rf [run_dir] + exec mkdir -p [run_dir]/genode +} + + +proc build_boot_image {binaries} { + + # + # Collect contents of the ISO image + # + copy_and_strip_genode_binaries_to_run_dir $binaries + + if {![nova_external] && ![file exists [nova_kernel]]} { build { kernel } } + + puts "using NOVA kernel at [nova_kernel]" + exec cp [nova_kernel] [run_dir]/hypervisor + + install_iso_bootloader_to_run_dir + + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on NOVA" + puts $fh " kernel /hypervisor serial" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + close $fh + + create_iso_image_from_run_dir +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + spawn_qemu $wait_for_re $timeout_value } + diff --git a/base-nova/src/base/console/core_console.h b/base-nova/src/base/console/core_console.h new file mode 100644 index 000000000..9bce170f5 --- /dev/null +++ b/base-nova/src/base/console/core_console.h @@ -0,0 +1,129 @@ +/* + * \brief Console backend for NOVA + * \author Norman Feske + * \date 2009-12-28 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* NOVA includes */ +#include + +typedef unsigned char uint8_t; + + +/** + * Read byte from I/O port + */ +inline uint8_t inb(unsigned short port) +{ + uint8_t res; + asm volatile ("inb %%dx, %0" :"=a"(res) :"Nd"(port)); + return res; +} + + +/** + * Write byte to I/O port + */ +inline void outb(unsigned short port, uint8_t val) +{ + asm volatile ("outb %b0, %w1" : : "a" (val), "Nd" (port)); +} + + +/** + * Definitions of PC serial ports + */ +enum Comport { COMPORT_0, COMPORT_1, COMPORT_2, COMPORT_3 }; + +enum { +// COMPORT_0_BASE = 0x1010, /* comport of serial PCI card */ + COMPORT_0_BASE = 0x3f8, /* default comport 0, used wiht Qemu */ + COMPORT_1_BASE = 0x2f8, + COMPORT_2_BASE = 0x3e8, + COMPORT_3_BASE = 0x2e8, + COMPORT_DATA_OFFSET = 0, + COMPORT_STATUS_OFFSET = 5 +}; + + +/** + * Initialize serial port + * + * Based on 'init_serial' of L4ka::Pistachio's 'kdb/platform/pc99/io.cc' + */ +static void init_comport(unsigned short port, unsigned baud) +{ + const unsigned + IER = port + 1, + EIR = port + 2, + LCR = port + 3, + MCR = port + 4, + LSR = port + 5, + MSR = port + 6, + DLLO = port + 0, + DLHI = port + 1; + + outb(LCR, 0x80); /* select bank 1 */ + for (volatile int i = 10000000; i--; ); + outb(DLLO, (115200/baud) >> 0); + outb(DLHI, (115200/baud) >> 8); + outb(LCR, 0x03); /* set 8,N,1 */ + outb(IER, 0x00); /* disable interrupts */ + outb(EIR, 0x07); /* enable FIFOs */ + outb(MCR, 0x0b); /* force data terminal ready */ + outb(IER, 0x01); /* enable RX interrupts */ + inb(IER); + inb(EIR); + inb(LCR); + inb(MCR); + inb(LSR); + inb(MSR); +} + + +/** + * Output character to serial port + */ +inline void serial_out_char(Comport comport, uint8_t c) +{ + static int io_port[] = { COMPORT_0_BASE, COMPORT_1_BASE, + COMPORT_2_BASE, COMPORT_3_BASE }; + + /* wait until serial port is ready */ + while (!(inb(io_port[comport] + COMPORT_STATUS_OFFSET) & 0x60)); + + /* output character */ + outb(io_port[comport] + COMPORT_DATA_OFFSET, c); +} + + +namespace Genode { + + class Core_console : public Console + { + protected: + + void _out_char(char c) + { + if (c == '\n') + serial_out_char(COMPORT_0, '\r'); + serial_out_char(COMPORT_0, c); + } + + public: + + Core_console() { init_comport(COMPORT_0_BASE, 115200); } + }; +} + diff --git a/base-nova/src/base/env/cap_sel_alloc.cc b/base-nova/src/base/env/cap_sel_alloc.cc new file mode 100644 index 000000000..51449881f --- /dev/null +++ b/base-nova/src/base/env/cap_sel_alloc.cc @@ -0,0 +1,121 @@ +/* + * \brief Capability-selector allocator + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2010-01-19 + * + * This is a NOVA-specific addition to the process enviroment. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* NOVA includes */ +#include + +using namespace Genode; + + +/** + * First available capability selector for custom use + * + * Must be initialized by the startup code + */ +int __first_free_cap_selector; +int __local_pd_sel; + +/** + * Low-level lock to protect the allocator + * + * We cannot use a normal Genode lock because this lock is used by code + * executed prior the initialization of Genode. + */ +class Alloc_lock +{ + private: + + int _sm_cap; + + public: + + /** + * Constructor + * + * \param sm_cap capability selector for the used semaphore + */ + Alloc_lock(int sm_cap) : _sm_cap(sm_cap) + { + Nova::create_sm(_sm_cap, __local_pd_sel, 1); + } + + void lock() { Nova::sm_ctrl(_sm_cap, Nova::SEMAPHORE_DOWN); } + + void unlock() { Nova::sm_ctrl(_sm_cap, Nova::SEMAPHORE_UP); } +}; + + +/** + * Return lock used to protect capability selector allocations + */ +static Alloc_lock *alloc_lock() +{ + static Alloc_lock alloc_lock_inst(__first_free_cap_selector); + return &alloc_lock_inst; +} + + +static int _cap_free; + + +addr_t Cap_selector_allocator::alloc(size_t num_caps_log2) +{ + alloc_lock()->lock(); + int num_caps = 1 << num_caps_log2; + int ret_base = (_cap_free + num_caps - 1) & ~(num_caps - 1); + _cap_free = ret_base + num_caps; + alloc_lock()->unlock(); + return ret_base; +} + + +void Cap_selector_allocator::free(addr_t cap, size_t num_caps_log2) +{ + /* + * We don't free capability selectors because revoke is not supported + * on NOVA yet, anyway. + */ +} + + +unsigned Cap_selector_allocator::pd_sel() { return __local_pd_sel; } + + +Cap_selector_allocator::Cap_selector_allocator() +{ + /* initialize lock */ + alloc_lock(); + + /* the first free selector is used for the lock */ + _cap_free = __first_free_cap_selector + 1; +} + + +namespace Genode { + + /** + * This function must not be called prior the initialization of + * '__first_free_cap_selector'. + */ + Cap_selector_allocator *cap_selector_allocator() + { + static Cap_selector_allocator inst; + return &inst; + } +} diff --git a/base-nova/src/base/env/main_thread.cc b/base-nova/src/base/env/main_thread.cc new file mode 100644 index 000000000..f33c688d4 --- /dev/null +++ b/base-nova/src/base/env/main_thread.cc @@ -0,0 +1,43 @@ +/* + * \brief Information about the main thread + * \author Norman Feske + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* NOVA includes */ +#include + +using namespace Genode; + +/** + * Location of the main thread's UTCB, initialized by the startup code + */ +Nova::mword_t __main_thread_utcb; + + +Native_utcb *main_thread_utcb() { return (Native_utcb *)__main_thread_utcb; } + + +int main_thread_running_semaphore() +{ + static int sm; + if (!sm) { + sm = cap_selector_allocator()->alloc(); + int res = Nova::create_sm(sm, Cap_selector_allocator::pd_sel(), 0); + if (res) + PERR("create_sm returned %d", res); + } + return sm; +} diff --git a/base-nova/src/base/ipc/ipc.cc b/base-nova/src/base/ipc/ipc.cc new file mode 100644 index 000000000..ef6ca0677 --- /dev/null +++ b/base-nova/src/base/ipc/ipc.cc @@ -0,0 +1,200 @@ +/* + * \brief Implementation of the IPC API for NOVA + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +#include + +/* NOVA includes */ +#include + +using namespace Genode; +using namespace Nova; + + +/*************** + ** Utilities ** + ***************/ + +/** + * Copy message registers from UTCB to destination message buffer + */ +static void copy_utcb_to_msgbuf(Nova::Utcb *utcb, Msgbuf_base *rcv_msg) +{ + size_t num_msg_words = utcb->msg_words(); + if (num_msg_words == 0) return; + + /* look up and validate destination message buffer to receive the payload */ + mword_t *msg_buf = (mword_t *)rcv_msg->buf; + if (num_msg_words*sizeof(mword_t) > rcv_msg->size()) { + PERR("receive message buffer too small msg size=%x, buf size=%zd", + num_msg_words*sizeof(mword_t), rcv_msg->size()); + num_msg_words = rcv_msg->size()/sizeof(mword_t); + } + + /* read message payload into destination message buffer */ + mword_t *src = (mword_t *)(void *)(&utcb->msg[0]); + mword_t *dst = (mword_t *)&msg_buf[0]; + for (unsigned i = 0; i < num_msg_words; i++) + *dst++ = *src++; + + rcv_msg->rcv_reset(); +} + + +/** + * Copy message payload to UTCB message registers + */ +static void copy_msgbuf_to_utcb(Nova::Utcb *utcb, Msgbuf_base *snd_msg, + unsigned num_msg_words, mword_t local_name) +{ + /* look up address and size of message payload */ + mword_t *msg_buf = (mword_t *)snd_msg->buf; + + /* + * XXX determine correct number of message registers + */ + enum { NUM_MSG_REGS = 256 }; + if (num_msg_words > NUM_MSG_REGS) { + PERR("Message does not fit into UTCB message registers\n"); + num_msg_words = NUM_MSG_REGS; + } + + msg_buf[0] = local_name; + + /* store message into UTCB message registers */ + mword_t *src = (mword_t *)&msg_buf[0]; + mword_t *dst = (mword_t *)(void *)&utcb->msg[0]; + for (unsigned i = 0; i < num_msg_words; i++) + *dst++ = *src++; + + utcb->set_msg_word(num_msg_words); + + /* append portal capability selectors */ + for (unsigned i = 0; i < snd_msg->snd_pt_sel_cnt(); i++) { + int pt_sel = snd_msg->snd_pt_sel(i); + if (pt_sel < 0) continue; + + utcb->append_item(Nova::Obj_crd(pt_sel, 0), i); + } + + /* we have consumed portal capability selectors, reset message buffer */ + snd_msg->snd_reset(); +} + + +/***************** + ** Ipc_ostream ** + *****************/ + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) +: + Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()), + _snd_msg(snd_msg), _dst(dst) +{ + _write_offset = sizeof(mword_t); +} + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() { } + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) +: Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), _rcv_msg(rcv_msg) +{ + _read_offset = sizeof(mword_t); +} + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() +{ + Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb(); + + copy_msgbuf_to_utcb(utcb, _snd_msg, _write_offset/sizeof(mword_t), + _dst.local_name()); + _rcv_msg->rcv_prepare_pt_sel_window(utcb); + + /* establish the mapping via a portal traversal */ + if (_dst.pt_sel() == 0) + PWRN("destination portal is zero"); + int res = Nova::call(_dst.pt_sel()); + if (res) + PERR("call returned %d", res); + + copy_utcb_to_msgbuf(utcb, _rcv_msg); + _snd_msg->snd_reset(); + + _write_offset = _read_offset = sizeof(mword_t); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, + Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) +{ } + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_wait() +{ + /* + * This function is only called by the portal dispatcher of server + * entrypoint'. When the dispatcher is called, the incoming message already + * arrived so that we do not need to block. The only remaining thing to do + * is unmarshalling the arguments. + */ + + Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb(); + + copy_utcb_to_msgbuf(utcb, _rcv_msg); + + /* reset unmarshaller */ + _read_offset = sizeof(mword_t); + _write_offset = 2*sizeof(mword_t); /* leave space for the return value */ +} + + +void Ipc_server::_reply() +{ + Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb(); + + copy_msgbuf_to_utcb(utcb, _snd_msg, _write_offset/sizeof(mword_t), + _dst.local_name()); + + Nova::reply(Thread_base::myself()->stack_top()); +} + + +void Ipc_server::_reply_wait() { } + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(Native_capability(), snd_msg) +{ } diff --git a/base-nova/src/base/ipc/pager.cc b/base-nova/src/base/ipc/pager.cc new file mode 100644 index 000000000..30abfa8b7 --- /dev/null +++ b/base-nova/src/base/ipc/pager.cc @@ -0,0 +1,67 @@ +/* + * \brief Low-level page-fault handling + * \author Norman Feske + * \date 2010-01-25 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* NOVA includes */ +#include + +enum { verbose_page_fault = false }; + +using namespace Genode; + +/** + * Print page-fault information in a human-readable form + */ +inline void print_page_fault(unsigned type, addr_t addr, addr_t ip) +{ + enum { TYPE_READ = 0x4, TYPE_WRITE = 0x2, TYPE_EXEC = 0x1, }; + printf("page (%s%s%s) fault at fault_addr=%lx, fault_ip=%lx\n", + type & TYPE_READ ? "r" : "-", + type & TYPE_WRITE ? "w" : "-", + type & TYPE_EXEC ? "x" : "-", + addr, ip); +} + + +void Ipc_pager::wait_for_fault() +{ + /* + * When this function is called from the page-fault handler EC, a page + * fault already occurred. So we never wait but immediately read the + * page-fault information from our UTCB. + */ + Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb(); + _fault_type = (Pf_type)utcb->qual[0]; + _fault_addr = utcb->qual[1]; + _fault_ip = utcb->eip; + + if (verbose_page_fault) + print_page_fault(_fault_type, _fault_addr, _fault_ip); +} + + +void Ipc_pager::set_reply_mapping(Mapping m) +{ + Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb(); + utcb->set_msg_word(0); + utcb->append_item(m.mem_crd(), m.dst_addr()); +} + + +void Ipc_pager::reply_and_wait_for_fault() +{ + Nova::reply(Thread_base::myself()->stack_top()); +} diff --git a/base-nova/src/base/lock/lock_helper.h b/base-nova/src/base/lock/lock_helper.h new file mode 100644 index 000000000..e460d98ef --- /dev/null +++ b/base-nova/src/base/lock/lock_helper.h @@ -0,0 +1,88 @@ +/* + * \brief Helper functions for the Lock implementation + * \author Norman Feske + * \date 2009-10-02 + * + * For documentation about the interface, please revisit the 'base-pistachio' + * implementation. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* NOVA includes */ +#include + + +extern int main_thread_running_semaphore(); + + +/** + * Resolve 'Thread_base::myself' when not linking the thread library + * + * This weak symbol is primarily used by test cases. Most other Genode programs + * use the thread library. If the thread library is not used, 'myself' can only + * be called by the main thread, for which 'myself' is defined as zero. + */ +Genode::Thread_base * __attribute__((weak)) Genode::Thread_base::myself() { return 0; } + + +static inline void thread_yield() { } + + +static bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + int sem = tid.rs_sel == 0 ? main_thread_running_semaphore() + : tid.rs_sel; + + Nova::sm_ctrl(sem, Nova::SEMAPHORE_UP); + return true; +} + + +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + /* + * We encode the main thread as tid { 0, 0, 0 } because we cannot + * call 'main_thread_running_semaphore()' here. + */ + Genode::Thread_base *myself = Genode::Thread_base::myself(); + + if (myself == 0) { + Genode::Native_thread_id main_tid = { 0, 0, 0 }; + return main_tid; + } else + return myself->tid(); +} + + +static inline Genode::Native_thread_id thread_invalid_id() +{ + Genode::Native_thread_id tid = { 0, 0, -1 }; + return tid; +} + + +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return tid.rs_sel != -1; +} + + +static inline void thread_switch_to(Genode::Native_thread_id tid) { } + + +static inline void thread_stop_myself() +{ + Genode::Thread_base *myself = Genode::Thread_base::myself(); + int sem = myself ? myself->tid().rs_sel : main_thread_running_semaphore(); + Nova::sm_ctrl(sem, Nova::SEMAPHORE_DOWN); +} diff --git a/base-nova/src/base/pager/pager.cc b/base-nova/src/base/pager/pager.cc new file mode 100644 index 000000000..7f3f2039e --- /dev/null +++ b/base-nova/src/base/pager/pager.cc @@ -0,0 +1,177 @@ +/* + * \brief Pager framework + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2010-01-25 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* NOVA includes */ +#include + +using namespace Genode; +using namespace Nova; + +enum { PF_HANDLER_STACK_SIZE = 4096 }; + + +static Lock *pf_lock() { static Lock inst; return &inst; } + + +void Pager_object::_page_fault_handler() +{ + Ipc_pager ipc_pager; + ipc_pager.wait_for_fault(); + + /* serialize page-fault handling */ + pf_lock()->lock(); + + Thread_base *myself = Thread_base::myself(); + if (!myself) { + PWRN("unexpected page-fault for non-existing pager object, going to sleep forever"); + sleep_forever(); + } + + Pager_object *obj = static_cast(myself); + int ret = obj->pager(ipc_pager); + pf_lock()->unlock(); + + if (ret) { + PWRN("page-fault resolution for for address 0x%lx failed, going to sleep forever", + ipc_pager.fault_addr()); + sleep_forever(); + } + + ipc_pager.reply_and_wait_for_fault(); +} + + +void Pager_object::_startup_handler() +{ + Pager_object *obj = static_cast(Thread_base::myself()); + Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); + + printf("start new pager object with EIP=0x%p, ESP=0x%p\n", + (void *)obj->_initial_eip, (void *)obj->_initial_esp); + + utcb->eip = obj->_initial_eip; + utcb->esp = obj->_initial_esp; + utcb->mtd = Mtd::EIP | Mtd::ESP; + reply(Thread_base::myself()->stack_top()); +} + + +void Pager_object::_invoke_handler() +{ + Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); + Pager_object *obj = static_cast(Thread_base::myself()); + + /* send single portal as reply */ + int event = utcb->msg[0]; + utcb->mtd = 0; + + if (event == PT_SEL_STARTUP || event == PT_SEL_PAGE_FAULT) + utcb->append_item(Obj_crd(obj->_exc_pt_sel + event, 0), 0); + + reply(Thread_base::myself()->stack_top()); +} + + +void Pager_object::wake_up() { PDBG("not yet implemented"); } + + +Pager_object::Pager_object(unsigned long badge) +: Thread_base("pager", PF_HANDLER_STACK_SIZE), _badge(badge) +{ + _tid.ec_sel = cap_selector_allocator()->alloc(); + unsigned pd_sel = cap_selector_allocator()->pd_sel(); + + enum { CPU_NO = 0, GLOBAL = false, EXC_BASE = 0 }; + + mword_t *thread_sp = (mword_t *)&_context->stack[-4]; + mword_t thread_utcb = (mword_t) &_context->utcb; + + /* create local EC */ + int res = create_ec(_tid.ec_sel, pd_sel, + CPU_NO, thread_utcb, + (mword_t)thread_sp, /* <- delivered to the startup handler */ + EXC_BASE, GLOBAL); + if (res) + PDBG("create_ec returned %d", res); + + /* allocate capability-selector range for event portals */ + _exc_pt_sel = cap_selector_allocator()->alloc(NUM_INITIAL_PT_LOG2); + + /* create portal for page-fault handler */ + res = create_pt(_exc_pt_sel + PT_SEL_PAGE_FAULT, pd_sel, _tid.ec_sel, + Mtd(Mtd::QUAL | Mtd::EIP), (mword_t)_page_fault_handler); + if (res) { + PERR("could not create page-fault portal, create_pt returned %d\n", + res); + class Create_page_fault_pt_failed { }; + throw Create_page_fault_pt_failed(); + } + + /* create portal for startup handler */ + res = create_pt(_exc_pt_sel + PT_SEL_STARTUP, pd_sel, _tid.ec_sel, + Mtd(Mtd::ESP | Mtd::EIP), (mword_t)_startup_handler); + if (res) { + PERR("could not create startup portal, create_pt returned %d\n", + res); + class Create_startup_pt_failed { }; + throw Create_startup_pt_failed(); + } + + /* + * Create object identity representing the pager object. It is used as + * argument to 'Cpu_session::set_pager'. Furthermore it can be invoked to + * request the mapping of the event capability selector window + * corresponding to the pager object. + */ + _pt_sel = cap_selector_allocator()->alloc(); + res = create_pt(_pt_sel, pd_sel, _tid.ec_sel, Mtd(0), (mword_t)_invoke_handler); + if (res) + PERR("could not create pager object identity, create_pt returned %d\n", res); +} + + +Pager_object::~Pager_object() +{ + revoke(Obj_crd(_tid.ec_sel, 0)); + /* revoke utcb */ + Rights rwx(true, true, true); + revoke(Nova::Mem_crd((unsigned)Thread_base::myself()->utcb() >> 12, 0, rwx)); +} + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + /* supplement capability with object ID obtained from CAP session */ + obj->Object_pool::Entry::cap(_cap_session->alloc(Native_capability(obj->pt_sel(), 0))); + + /* add server object to object pool */ + insert(obj); + + /* return capability that uses the object id as badge */ + return reinterpret_cap_cast(obj->Object_pool::Entry::cap()); +} + + +void Pager_entrypoint::dissolve(Pager_object *obj) +{ + pf_lock()->lock(); + remove(obj); + pf_lock()->unlock(); +} + diff --git a/base-nova/src/base/server/server.cc b/base-nova/src/base/server/server.cc new file mode 100644 index 000000000..7f6222877 --- /dev/null +++ b/base-nova/src/base/server/server.cc @@ -0,0 +1,187 @@ +/* + * \brief NOVA-specific support code for the server-side RPC API + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2010-01-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* NOVA includes */ +#include + + +using namespace Genode; + + +/*********************** + ** Server entrypoint ** + ***********************/ + +Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj) +{ + using namespace Nova; + + int ec_sel = tid().ec_sel; + int pt_sel = cap_selector_allocator()->alloc(); + int pd_sel = cap_selector_allocator()->pd_sel(); + + /* create portal */ + int res = create_pt(pt_sel, pd_sel, ec_sel, Mtd(0), + (mword_t)_activation_entry); + + if (res) { + PERR("could not create server-object portal, create_pt returned %d\n", + res); + return Untyped_capability(); + } + + /* create capability to portal as destination address */ + Untyped_capability ep_cap = Native_capability(pt_sel, 0); + + /* supplement capability with object ID obtained from CAP session */ + Untyped_capability new_obj_cap = _cap_session->alloc(ep_cap); + + /* add server object to object pool */ + obj->cap(new_obj_cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return new_obj_cap; +} + + +void Rpc_entrypoint::_dissolve(Rpc_object_base *obj) +{ + /* make sure nobody is able to find this object */ + remove(obj); + + /* + * The activation may execute a blocking operation + * in a dispatch function. Before resolving the + * corresponding object, we need to ensure that + * it is no longer used by an activation. Therefore, + * we to need cancel an eventually blocking operation + * and let the activation leave the context of the + * object. + */ + _leave_server_object(obj); + + /* wait until nobody is inside dispatch */ + obj->lock(); + + /* now the object may be safely destructed */ +} + + +void Rpc_entrypoint::_activation_entry() +{ + /* retrieve portal id from eax */ + int id_pt; asm volatile ("" : "=a" (id_pt)); + Rpc_entrypoint *ep = static_cast(Thread_base::myself()); + + Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf); + + /* destination of next reply */ + srv.dst(Native_capability(id_pt, srv.badge())); + + int opcode = 0; + + srv >> IPC_WAIT >> opcode; + + /* set default return value */ + srv.ret(ERR_INVALID_OBJECT); + + /* atomically lookup and lock referenced object */ + { + Lock::Guard lock_guard(ep->_curr_obj_lock); + + ep->_curr_obj = ep->obj_by_id(srv.badge()); + if (!ep->_curr_obj) { + PERR("could not look up server object, return from call"); + srv << IPC_REPLY; + } + + ep->_curr_obj->lock(); + } + + /* dispatch request */ + try { srv.ret(ep->_curr_obj->dispatch(opcode, srv, srv)); } + catch (Blocking_canceled) { } + + ep->_curr_obj->unlock(); + ep->_curr_obj = 0; + + ep->_rcv_buf.rcv_prepare_pt_sel_window((Nova::Utcb *)ep->utcb()); + srv << IPC_REPLY; +} + + +void Rpc_entrypoint::entry() +{ + /* + * Thread entry is not used for activations on NOVA + */ +} + + +void Rpc_entrypoint::_leave_server_object(Rpc_object_base *obj) { } + + +void Rpc_entrypoint::_block_until_cap_valid() { } + + +void Rpc_entrypoint::activate() +{ + /* + * In contrast to a normal thread, a server activation is created at + * construction time. However, it executes no code because processing time + * is always provided by the caller of the server activation. To delay the + * processing of requests until the 'activate' function is called, we grab the + * '_curr_obj_lock' on construction and release it here. + */ + _curr_obj_lock.unlock(); +} + + +Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size, + const char *name, bool start_on_construction) +: + Thread_base(name, stack_size), + _curr_obj(0), + _curr_obj_lock(Lock::LOCKED), + _cap_session(cap_session) +{ + using namespace Nova; + + /* + * Create EC here to ensure that 'tid()' returns a valid 'ec_sel' + * portal selector even before 'activate' is called. + */ + + mword_t *sp = (mword_t *)&_context->stack[-4]; + mword_t utcb = (mword_t) &_context->utcb; + + /* create local EC */ + enum { CPU_NO = 0, GLOBAL = false }; + int res = create_ec(_tid.ec_sel, Cap_selector_allocator::pd_sel(), + CPU_NO, utcb, (mword_t)sp, + _tid.exc_pt_sel, GLOBAL); + if (res) + PDBG("create_ec returned %d", res); + + _rcv_buf.rcv_prepare_pt_sel_window((Utcb *)utcb); + + if (start_on_construction) + activate(); +} diff --git a/base-nova/src/base/thread/thread_context.cc b/base-nova/src/base/thread/thread_context.cc new file mode 100644 index 000000000..27542de52 --- /dev/null +++ b/base-nova/src/base/thread/thread_context.cc @@ -0,0 +1,38 @@ +/* + * \brief Thread-context specific part of the thread library + * \author Norman Feske + * \date 2010-01-19 + * + * This part of the thread library is required by the IPC framework + * also if no threads are used. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + + +Native_utcb *main_thread_utcb(); + + +Native_utcb *Thread_base::utcb() +{ + /* + * If 'utcb' is called on the object returned by 'myself', + * the 'this' pointer may be NULL (if the calling thread is + * the main thread). Therefore we allow this special case + * here. + */ + if (this == 0) return main_thread_utcb(); + + return &_context->utcb; +} + + diff --git a/base-nova/src/base/thread/thread_nova.cc b/base-nova/src/base/thread/thread_nova.cc new file mode 100644 index 000000000..53a07d187 --- /dev/null +++ b/base-nova/src/base/thread/thread_nova.cc @@ -0,0 +1,141 @@ +/* + * \brief NOVA-specific implementation of the Thread API + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* NOVA includes */ +#include + +using namespace Genode; + + +/** + * Entry point entered by new threads + */ +void Thread_base::_thread_start() +{ + Genode::Thread_base::myself()->entry(); + Genode::sleep_forever(); +} + + +static void request_event_portal(Pager_capability pager_cap, + int exc_base, int event) +{ + using namespace Nova; + Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); + + /* save original receive window */ + Crd orig_crd = utcb->crd_rcv; + + /* request event-handler portal */ + utcb->msg[0] = event; + utcb->set_msg_word(1); + utcb->crd_rcv = Obj_crd(exc_base + event, 0); + + int res = call(pager_cap.pt_sel()); + if (res) + PERR("request of event (%d) capability selector failed", event); + + /* restore original receive window */ + utcb->crd_rcv = orig_crd; +} + + +/***************** + ** Thread base ** + *****************/ + +void Thread_base::_init_platform_thread() +{ + using namespace Nova; + + /* + * Allocate capability selectors for the thread's execution context, + * scheduling context, running semaphore and exception handler portals. + */ + _tid.ec_sel = cap_selector_allocator()->alloc(); + _tid.sc_sel = cap_selector_allocator()->alloc(); + _tid.rs_sel = cap_selector_allocator()->alloc(); + _tid.pd_sel = cap_selector_allocator()->pd_sel(); + _tid.exc_pt_sel = cap_selector_allocator()->alloc(NUM_INITIAL_PT_LOG2); + + /* create thread at core */ + char buf[48]; + name(buf, sizeof(buf)); + Thread_capability thread_cap = env()->cpu_session()->create_thread(buf); + + /* create new pager object and assign it to the new thread */ + Pager_capability pager_cap = env()->rm_session()->add_client(thread_cap); + env()->cpu_session()->set_pager(thread_cap, pager_cap); + + /* register initial IP and SP at core */ + mword_t thread_sp = (mword_t)&_context->stack[-4]; + env()->cpu_session()->start(thread_cap, (addr_t)_thread_start, thread_sp); + + request_event_portal(pager_cap, _tid.exc_pt_sel, PT_SEL_STARTUP); + request_event_portal(pager_cap, _tid.exc_pt_sel, PT_SEL_PAGE_FAULT); + + /* create running semaphore required for locking */ + int res = create_sm(_tid.rs_sel, _tid.pd_sel, 0); + if (res) + PERR("create_sm returned %d", res); +} + + +void Thread_base::_deinit_platform_thread() +{ + Nova::revoke(Nova::Obj_crd(_tid.sc_sel, 0)); + Nova::revoke(Nova::Obj_crd(_tid.ec_sel, 0)); + Nova::revoke(Nova::Obj_crd(_tid.rs_sel, 0)); + + /* revoke utcb */ + Nova::Rights rwx(true, true, true); + Nova::revoke(Nova::Mem_crd((unsigned)Thread_base::myself()->utcb() >> 12, 0, rwx)); +} + + +void Thread_base::start() +{ + using namespace Nova; + + /* create execution context */ + enum { THREAD_CPU_NO = 0, THREAD_GLOBAL = true }; + int res = create_ec(_tid.ec_sel, _tid.pd_sel, THREAD_CPU_NO, (mword_t)&_context->utcb, + 0, _tid.exc_pt_sel, THREAD_GLOBAL); + if (res) + PDBG("create_ec returned %d", res); + + /* + * Create scheduling context + * + * With assigning a scheduling context to the execution context, the new + * thread will immediately start, enter the startup portal, and receives + * the configured initial IP and SP from core. + */ + res = create_sc(_tid.sc_sel, _tid.pd_sel, _tid.ec_sel, Qpd()); + if (res) + PERR("create_sc returned %d", res); +} + + +void Thread_base::cancel_blocking() +{ + Nova::sm_ctrl(_tid.rs_sel, Nova::SEMAPHORE_UP); +} diff --git a/base-nova/src/core/core_rm_session.cc b/base-nova/src/core/core_rm_session.cc new file mode 100644 index 000000000..673645dd4 --- /dev/null +++ b/base-nova/src/core/core_rm_session.cc @@ -0,0 +1,53 @@ +/* + * \brief Core-local RM session + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include +#include + +/* NOVA includes */ +#include + +using namespace Genode; + + +Rm_session::Local_addr +Core_rm_session::attach(Dataspace_capability ds_cap, size_t size, + off_t offset, bool use_local_addr, + Rm_session::Local_addr local_addr) +{ + Dataspace_component *ds = static_cast(_ds_ep->obj_by_cap(ds_cap)); + if (!ds) + throw Invalid_dataspace(); + + if (size == 0) + size = ds->size(); + + if (use_local_addr) { + PERR("Parameter 'use_local_addr' not supported within core"); + return 0; + } + + if (offset) { + PERR("Parameter 'offset' not supported within core"); + return 0; + } + + /* allocate range in core's virtual address space */ + return ds->core_local_addr(); +} diff --git a/base-nova/src/core/echo.cc b/base-nova/src/core/echo.cc new file mode 100644 index 000000000..9841b3965 --- /dev/null +++ b/base-nova/src/core/echo.cc @@ -0,0 +1,62 @@ +/* + * \brief Echo implementation + * \author Norman Feske + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include + +enum { + ECHO_UTCB_ADDR = 0x50000000, + ECHO_STACK_SIZE = 1024, + ECHO_CPU_NO = 0, + ECHO_GLOBAL = false, + ECHO_EXC_BASE = 0 +}; + +inline void *echo_stack_top() +{ + static char echo_stack[ECHO_STACK_SIZE]; + return &echo_stack[ECHO_STACK_SIZE - sizeof(long)]; +} + + +/** + * IDC handler for the echo portal, executed by the echo EC + */ static void echo_reply(){ Nova::reply(echo_stack_top()); } + + +Echo::Echo(Genode::addr_t utcb_addr) +: + _ec_sel(Genode::cap_selector_allocator()->alloc()), + _pt_sel(Genode::cap_selector_allocator()->alloc()), + _utcb((Nova::Utcb *)utcb_addr) +{ + using namespace Nova; + + /* create echo EC */ + int pd_sel = Genode::Cap_selector_allocator::pd_sel(); + int res = create_ec(_ec_sel, pd_sel, ECHO_CPU_NO, utcb_addr, + (mword_t)echo_stack_top(), ECHO_EXC_BASE, ECHO_GLOBAL); + + /* make error condition visible by raising an unhandled page fault */ + if (res) { ((void (*)())(res*0x10000))(); } + + /* set up echo portal to ourself */ + res = create_pt(_pt_sel, pd_sel, _ec_sel, Mtd(0), (mword_t)echo_reply); + if (res) { ((void (*)())(res*0x10001))(); } +} + + +Echo *echo() { static Echo inst(ECHO_UTCB_ADDR); return &inst; } diff --git a/base-nova/src/core/include/cap_session_component.h b/base-nova/src/core/include/cap_session_component.h new file mode 100644 index 000000000..23a7fb4e2 --- /dev/null +++ b/base-nova/src/core/include/cap_session_component.h @@ -0,0 +1,48 @@ +/* + * \brief Capability allocation service + * \author Norman Feske + * \date 2006-06-26 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ + +#include +#include +#include + +namespace Genode { + + class Cap_session_component : public Rpc_object + { + private: + + static long _unique_id_cnt; + + static Lock &_lock() + { + static Lock static_lock; + return static_lock; + } + + public: + + Native_capability alloc(Native_capability ep) + { + Lock::Guard lock_guard(_lock()); + + return Native_capability(ep.pt_sel(), ++_unique_id_cnt); + } + + void free(Native_capability cap) { } + }; +} + +#endif /* _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ */ diff --git a/base-nova/src/core/include/core_rm_session.h b/base-nova/src/core/include/core_rm_session.h new file mode 100644 index 000000000..5ddd3af33 --- /dev/null +++ b/base-nova/src/core/include/core_rm_session.h @@ -0,0 +1,52 @@ +/* + * \brief Core-local region manager session + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_RM_SESSION_H_ +#define _CORE__INCLUDE__CORE_RM_SESSION_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +namespace Genode { + + class Core_rm_session : public Rm_session + { + private: + + Rpc_entrypoint *_ds_ep; + + public: + + Core_rm_session(Rpc_entrypoint *ds_ep) : _ds_ep(ds_ep) { } + + Local_addr attach(Dataspace_capability ds_cap, size_t size=0, + off_t offset=0, bool use_local_addr = false, + Local_addr local_addr = 0); + + void detach(Local_addr) { } + + Pager_capability add_client(Thread_capability thread) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability handler) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_RM_SESSION_H_ */ diff --git a/base-nova/src/core/include/echo.h b/base-nova/src/core/include/echo.h new file mode 100644 index 000000000..27bfd7c6e --- /dev/null +++ b/base-nova/src/core/include/echo.h @@ -0,0 +1,54 @@ +/* + * \brief Echo interface + * \author Norman Feske + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _ECHO_H_ +#define _ECHO_H_ + +/* NOVA includes */ +#include + +class Echo +{ + private: + + int _ec_sel; /* execution context */ + int _pt_sel; /* portal */ + Nova::Utcb *_utcb; + + public: + + /** + * Constructor + * + * \param utcb_addr designated UTCB location for echo EC + */ + Echo(Genode::addr_t utcb_addr); + + /** + * UTCB of echo execution context + */ + Nova::Utcb *utcb() { return _utcb; } + + /** + * Capability selector for portal to echo + */ + int pt_sel() { return _pt_sel; } +}; + + +/** + * Get single 'Echo' instance + */ +Echo *echo(); + +#endif /* _ECHO_H_ */ diff --git a/base-nova/src/core/include/irq_session_component.h b/base-nova/src/core/include/irq_session_component.h new file mode 100644 index 000000000..ea4874d78 --- /dev/null +++ b/base-nova/src/core/include/irq_session_component.h @@ -0,0 +1,74 @@ +/* + * \brief IRQ session interface for NOVA + * \author Norman Feske + * \date 2010-01-30 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ + +#include +#include +#include + +#include + +namespace Genode { + + class Irq_session_component : public Rpc_object, + public List::Element + { + private: + + unsigned _irq_number; + Range_allocator *_irq_alloc; + + /* + * Each IRQ session uses a dedicated server activation + */ + enum { STACK_SIZE = 2048 }; + Rpc_entrypoint _ep; + Irq_session_capability _irq_cap; + + public: + + /** + * Constructor + * + * \param cap_session capability session to use + * \param irq_alloc platform-dependent IRQ allocator + * \param args session construction arguments + */ + Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args); + + /** + * Destructor + */ + ~Irq_session_component(); + + /** + * Return capability to this session + * + * If an initialization error occurs, returned capability is invalid. + */ + Irq_session_capability cap() const { return _irq_cap; } + + + /*************************** + ** Irq session interface ** + ***************************/ + + void wait_for_irq(); + }; +} + +#endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */ diff --git a/base-nova/src/core/include/map_local.h b/base-nova/src/core/include/map_local.h new file mode 100644 index 000000000..aa2a2477d --- /dev/null +++ b/base-nova/src/core/include/map_local.h @@ -0,0 +1,45 @@ +/* + * \brief Core-local mapping + * \author Norman Feske + * \date 2010-02-15 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__MAP_LOCAL_H_ +#define _CORE__INCLUDE__MAP_LOCAL_H_ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +namespace Genode { + + /** + * Map pages locally within core + * + * On NOVA, address-space mappings from core to core originate always from + * the physical address space. + * + * \param from_phys physical source address + * \param to_addr core-local destination address + * \param num_pages number of pages to map + * + * \return true on success + */ + inline bool map_local(addr_t from_phys, addr_t to_virt, size_t num_pages) + { + return ::map_local((Nova::Utcb *)Thread_base::myself()->utcb(), + from_phys, to_virt, num_pages, true); + } +} + +#endif /* _CORE__INCLUDE__MAP_LOCAL_H_ */ diff --git a/base-nova/src/core/include/nova_util.h b/base-nova/src/core/include/nova_util.h new file mode 100644 index 000000000..6b3722877 --- /dev/null +++ b/base-nova/src/core/include/nova_util.h @@ -0,0 +1,141 @@ +/* + * \brief NOVA-specific convenience functions + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _NOVA_UTIL_H_ +#define _NOVA_UTIL_H_ + +/* Genode includes */ +#include + +/* NOVA includes */ +#include + +/* local includes */ +#include +#include + +enum { verbose_local_map = false }; + + +/** + * Establish a one-to-one mapping + * + * \param utcb UTCB of the calling EC + * \param src_crd capability range descriptor of source + * resource to map locally + * \param dst_crd capability range descriptor of mapping + * target + * + * This functions sends a mapping from the calling EC to the echo EC. + * In order to successfully transfer the mapping, we have to open a + * corresponding receive window at the echo EC. We do this by poking + * a receive-capability-range descriptor directly onto the echo UTCB. + */ +static int map_local(Nova::Utcb *utcb, Nova::Crd src_crd, Nova::Crd dst_crd, + bool kern_pd = false) +{ + /* open receive window at the echo EC */ + echo()->utcb()->crd_rcv = dst_crd; + + /* reset message transfer descriptor */ + utcb->set_msg_word(0); + + /* append capability-range as message-transfer item */ + utcb->append_item(src_crd, 0, kern_pd); + + /* establish the mapping via a portal traversal */ + if (echo()->pt_sel() == 0) + PWRN("call to pt 0"); + return Nova::call(echo()->pt_sel()); +} + + +static inline int unmap_local(Nova::Crd crd, bool self = true) { + return Nova::revoke(crd, self); } + + +inline int map_local_one_to_one(Nova::Utcb *utcb, Nova::Crd crd) { + return map_local(utcb, crd, crd, true); } + + +/** + * Remap pages in the local address space + * + * \param utcb UTCB of the main thread + * \param from_start physical source address + * \param to_start local virtual destination address + * \param num_pages number of pages to map + */ +inline int map_local(Nova::Utcb *utcb, + Genode::addr_t from_start, Genode::addr_t to_start, + Genode::size_t num_pages, + bool kern_pd = false) +{ + if (verbose_local_map) + Genode::printf("::map_local: from %lx to %lx, %zd pages from kernel %u\n", + from_start, to_start, num_pages, kern_pd); + + using namespace Nova; + using namespace Genode; + Rights const rwx(true, true, true); + + size_t const size = num_pages << get_page_size_log2(); + + addr_t const from_end = from_start + size; + addr_t const to_end = to_start + size; + + for (addr_t offset = 0; offset < size; ) { + + addr_t const from_curr = from_start + offset; + addr_t const to_curr = to_start + offset; + + /* + * The common alignment corresponds to the number of least significant + * zero bits in both addresses. + */ + addr_t const common_bits = from_curr | to_curr; + + /* + * Find highest clear bit in 'diff', starting from the least + * significant candidate. We can skip all bits lower then + * 'get_page_size_log2()' because they are not relevant as flexpage + * size (and are always zero). + */ + size_t order = get_page_size_log2(); + for (; order < 32 && !(common_bits & (1 << order)); order++); + + /* + * Look if flexpage fits into both 'from' and 'to' address range + */ + + if (from_curr + (1 << order) > from_end) + order = log2(from_end - from_curr); + + if (to_curr + (1 << order) > to_end) + order = log2(to_end - to_curr); + + int const res = map_local(utcb, + Mem_crd((from_curr >> 12), order - get_page_size_log2(), rwx), + Mem_crd((to_curr >> 12), order - get_page_size_log2(), rwx), + kern_pd); + if (res) return res; + + /* advance offset by current flexpage size */ + offset += (1 << order); + } + return 0; +} + + +#endif /* _NOVA_UTIL_H_ */ diff --git a/base-nova/src/core/include/platform.h b/base-nova/src/core/include/platform.h new file mode 100644 index 000000000..49cda6d7a --- /dev/null +++ b/base-nova/src/core/include/platform.h @@ -0,0 +1,81 @@ +/* + * \brief Platform interface + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_H_ +#define _CORE__INCLUDE__PLATFORM_H_ + +/* core includes */ +#include +#include + +#include + +namespace Genode { + + class Platform : public Platform_generic + { + typedef Core_mem_allocator::Phys_allocator Phys_allocator; + + Core_mem_allocator _core_mem_alloc; /* core-accessible memory */ + Phys_allocator _io_mem_alloc; /* MMIO allocator */ + Phys_allocator _io_port_alloc; /* I/O port allocator */ + Phys_allocator _irq_alloc; /* IRQ allocator */ + Rom_fs _rom_fs; /* ROM file system */ + int _gsi_base_sel; /* cap selector of 1st IRQ */ + + /** + * Virtual address range usable by non-core processes + */ + addr_t _vm_base; + size_t _vm_size; + + void _preserve_page(addr_t phys_page); + + public: + + /** + * Constructor + */ + Platform(); + + + /******************************** + ** Generic platform interface ** + ********************************/ + + Range_allocator *ram_alloc() { return _core_mem_alloc.phys_alloc(); } + Range_allocator *io_mem_alloc() { return &_io_mem_alloc; } + Range_allocator *io_port_alloc() { return &_io_port_alloc; } + Range_allocator *irq_alloc() { return &_irq_alloc; } + Range_allocator *region_alloc() { return _core_mem_alloc.virt_alloc(); } + Allocator *core_mem_alloc() { return &_core_mem_alloc; } + addr_t vm_start() const { return _vm_base; } + size_t vm_size() const { return _vm_size; } + Rom_fs *rom_fs() { return &_rom_fs; } + + void wait_for_exit(); + bool supports_unmap() { return true; } + + + /******************* + ** NOVA specific ** + *******************/ + + /** + * Return capability selector of first global system interrupt + */ + int gsi_base_sel() const { return _gsi_base_sel; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-nova/src/core/include/platform_pd.h b/base-nova/src/core/include/platform_pd.h new file mode 100644 index 000000000..1ad065bfc --- /dev/null +++ b/base-nova/src/core/include/platform_pd.h @@ -0,0 +1,79 @@ +/* + * \brief Protection-domain facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_PD_H_ +#define _CORE__INCLUDE__PLATFORM_PD_H_ + +#include + +namespace Genode { + + class Platform_thread; + class Platform_pd + { + private: + + int _thread_cnt; + Native_capability _parent; + int _id; + int _pd_sel; + + public: + + /** + * Constructors + */ + Platform_pd(signed pd_id = -1, bool create = true); + + /** + * Destructor + */ + ~Platform_pd(); + + /** + * Bind thread to protection domain + * + * \return 0 on success or + * -1 if thread ID allocation failed. + */ + int bind_thread(Platform_thread *thread); + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread *thread); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent); + + /** + * Return portal capability selector for parent interface + */ + int parent_pt_sel() { return _parent.pt_sel(); } + + /** + * Assign PD selector to PD + */ + void assign_pd(int pd_sel) { _pd_sel = pd_sel; } + + int pd_sel() { return _pd_sel; } + + int id() { return _id; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ diff --git a/base-nova/src/core/include/platform_thread.h b/base-nova/src/core/include/platform_thread.h new file mode 100644 index 000000000..5fbbab9d3 --- /dev/null +++ b/base-nova/src/core/include/platform_thread.h @@ -0,0 +1,125 @@ +/* + * \brief Thread facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__PLATFORM_THREAD_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +namespace Genode { + + class Platform_pd; + class Platform_thread + { + private: + + Platform_pd *_pd; + Pager_object *_pager; + bool _is_main_thread; + int _id; + + public: + + enum { THREAD_INVALID = -1 }; /* invalid thread number */ + + /** + * Constructor + */ + Platform_thread(const char *name = 0, unsigned priority = 0, + int thread_id = THREAD_INVALID); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * \param cpu_no target cpu + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp, unsigned int cpu_no = 0); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Set pager + */ + void pager(Pager_object *pager) { _pager = pager; } + + Pager_object *pager() { return _pager; } + + /** + * Return identification of thread when faulting + */ + unsigned long pager_object_badge() const; + + /** + * Set the executing CPU for this thread. + */ + void set_cpu(unsigned int cpu_no); + + /** + * Get thread name + */ + const char *name() const { return "noname"; } + + /** + * Associate thread with protection domain + */ + void bind_to_pd(Platform_pd *pd, bool is_main_thread) + { + _pd = pd, _is_main_thread = is_main_thread; + } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-nova/src/core/include/util.h b/base-nova/src/core/include/util.h new file mode 100644 index 000000000..6c5248dbe --- /dev/null +++ b/base-nova/src/core/include/util.h @@ -0,0 +1,74 @@ +/* + * \brief Core-internal utilities + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__UTIL_H_ +#define _CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include + +namespace Genode { + + inline size_t get_page_size_log2() { return 12; } + inline size_t get_page_size() { return 1 << get_page_size_log2(); } + inline addr_t get_page_mask() { return ~(get_page_size() - 1); } + inline size_t get_super_page_size_log2() { return 22; } + inline size_t get_super_page_size() { return 1 << get_super_page_size_log2(); } + inline addr_t trunc_page(addr_t addr) { return addr & get_page_mask(); } + inline addr_t round_page(addr_t addr) { return trunc_page(addr + get_page_size() - 1); } + + + inline addr_t map_src_addr(addr_t core_local, addr_t phys) { return core_local; } + + + inline size_t constrain_map_size_log2(size_t size_log2) { return size_log2; } + + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long faulter_badge) + { + printf("%s (%s pf_addr=%p pf_ip=%p from %02lx)\n", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, + faulter_badge); + } + + + inline void backtrace() + { + using namespace Genode; + printf("\nbacktrace\n"); + printf(" %p\n", __builtin_return_address(0)); + printf(" %p\n", __builtin_return_address(1)); + printf(" %p\n", __builtin_return_address(2)); + printf(" %p\n", __builtin_return_address(3)); + printf(" %p\n", __builtin_return_address(4)); + } + + + inline void hexdump(void *addr) + { + unsigned char *s = (unsigned char *)addr; + printf("\nhexdump at 0x%p:\n", addr); + for (unsigned j = 0; j < 4; j++) { + printf(" "); + for (unsigned i = 0; i < 16; i++) + printf("0x%02x ", s[j*16 + i]); + printf("\n"); + } + } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-nova/src/core/io_mem_session_support.cc b/base-nova/src/core/io_mem_session_support.cc new file mode 100644 index 000000000..ba8c42b2f --- /dev/null +++ b/base-nova/src/core/io_mem_session_support.cc @@ -0,0 +1,59 @@ +/* + * \brief Implementation of the IO_MEM session interface + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2009-03-29 + * + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include +#include + +using namespace Genode; + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ + size_t page_rounded_size = (size + get_page_size() - 1) & get_page_mask(); + + Nova::Rights rwx(true, true, true); + int count = page_rounded_size >> 12; + + for (int i = 0; i < count; i++) + unmap_local(Nova::Mem_crd((base >> 12) + i, 0, rwx)); +} + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ + size_t page_rounded_size = (size + get_page_size() - 1) & get_page_mask(); + + /* align large I/O dataspaces on a super-page boundary within core */ + size_t alignment = (size >= get_super_page_size()) ? get_super_page_size_log2() + : get_page_size_log2(); + + /* allocate range in core's virtual address space */ + void *virt_addr; + if (!platform()->region_alloc()->alloc_aligned(page_rounded_size, + &virt_addr, alignment)) { + PERR("Could not allocate virtual address range in core of size %zd\n", + page_rounded_size); + return 0; + } + + /* map the dataspace's physical pages to local addresses */ + map_local((Nova::Utcb *)Thread_base::myself()->utcb(), + base, (addr_t)virt_addr, + page_rounded_size >> get_page_size_log2(), true); + + return (addr_t)virt_addr; +} diff --git a/base-nova/src/core/irq_session_component.cc b/base-nova/src/core/irq_session_component.cc new file mode 100644 index 000000000..7e929d2b7 --- /dev/null +++ b/base-nova/src/core/irq_session_component.cc @@ -0,0 +1,74 @@ +/* + * \brief Implementation of IRQ session component + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include + +/* NOVA includes */ +#include +#include + +using namespace Genode; + + +void Irq_session_component::wait_for_irq() +{ + Nova::sm_ctrl(_irq_number, Nova::SEMAPHORE_DOWN); +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _ep(cap_session, STACK_SIZE, "irq") +{ + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + if (irq_number == -1 || !irq_alloc || + irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK) { + PERR("Unavailable IRQ %lx requested", irq_number); + throw Root::Invalid_args(); + } + + /* alloc slector where IRQ will be mapped */ + _irq_number = cap_selector_allocator()->alloc(); + + /* since we run in APIC mode translate IRQ 0 (PIT) to 2 */ + if (!irq_number) + irq_number = 2; + + /* map IRQ number to selector */ + int ret = map_local((Nova::Utcb *)Thread_base::myself()->utcb(), + Nova::Obj_crd(platform_specific()->gsi_base_sel() + irq_number, 0), + Nova::Obj_crd(_irq_number, 0), + true); + if (ret) { + PERR("Could not map IRQ %d", _irq_number); + throw Root::Unavailable(); + } + + /* assign IRQ to CPU */ + enum { CPU = 0 }; + Nova::assign_gsi(_irq_number, 0, CPU); + /* initialize capability */ + _irq_cap = _ep.manage(this); +} + + +Irq_session_component::~Irq_session_component() { } diff --git a/base-nova/src/core/platform.cc b/base-nova/src/core/platform.cc new file mode 100644 index 000000000..bb60e58db --- /dev/null +++ b/base-nova/src/core/platform.cc @@ -0,0 +1,352 @@ +/* + * \brief Platform interface implementation + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include + +/* NOVA includes */ +#include + +using namespace Genode; +using namespace Nova; + + +enum { verbose_boot_info = true }; + + +/** + * Initial value of esp register, saved by the crt0 startup code + * + * This value contains the address of the hypervisor information page. + */ +extern long __initial_sp; + + +/** + * First available capability selector for custom use + */ +extern int __first_free_cap_selector; + + +/** + * Pointer to the UTCB of the main thread + */ +extern Utcb *__main_thread_utcb; + + +/** + * Virtual address range consumed by core's program image + */ +extern unsigned _prog_img_beg, _prog_img_end; + + +/** + * Capability selector of root PD + */ +extern int __local_pd_sel; + +/** + * Preserve physical page for the exclusive (read-only) use by core + */ +void Platform::_preserve_page(addr_t phys_page) +{ + /* locally map page one-to-one */ + map_local_one_to_one(__main_thread_utcb, + Mem_crd(phys_page, 0, + Rights(true, true, false))); + + /* remove page with command line from physical-memory allocator */ + addr_t addr = phys_page*get_page_size(); + _core_mem_alloc.phys_alloc()->remove_range(addr, get_page_size()); + _core_mem_alloc.virt_alloc()->remove_range(addr, get_page_size()); +} + + +/***************************** + ** Core page-fault handler ** + *****************************/ + +enum { CORE_PAGER_UTCB_ADDR = 0x50002000 }; + + +/** + * IDC handler for the page-fault portal + */ +static void page_fault_handler() +{ + Utcb *utcb = (Utcb *)CORE_PAGER_UTCB_ADDR; + + addr_t pf_addr = utcb->qual[1]; + addr_t pf_eip = utcb->eip; + addr_t pf_esp = utcb->esp; + + printf("\nPAGE-FAULT IN CORE: ADDR %lx IP %lx SP %lx stack trace follows...\n", + pf_addr, pf_eip, pf_esp); + + /* dump stack trace */ + struct Core_img + { + addr_t _beg; + addr_t _end; + addr_t *_ip; + + Core_img(addr_t sp) + { + extern addr_t _dtors_end; + _beg = (addr_t)&_prog_img_beg; + _end = (addr_t)&_dtors_end; + + _ip = (addr_t *)sp; + for (;!ip_valid(); _ip++) {} + } + + addr_t *ip() { return _ip; } + void next_ip() { _ip = ((addr_t *)*(_ip - 1)) + 1;} + bool ip_valid() { return *_ip >= _beg && *_ip < _end; } + }; + + int count = 1; + printf(" #%d %08lx %08lx\n", count++, pf_esp, pf_eip); + + Core_img dump(pf_esp); + while (dump.ip_valid()) { + printf(" #%d %p %08lx\n", count++, dump.ip(), *dump.ip()); + dump.next_ip(); + } + + sleep_forever(); +} + + +static void init_core_page_fault_handler() +{ + /* create echo EC */ + enum { + STACK_SIZE = 4*1024, + CPU_NO = 0, + GLOBAL = false, + EXC_BASE = 0 + }; + + static char stack[STACK_SIZE]; + + mword_t sp = (long)&stack[STACK_SIZE - sizeof(long)]; + int ec_sel = cap_selector_allocator()->alloc(); + + int ret = create_ec(ec_sel, __local_pd_sel, CPU_NO, CORE_PAGER_UTCB_ADDR, + (mword_t)sp, EXC_BASE, GLOBAL); + if (ret) + PDBG("create_ec returned %d", ret); + + /* set up page-fault portal */ + create_pt(PT_SEL_PAGE_FAULT, __local_pd_sel, ec_sel, + Mtd(Mtd::QUAL | Mtd::ESP | Mtd::EIP), + (mword_t)page_fault_handler); +} + + +/************** + ** Platform ** + **************/ + +Platform::Platform() : + _io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()), + _irq_alloc(core_mem_alloc()), + _vm_base(0), _vm_size(0) +{ + Hip *hip = (Hip *)__initial_sp; + + /* register UTCB of main thread */ + __main_thread_utcb = (Utcb *)(__initial_sp - get_page_size()); + + /* register start of usable capability range */ + __first_free_cap_selector = hip->sel_exc + hip->sel_gsi + 3; + + /* set core pd selector */ + __local_pd_sel = hip->sel_exc; + + /* locally map the whole I/O port range */ + enum { ORDER_64K = 16 }; + map_local_one_to_one(__main_thread_utcb, Io_crd(0, ORDER_64K)); + + /* + * Now that we can access the I/O ports for comport 0, printf works... + */ + + /* set up page fault handler for core - for debugging */ + init_core_page_fault_handler(); + + if (verbose_boot_info) { + printf("Hypervisor %s VMX\n", hip->has_feature_vmx() ? "features" : "does not feature"); + printf("Hypervisor %s SVM\n", hip->has_feature_svm() ? "features" : "does not feature"); + } + + /* initialize core allocators */ + size_t num_mem_desc = (hip->hip_length - hip->mem_desc_offset) + / hip->mem_desc_size; + + if (verbose_boot_info) + printf("Hypervisor info page contains %zd memory descriptors:\n", num_mem_desc); + + addr_t mem_desc_base = ((addr_t)hip + hip->mem_desc_offset); + + /* define core's virtual address space */ + addr_t virt_beg = get_page_size(); + addr_t virt_end = Thread_base::CONTEXT_AREA_VIRTUAL_BASE; + _core_mem_alloc.virt_alloc()->add_range(virt_beg, + virt_end - virt_beg); + + /* exclude core image from core's virtual address allocator */ + addr_t core_virt_beg = trunc_page((addr_t)&_prog_img_beg), + core_virt_end = round_page((addr_t)&_prog_img_end); + size_t core_size = core_virt_end - core_virt_beg; + _core_mem_alloc.virt_alloc()->remove_range(core_virt_beg, core_size); + + /* preserve context area in core's virtual address space */ + _core_mem_alloc.virt_alloc()->remove_range(Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + Thread_base::CONTEXT_AREA_VIRTUAL_SIZE); + + /* initialize core's physical-memory and I/O memory allocator */ + _io_mem_alloc.add_range(0, ~0xfff); + Hip::Mem_desc *mem_desc = (Hip::Mem_desc *)mem_desc_base; + for (unsigned i = 0; i < num_mem_desc; i++, mem_desc++) { + if (mem_desc->type != Hip::Mem_desc::AVAILABLE_MEMORY) continue; + + addr_t base = round_page(mem_desc->addr); + size_t size = trunc_page(mem_desc->addr + mem_desc->size - 1) - base; + + if (verbose_boot_info) + printf("detected physical memory: 0x%lx - 0x%zx\n", base, size); + + _io_mem_alloc.remove_range(base, size); + _core_mem_alloc.phys_alloc()->add_range(base, size); + } + + /* exclude all non-available memory from physical allocator */ + mem_desc = (Hip::Mem_desc *)mem_desc_base; + for (unsigned i = 0; i < num_mem_desc; i++, mem_desc++) { + if (mem_desc->type == Hip::Mem_desc::AVAILABLE_MEMORY) continue; + + addr_t base = trunc_page(mem_desc->addr); + size_t size = round_page(mem_desc->addr + mem_desc->size - 1) - base; + _io_mem_alloc.add_range(base, size); + _core_mem_alloc.phys_alloc()->remove_range(base, size); + } + + /* needed as I/O memory by the VESA driver */ + _io_mem_alloc.add_range(0, 0x1000); + _core_mem_alloc.phys_alloc()->remove_range(0, 0x1000); + + /* exclude pages holding multi-boot command lines from core allocators */ + mem_desc = (Hip::Mem_desc *)mem_desc_base; + addr_t prev_cmd_line_page = 0, curr_cmd_line_page = 0; + for (unsigned i = 0; i < num_mem_desc; i++, mem_desc++) { + if (mem_desc->type != Hip::Mem_desc::MULTIBOOT_MODULE) continue; + + curr_cmd_line_page = mem_desc->aux >> get_page_size_log2(); + if (curr_cmd_line_page == prev_cmd_line_page) continue; + + _preserve_page(curr_cmd_line_page); + prev_cmd_line_page = curr_cmd_line_page; + } + + /* preserve page following the last multi-boot command line */ + _preserve_page(curr_cmd_line_page + 1); + + /* + * From now on, it is save to use the core allocators... + */ + + /* build ROM file system */ + mem_desc = (Hip::Mem_desc *)mem_desc_base; + for (unsigned i = 0; i < num_mem_desc; i++, mem_desc++) { + if (mem_desc->type != Hip::Mem_desc::MULTIBOOT_MODULE) continue; + + const char *name = commandline_to_basename((char *)mem_desc->aux); + printf("detected multi-boot module: %s 0x%lx-0x%lx\n", name, + (long)mem_desc->addr, (long)(mem_desc->addr + mem_desc->size - 1)); + + void *core_local_addr = (void*)0x234; + if (!region_alloc()->alloc(round_page(mem_desc->size), &core_local_addr)) + PERR("could not locally map multi-boot module"); + + int res = map_local(__main_thread_utcb, mem_desc->addr, (addr_t)core_local_addr, + round_page(mem_desc->size) >> get_page_size_log2(), true); + if (res) + PERR("map_local failed res=%d", res); + + Rom_module *rom_module = new (core_mem_alloc()) + Rom_module((addr_t)core_local_addr, mem_desc->size, name); + _rom_fs.insert(rom_module); + + /* zero remainder of last ROM page */ + size_t count = 0x1000 - rom_module->size() % 0x1000; + if (count != 0x1000) + memset(reinterpret_cast(rom_module->addr() + rom_module->size()), 0, count); + + } + + /* export hypervisor info page as ROM module */ + _rom_fs.insert(new (core_mem_alloc()) + Rom_module((addr_t)hip, get_page_size(), "hypervisor_info_page")); + + /* configure non-core virtual address spaces as 2G-2G split */ + _vm_base = get_page_size(); + _vm_size = 2*1024*1024*1024UL - _vm_base; + + /* I/O port allocator (only meaningful for x86) */ + _io_port_alloc.add_range(0, 0x10000); + + /* IRQ allocator */ + _irq_alloc.add_range(0, hip->sel_gsi - 1); + _gsi_base_sel = hip->sel_exc; + + if (verbose_boot_info) { + printf(":virt_alloc: "); _core_mem_alloc.virt_alloc()->raw()->dump_addr_tree(); + printf(":phys_alloc: "); _core_mem_alloc.phys_alloc()->raw()->dump_addr_tree(); + printf(":io_mem_alloc: "); _io_mem_alloc.raw()->dump_addr_tree(); + } +} + + +/**************************************** + ** Support for core memory management ** + ****************************************/ + +bool Core_mem_allocator::Mapped_mem_allocator::_map_local(addr_t virt_addr, + addr_t phys_addr, + unsigned size_log2) +{ + map_local((Utcb *)Thread_base::myself()->utcb(), phys_addr, + virt_addr, 1 << (size_log2 - get_page_size_log2()), true); + return true; +} + + +/******************************** + ** Generic platform interface ** + ********************************/ + +void Platform::wait_for_exit() { sleep_forever(); } + + +void Core_parent::exit(int exit_value) { } diff --git a/base-nova/src/core/platform_pd.cc b/base-nova/src/core/platform_pd.cc new file mode 100644 index 000000000..ba543ff84 --- /dev/null +++ b/base-nova/src/core/platform_pd.cc @@ -0,0 +1,57 @@ +/* + * \brief Protection-domain facility + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + +using namespace Genode; + + +/*************************** + ** Public object members ** + ***************************/ + +int Platform_pd::bind_thread(Platform_thread *thread) +{ + thread->bind_to_pd(this, _thread_cnt == 0); + _thread_cnt++; + return 0; +} + + +void Platform_pd::unbind_thread(Platform_thread *thread) +{ + PDBG("not implemented"); +} + + +int Platform_pd::assign_parent(Native_capability parent) +{ + if (_parent.valid()) return -1; + _parent = parent; + return 0; +} + + +static int id_cnt; + + +Platform_pd::Platform_pd(signed pd_id, bool create) +: _thread_cnt(0), _id(++id_cnt), _pd_sel(0) { } + + +Platform_pd::~Platform_pd() +{ } diff --git a/base-nova/src/core/platform_thread.cc b/base-nova/src/core/platform_thread.cc new file mode 100644 index 000000000..dd89c088d --- /dev/null +++ b/base-nova/src/core/platform_thread.cc @@ -0,0 +1,143 @@ +/* + * \brief Thread facility + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include + +/* NOVA includes */ +#include + +using namespace Genode; + + +/********************* + ** Platform thread ** + *********************/ + +void Platform_thread::set_cpu(unsigned int cpu_no) +{ + PERR("not yet implemented"); +} + + +int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no) +{ + using namespace Nova; + + if (!_pager) { + PERR("pager undefined"); + return -1; + } + + enum { PD_EC_CPU_NO = 0, PD_UTCB = 0x6000000 }; + + _pager->initial_eip((addr_t)ip); + + if (!_is_main_thread || !_pd) { + _pager->initial_esp((addr_t)sp); + return 0; + } + + /* + * For the first thread of a new PD, use the initial stack pointer for + * reporting the thread's UTCB address. + */ + _pager->initial_esp(PD_UTCB + get_page_size()); + + /* locally map parent portal to initial portal window */ + int res = map_local((Nova::Utcb *)Thread_base::myself()->utcb(), + Obj_crd(_pd->parent_pt_sel(), 0), + Obj_crd(_pager->exc_pt_sel() + PT_SEL_PARENT, 0)); + if (res) + PERR("could not locally remap parent portal"); + + Obj_crd initial_pts(_pager->exc_pt_sel(), Nova::NUM_INITIAL_PT_LOG2); + + + int pd_sel = cap_selector_allocator()->pd_sel(); + int pd0_sel = _pager->exc_pt_sel() + Nova::PD_SEL; + _pd->assign_pd(pd0_sel); + + res = create_pd(pd0_sel, pd_sel, initial_pts); + if (res) + PERR("create_pd returned %d", res); + + int ec_sel = cap_selector_allocator()->alloc(); + int sc_sel = cap_selector_allocator()->alloc(); + + enum { THREAD_GLOBAL = true }; + res = create_ec(ec_sel, pd0_sel, PD_EC_CPU_NO, PD_UTCB, 0, 0, + THREAD_GLOBAL); + if (res) + PDBG("create_ec returned %d", res); + + res = create_sc(sc_sel, pd0_sel, ec_sel, Qpd()); + if (res) + PERR("create_sc returned %d", res); + + return 0; +} + + +void Platform_thread::pause() +{ + PDBG("not implemented"); +} + + +void Platform_thread::resume() +{ + PDBG("not implemented"); +} + + +int Platform_thread::state(Thread_state *state_dst) +{ + PWRN("not implemented"); + return -1; +} + + +void Platform_thread::cancel_blocking() { PWRN("not implemented"); } + + +unsigned long Platform_thread::pager_object_badge() +const +{ + return _pd ? ((_pd->id() << 16) || _id) : ~0; +} + + +static int id_cnt; + + +Platform_thread::Platform_thread(const char *name, unsigned, int thread_id) +: _pd(0), _id(++id_cnt) { } + + +Platform_thread::~Platform_thread() +{ + using namespace Nova; + + if (_is_main_thread) + revoke(Obj_crd(_pd->pd_sel(), 0)); +} diff --git a/base-nova/src/core/ram_session_support.cc b/base-nova/src/core/ram_session_support.cc new file mode 100644 index 000000000..1e3269063 --- /dev/null +++ b/base-nova/src/core/ram_session_support.cc @@ -0,0 +1,77 @@ +/* + * \brief Export RAM dataspace as shared memory object + * \author Norman Feske + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include +#include +#include + +/* NOVA includes */ +#include + +enum { verbose_ram_ds = false }; + +using namespace Genode; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { } + +void Ram_session_component::_clear_ds(Dataspace_component *ds) +{ + /* + * Map dataspace core-locally and clear its content + */ + + size_t page_rounded_size = (ds->size() + get_page_size() - 1) & get_page_mask(); + + /* + * Allocate range in core's virtual address space + * + * Start with trying to use natural alignment. If this does not work, + * successively weaken the alignment constraint until we hit the page size. + */ + void *virt_addr; + bool virt_alloc_succeeded = false; + size_t align_log2 = log2(ds->size()); + for (; align_log2 >= get_page_size_log2(); align_log2--) { + if (platform()->region_alloc()->alloc_aligned(page_rounded_size, + &virt_addr, align_log2)) { + virt_alloc_succeeded = true; + break; + } + } + + if (!virt_alloc_succeeded) { + PERR("Could not allocate virtual address range in core of size %zd\n", + page_rounded_size); + return; + } + + if (verbose_ram_ds) + printf("-- ram ds size=%x phys %lx has core-local addr %p\n", + page_rounded_size, ds->phys_addr(), virt_addr); + + /* map the dataspace's physical pages to local addresses */ + map_local((Nova::Utcb *)Thread_base::myself()->utcb(), + ds->phys_addr(), (addr_t)virt_addr, + page_rounded_size >> get_page_size_log2(), true); + + memset(virt_addr, 0, page_rounded_size); + + ds->assign_core_local_addr(virt_addr); +} diff --git a/base-nova/src/core/rm_session_support.cc b/base-nova/src/core/rm_session_support.cc new file mode 100644 index 000000000..d95a63e32 --- /dev/null +++ b/base-nova/src/core/rm_session_support.cc @@ -0,0 +1,55 @@ +/* + * \brief RM-session implementation + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2009-10-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +using namespace Genode; + +static const bool verbose = false; + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + addr_t const core_local_end = core_local_base + (size - 1); + off_t const core_to_virt = virt_base - core_local_base; + + Nova::Rights rwx(true, true, true); + + while (true) { + Nova::Mem_crd crd(core_local_base >> 12, 32, rwx); + Nova::lookup(&crd); + + if (crd.is_null()) { + PERR("Invalid unmap at local: %08lx virt: %08lx", + core_local_base, core_local_base + core_to_virt); + return; + } + + if (verbose) + PINF("Lookup core_addr: %08lx base: %x order: %x is null %d", core_local_base, crd.base(), crd.order(), crd.is_null()); + + unmap_local(crd, false); + + core_local_base = (crd.base() << 12) /* base address of mapping */ + + (0x1000 << crd.order()); /* size of mapping */ + + if (core_local_base > core_local_end) + return; + } +} diff --git a/base-nova/src/core/signal_source_component.cc b/base-nova/src/core/signal_source_component.cc new file mode 100644 index 000000000..2b1dda474 --- /dev/null +++ b/base-nova/src/core/signal_source_component.cc @@ -0,0 +1,73 @@ +/* + * \brief Implementation of the SIGNAL interface + * \author Norman Feske + * \date 2009-08-11 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +/* NOVA includes */ +#include + +using namespace Genode; + + +/***************************** + ** Signal-source component ** + *****************************/ + +void Signal_source_component::submit(Signal_context_component *context, + Ipc_ostream *ostream, + int cnt) +{ + /* enqueue signal to context */ + context->increment_signal_cnt(cnt); + + if (!context->is_enqueued()) { + _signal_queue.enqueue(context); + + /* wake up client */ + Nova::sm_ctrl(_blocking_semaphore.pt_sel(), Nova::SEMAPHORE_UP); + } +} + + +Signal_source::Signal Signal_source_component::wait_for_signal() +{ + if (_signal_queue.empty()) { + PWRN("unexpected call of wait_for_signal"); + return Signal(0, 0); + } + + /* dequeue and return pending signal */ + Signal_context_component *context = _signal_queue.dequeue(); + Signal result(context->imprint(), context->cnt()); + context->reset_signal_cnt(); + return result; +} + + +Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) +: _entrypoint(ep) +{ + /* initialized blocking semaphore */ + int sem_sel = cap_selector_allocator()->alloc(); + int ret = Nova::create_sm(sem_sel, cap_selector_allocator()->pd_sel(), 0); + if (ret) + PERR("create_sm returned %d", ret); + + _blocking_semaphore = Native_capability(sem_sel, 0); +} diff --git a/base-nova/src/core/target.inc b/base-nova/src/core/target.inc new file mode 100644 index 000000000..fc026e7b8 --- /dev/null +++ b/base-nova/src/core/target.inc @@ -0,0 +1,56 @@ +TARGET = core +LIBS = cxx ipc heap core_printf process pager lock \ + raw_signal raw_server + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = \ + main.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_start.cc \ + platform_thread.cc \ + platform_pd.cc \ + platform.cc \ + core_mem_alloc.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + core_rm_session.cc \ + cap_sel_alloc.cc \ + main_thread.cc \ + context_area.cc \ + echo.cc \ + dump_alloc.cc + +INC_DIR = $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include + +vpath main.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath io_port_session_component.cc $(GEN_CORE_DIR)/x86 +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath core_mem_alloc.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath context_area.cc $(GEN_CORE_DIR) +vpath %.cc $(REP_DIR)/src/core +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath cap_sel_alloc.cc $(REP_DIR)/src/base/env +vpath main_thread.cc $(REP_DIR)/src/base/env diff --git a/base-nova/src/core/target.mk b/base-nova/src/core/target.mk new file mode 100644 index 000000000..36a57d0f3 --- /dev/null +++ b/base-nova/src/core/target.mk @@ -0,0 +1,4 @@ +include $(PRG_DIR)/target.inc + +LD_SCRIPT_STATIC = $(REP_DIR)/src/platform/roottask.ld +LD_TEXT_ADDR = 0x100000 diff --git a/base-nova/src/core/thread_start.cc b/base-nova/src/core/thread_start.cc new file mode 100644 index 000000000..a7731e75a --- /dev/null +++ b/base-nova/src/core/thread_start.cc @@ -0,0 +1,63 @@ +/* + * \brief NOVA-specific implementation of the Thread API for core + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* NOVA includes */ +#include + +/* core includes */ +#include + +using namespace Genode; + + +/** + * This function is called for constructing server activations and pager + * objects. It allocates capability selectors for the thread's execution + * context and a synchronization-helper semaphore needed for 'Lock'. + */ +void Thread_base::_init_platform_thread() +{ + _tid.ec_sel = cap_selector_allocator()->alloc(); + _tid.sc_sel = ~0; /* not needed within core */ + _tid.rs_sel = cap_selector_allocator()->alloc(); + _tid.pd_sel = cap_selector_allocator()->pd_sel(); + + /* create running semaphore required for locking */ + int res = Nova::create_sm(_tid.rs_sel, _tid.pd_sel, 0); + if (res) + PERR("create_sm returned %d", res); +} + + +void Thread_base::_deinit_platform_thread() +{ + unmap_local(Nova::Obj_crd(_tid.sc_sel, 0)); + unmap_local(Nova::Obj_crd(_tid.ec_sel, 0)); + unmap_local(Nova::Obj_crd(_tid.rs_sel, 0)); +} + + +void Thread_base::start() +{ + /* + * On NOVA, core never starts regular threads. + */ +} diff --git a/base-nova/src/kernel/target.mk b/base-nova/src/kernel/target.mk new file mode 100644 index 000000000..78a90fdf2 --- /dev/null +++ b/base-nova/src/kernel/target.mk @@ -0,0 +1,35 @@ +TARGET = hypervisor +REQUIRES = x86 32bit nova +NOVA_SRC_DIR = $(REP_DIR)/contrib +NOVA_BUILD_DIR = $(BUILD_BASE_DIR)/kernel +STARTUP_LIB = +SRC_CC = $(sort $(notdir $(wildcard $(NOVA_SRC_DIR)/src/*.cpp))) +SRC_S = $(sort $(notdir $(wildcard $(NOVA_SRC_DIR)/src/*.S))) +INC_DIR = $(NOVA_SRC_DIR)/include +CC_OLEVEL = -Os +CC_WARN = -Wall -Wextra -Waggregate-return -Wcast-align -Wcast-qual \ + -Wconversion -Wdisabled-optimization -Wformat=2 \ + -Wmissing-format-attribute -Wmissing-noreturn -Wpacked \ + -Wpointer-arith -Wredundant-decls -Wshadow -Wwrite-strings \ + -Wabi -Wctor-dtor-privacy -Wno-non-virtual-dtor \ + -Wold-style-cast -Woverloaded-virtual -Wsign-promo \ + -Wframe-larger-than=64 -Wlogical-op -Wstrict-null-sentinel \ + -Wstrict-overflow=5 -Wvolatile-register-var +CC_OPT += -pipe -mpreferred-stack-boundary=2 -mregparm=3 -m32 \ + -fdata-sections -fomit-frame-pointer -freg-struct-return \ + -freorder-blocks -funit-at-a-time -fno-exceptions -fno-rtti \ + -fno-stack-protector -fvisibility-inlines-hidden +CXX_LINK_OPT = -Wl,--gc-sections -Wl,--warn-common -Wl,-static -Wl,-n +LD_TEXT_ADDR = 0xc0000000 +LD_SCRIPT_STATIC = hypervisor.o + +$(TARGET): hypervisor.o + +hypervisor.o: $(NOVA_SRC_DIR)/src/hypervisor.ld + $(VERBOSE)$(CC) $(INCLUDES) -MP -MMD -pipe -m32 -xc -E -P $< -o $@ + +clean cleanall: + $(VERBOSE)rm -rf $(NOVA_BUILD_DIR) + +vpath %.cpp $(NOVA_SRC_DIR)/src +vpath %.S $(NOVA_SRC_DIR)/src diff --git a/base-nova/src/lib/printf_stdio/printf_stdio.cc b/base-nova/src/lib/printf_stdio/printf_stdio.cc new file mode 100644 index 000000000..a1981ee00 --- /dev/null +++ b/base-nova/src/lib/printf_stdio/printf_stdio.cc @@ -0,0 +1,35 @@ +/* + * \brief Genode::printf back-end for stdio + * \author Norman Feske + * \date 2009-10-06 + * + * This library can be used by unit test executed on the host platform to + * direct output from the Genode framework to stdout. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + + +void Genode::printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + ::vprintf(format, list); + + va_end(list); +} + + +void Genode::vprintf(const char *format, va_list list) +{ + ::vprintf(format, list); +} diff --git a/base-nova/src/platform/_main_helper.h b/base-nova/src/platform/_main_helper.h new file mode 100644 index 000000000..5eb89fc51 --- /dev/null +++ b/base-nova/src/platform/_main_helper.h @@ -0,0 +1,58 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2009-12-28 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM___MAIN_HELPER_H_ +#define _PLATFORM___MAIN_HELPER_H_ + +#include + +/** + * Location of the main thread's UTCB, initialized by the startup code + */ +extern Nova::mword_t __main_thread_utcb; + +/** + * Initial value of esp register, saved by the crt0 startup code + * + * This value contains the address of the hypervisor information page. + */ +extern long __initial_sp; + +/** + * First available capability selector for custom use + */ +extern int __first_free_cap_selector; + +/** + * Selector of local protection domain + */ +extern int __local_pd_sel; + +static void main_thread_bootstrap() +{ + /* register UTCB of main thread */ + __main_thread_utcb = __initial_sp - Nova::PAGE_SIZE; + + /* register start of usable capability range */ + enum { FIRST_FREE_PORTAL = 0x1000 }; + + /* this variable may be set by the dynamic linker (ldso) */ + if (!__first_free_cap_selector) + __first_free_cap_selector = FIRST_FREE_PORTAL; + + /* register pd selector at cap allocator */ + __local_pd_sel = Nova::PD_SEL; +} + +#endif /* _PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-nova/src/platform/_main_parent_cap.h b/base-nova/src/platform/_main_parent_cap.h new file mode 100644 index 000000000..a730c18cb --- /dev/null +++ b/base-nova/src/platform/_main_parent_cap.h @@ -0,0 +1,46 @@ +/* + * \brief Obtain parent capability + * \author Norman Feske + * \date 2010-01-26 + * + * On NOVA, the parent capability consists of two parts, a local portal + * capability selector (as invokation address) and a global unique object ID. + * The parent portal is, by convention, capability selector 'PT_CAP_PARENT' + * supplied with the initial portals when the PD is created. The object ID is + * provided at the begin of the data segment of the loaded ELF image. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__MAIN_PARENT_CAP_H_ +#define _PLATFORM__MAIN_PARENT_CAP_H_ + +/* Genode includes */ +#include + +/* NOVA includes */ +#include + +namespace Genode { + + /** + * Return constructed parent capability + */ + Parent_capability parent_cap() + { + /* read capability from start of data section, containing object ID */ + Native_capability cap; + memcpy(&cap, (void *)&_parent_cap, sizeof(cap)); + + /* assemble parent capability from object ID and portal */ + return reinterpret_cap_cast(Native_capability(Nova::PT_SEL_PARENT, + cap.unique_id())); + } +} + +#endif /* _PLATFORM__MAIN_PARENT_CAP_H_ */ diff --git a/base-nova/src/platform/roottask.ld b/base-nova/src/platform/roottask.ld new file mode 100644 index 000000000..936a1798d --- /dev/null +++ b/base-nova/src/platform/roottask.ld @@ -0,0 +1,104 @@ +/* + * \brief Linker script for Genode programs + * \author Christian Helmuth + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* + * NOVA-specific change: NOVA does not support the BSS segment for + * roottask. Therefore, we have to place all BSS content into the + * data section. + */ + +ENTRY(_start) + +PHDRS +{ + ro PT_LOAD; + rw PT_LOAD; +} + +SECTIONS +{ + .text : { + /* begin of program image (link address) */ + _prog_img_beg = .; + + *(.init) + *(.text .text.* .gnu.linkonce.t.*) + *(.fini) + *(.rodata .rodata.* .gnu.linkonce.r.*) + + . = ALIGN(0x08); + + _ctors_start = .; + KEEP (*(.ctors)) + KEEP (*(SORT(.ctors.*))) + _ctors_end = .; + _dtors_start = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + _dtors_end = .; + } : ro = 0x90909090 + + /* Linux: exception section for uaccess mechanism */ + __ex_table : { *(__ex_table) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } + + . = ALIGN(0x1000); + + _prog_img_data = .; + + .data : { + /* + * Leave space for parent capability parameters at start of data + * section. The protection domain creator is reponsible for storing + * sane values here. + */ + _parent_cap = .; + _parent_cap_thread_id = .; + LONG(0xffffffff); + _parent_cap_local_name = .; + LONG(0xffffffff); + + *(.data .data.* .gnu.linkonce.d.*) + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } : rw + + /* exception frames for C++ */ + .eh_frame : { + __eh_frame_start__ = .; + KEEP (*(.eh_frame)) + LONG(0) + } : rw + + .init_array : { + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } + + .gcc_except_table : { + KEEP(*(.gcc_except_table)) + KEEP(*(.gcc_except_table.*)) + } + .dynamic : { *(.dynamic) } + + /* end of program image -- must be after last section */ + _prog_img_end = .; + + /DISCARD/ : { + *(.note) + *(.note.ABI-tag) + *(.comment) + } +} diff --git a/base-okl4/Makefile b/base-okl4/Makefile new file mode 100644 index 000000000..2747f074a --- /dev/null +++ b/base-okl4/Makefile @@ -0,0 +1,49 @@ +# +# \brief Download, unpack and patch OKL4 source code +# \author Stefan Kalkowski +# \date 2011-05-02 + +DOWNLOAD_DIR = download +CONTRIB_DIR = contrib/okl4 + +VERBOSE ?= @ +ECHO = @echo +OKL4_VERSION = okl4_2.1.1-patch.9 +OKL4_ARCHIVE = $(OKL4_VERSION).tar.gz +OKL4_URI = http://wiki.ok-labs.com/downloads/release-2.1.1-patch.9/$(OKL4_ARCHIVE) +PATCHES = $(shell find patches -name *.patch) + +# +# Print help information by default +# +help: + $(ECHO) + $(ECHO) "Prepare the OKL4 base repository" + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - download and extract the OKL4 source code" + $(ECHO) "clean - clean everything except downloaded archives" + $(ECHO) "cleanall - clean everything including downloaded archives" + $(ECHO) + +$(DOWNLOAD_DIR)/$(OKL4_ARCHIVE): + $(ECHO) "downloading source code to '$(DOWNLOAD_DIR)/'" + $(VERBOSE)mkdir -p $(DOWNLOAD_DIR) + $(VERBOSE)wget -c $(OKL4_URI) -O $@ + +$(CONTRIB_DIR): clean + +$(CONTRIB_DIR): $(DOWNLOAD_DIR)/$(OKL4_ARCHIVE) + $(ECHO) "unpacking source code to '$(CONTRIB_DIR)/'" + $(VERBOSE)tar xzf $< + $(VERBOSE)mv $(OKL4_VERSION) $@ + $(ECHO) "applying patches to '$(CONTRIB_DIR)/'" + $(VERBOSE)for i in $(PATCHES); do patch -d $@ -p1 < $$i; done + +prepare: $(CONTRIB_DIR) + +clean: + $(VERBOSE)rm -rf $(CONTRIB_DIR) + +cleanall: clean + $(VERBOSE)rm -rf $(DOWNLOAD_DIR) diff --git a/base-okl4/README b/base-okl4/README new file mode 100644 index 000000000..1dd127896 --- /dev/null +++ b/base-okl4/README @@ -0,0 +1,10 @@ +This repository contains the implementation of Genode for the OKL4 +kernel version 2.1. For further information, please refer to the +following documents: + +:[http://genode.org/community/wiki/GenodeOnOKL4 - Genode on OKL4 Wiki page]: + This Wiki page contains the information on how to build and use + Genode with OKL4. + +:[http://genode.org/documentation/articles/genode-on-okl4 - Bringing Genode to OKL4]: + This article explains the OKL4-specific porting work. diff --git a/base-okl4/contrib/generated/README b/base-okl4/contrib/generated/README new file mode 100644 index 000000000..9094cae31 --- /dev/null +++ b/base-okl4/contrib/generated/README @@ -0,0 +1,8 @@ +This directory and its subdirectories contain machine-generated code, +produced when building the OKL4 kernel with its native Scons build environment. + +It is not part of the Genode project and remains under the licence of the OKL4 kernel. + +You can obtain the OKL4 kernel version 2.1.1. here: + +[http://www.ok-labs.com - Open Kernel Labs] diff --git a/base-okl4/contrib/generated/x86/asmsyms.h b/base-okl4/contrib/generated/x86/asmsyms.h new file mode 100644 index 000000000..79b3447a2 --- /dev/null +++ b/base-okl4/contrib/generated/x86/asmsyms.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008 Open Kernel Labs, Inc. (Copyright Holder). + * All rights reserved. + * + * 1. Redistribution and use of OKL4 (Software) in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * (a) Redistributions of source code must retain this clause 1 + * (including paragraphs (a), (b) and (c)), clause 2 and clause 3 + * (Licence Terms) and the above copyright notice. + * + * (b) Redistributions in binary form must reproduce the above + * copyright notice and the Licence Terms in the documentation and/or + * other materials provided with the distribution. + * + * (c) Redistributions in any form must be accompanied by information on + * how to obtain complete source code for: + * (i) the Software; and + * (ii) all accompanying software that uses (or is intended to + * use) the Software whether directly or indirectly. Such source + * code must: + * (iii) either be included in the distribution or be available + * for no more than the cost of distribution plus a nominal fee; + * and + * (iv) be licensed by each relevant holder of copyright under + * either the Licence Terms (with an appropriate copyright notice) + * or the terms of a licence which is approved by the Open Source + * Initative. For an executable file, "complete source code" + * means the source code for all modules it contains and includes + * associated build and other files reasonably required to produce + * the executable. + * + * 2. THIS SOFTWARE IS PROVIDED ``AS IS'' AND, TO THE EXTENT PERMITTED BY + * LAW, ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. WHERE ANY WARRANTY IS + * IMPLIED AND IS PREVENTED BY LAW FROM BEING DISCLAIMED THEN TO THE + * EXTENT PERMISSIBLE BY LAW: (A) THE WARRANTY IS READ DOWN IN FAVOUR OF + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT) AND (B) ANY LIMITATIONS PERMITTED BY LAW (INCLUDING AS TO + * THE EXTENT OF THE WARRANTY AND THE REMEDIES AVAILABLE IN THE EVENT OF + * BREACH) ARE DEEMED PART OF THIS LICENCE IN A FORM MOST FAVOURABLE TO + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT). IN THE LICENCE TERMS, "PARTICIPANT" INCLUDES EVERY + * PERSON WHO HAS CONTRIBUTED TO THE SOFTWARE OR WHO HAS BEEN INVOLVED IN + * THE DISTRIBUTION OR DISSEMINATION OF THE SOFTWARE. + * + * 3. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ANY OTHER PARTICIPANT BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* machine-generated file - do NOT edit */ +#ifndef __ASMSYMS_H__ +#define __ASMSYMS_H__ + +#define TSTATE_POLLING 0xb +#define TSTATE_WAITING_FOREVER 0xffffffff +#define TSTATE_RUNNING 0x2 +#define PT_SIZE 0x4c +#define OFS_CAP_TCB 0x0 + +#endif /* __ASMSYMS_H__ */ diff --git a/base-okl4/contrib/generated/x86/kdb_class_helper.h b/base-okl4/contrib/generated/x86/kdb_class_helper.h new file mode 100644 index 000000000..7a9c33853 --- /dev/null +++ b/base-okl4/contrib/generated/x86/kdb_class_helper.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008 Open Kernel Labs, Inc. (Copyright Holder). + * All rights reserved. + * + * 1. Redistribution and use of OKL4 (Software) in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * (a) Redistributions of source code must retain this clause 1 + * (including paragraphs (a), (b) and (c)), clause 2 and clause 3 + * (Licence Terms) and the above copyright notice. + * + * (b) Redistributions in binary form must reproduce the above + * copyright notice and the Licence Terms in the documentation and/or + * other materials provided with the distribution. + * + * (c) Redistributions in any form must be accompanied by information on + * how to obtain complete source code for: + * (i) the Software; and + * (ii) all accompanying software that uses (or is intended to + * use) the Software whether directly or indirectly. Such source + * code must: + * (iii) either be included in the distribution or be available + * for no more than the cost of distribution plus a nominal fee; + * and + * (iv) be licensed by each relevant holder of copyright under + * either the Licence Terms (with an appropriate copyright notice) + * or the terms of a licence which is approved by the Open Source + * Initative. For an executable file, "complete source code" + * means the source code for all modules it contains and includes + * associated build and other files reasonably required to produce + * the executable. + * + * 2. THIS SOFTWARE IS PROVIDED ``AS IS'' AND, TO THE EXTENT PERMITTED BY + * LAW, ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. WHERE ANY WARRANTY IS + * IMPLIED AND IS PREVENTED BY LAW FROM BEING DISCLAIMED THEN TO THE + * EXTENT PERMISSIBLE BY LAW: (A) THE WARRANTY IS READ DOWN IN FAVOUR OF + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT) AND (B) ANY LIMITATIONS PERMITTED BY LAW (INCLUDING AS TO + * THE EXTENT OF THE WARRANTY AND THE REMEDIES AVAILABLE IN THE EVENT OF + * BREACH) ARE DEEMED PART OF THIS LICENCE IN A FORM MOST FAVOURABLE TO + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT). IN THE LICENCE TERMS, "PARTICIPANT" INCLUDES EVERY + * PERSON WHO HAS CONTRIBUTED TO THE SOFTWARE OR WHO HAS BEEN INVOLVED IN + * THE DISTRIBUTION OR DISSEMINATION OF THE SOFTWARE. + * + * 3. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ANY OTHER PARTICIPANT BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +static cmd_ret_t cmd_list_clists(cmd_group_t*); +static cmd_ret_t cmd_show_clist(cmd_group_t*); +static cmd_ret_t cmd__help(cmd_group_t*); +static cmd_ret_t cmd__abort(cmd_group_t*); +static cmd_ret_t cmd__prior(cmd_group_t*); +static cmd_ret_t cmd_mode_switch(cmd_group_t*); +static cmd_ret_t cmd_toggle_cpuprefix(cmd_group_t*); +static cmd_ret_t cmd_go(cmd_group_t*); +static cmd_ret_t cmd_arch(cmd_group_t*); +static cmd_ret_t cmd_config(cmd_group_t*); +static cmd_ret_t cmd_statistics(cmd_group_t*); +static cmd_ret_t cmd_profiling(cmd_group_t*); +static cmd_ret_t cmd_kmem_stats(cmd_group_t*); +static cmd_ret_t cmd_dump_ptab(cmd_group_t*); +static cmd_ret_t cmd_wordsize(cmd_group_t*); +static cmd_ret_t cmd_memdump(cmd_group_t*); +static cmd_ret_t cmd_memdump_remote(cmd_group_t*); +static cmd_ret_t cmd_memdump_phys(cmd_group_t*); +static cmd_ret_t cmd_show_mutexes(cmd_group_t*); +static cmd_ret_t cmd_show_dep_graph(cmd_group_t*); +static cmd_ret_t cmd_profile_print(cmd_group_t*); +static cmd_ret_t cmd_profile_enable(cmd_group_t*); +static cmd_ret_t cmd_profile_disable(cmd_group_t*); +static cmd_ret_t cmd_profile_reset(cmd_group_t*); +static cmd_ret_t cmd_reboot(cmd_group_t*); +static cmd_ret_t cmd_show_ready(cmd_group_t*); +static cmd_ret_t cmd_show_units(cmd_group_t*); +static cmd_ret_t cmd_list_spaces(cmd_group_t*); +static cmd_ret_t cmd_show_space(cmd_group_t*); +static cmd_ret_t cmd_show_tcb(cmd_group_t*); +static cmd_ret_t cmd_show_tcbext(cmd_group_t*); +static cmd_ret_t cmd_tid_format(cmd_group_t*); +static cmd_ret_t cmd_tracebuffer(cmd_group_t*); +static cmd_ret_t cmd_tb_info(cmd_group_t*); +static cmd_ret_t cmd_tb_logmask(cmd_group_t*); +static cmd_ret_t cmd_tb_dump(cmd_group_t*); +static cmd_ret_t cmd_tb_reset(cmd_group_t*); +static cmd_ret_t cmd_tracepoints(cmd_group_t*); +static cmd_ret_t cmd_tp_list(cmd_group_t*); +static cmd_ret_t cmd_tp_conf(cmd_group_t*); +static cmd_ret_t cmd_tp_conf_all(cmd_group_t*); +static cmd_ret_t cmd_tp_reset(cmd_group_t*); +static cmd_ret_t cmd_tp_enable(cmd_group_t*); +static cmd_ret_t cmd_tp_disable(cmd_group_t*); +static cmd_ret_t cmd_breakpoint(cmd_group_t*); +static cmd_ret_t cmd_singlestep(cmd_group_t*); +static cmd_ret_t cmd_branchstep(cmd_group_t*); +static cmd_ret_t cmd_reset(cmd_group_t*); +static cmd_ret_t cmd_show_ctrlregs(cmd_group_t*); +static cmd_ret_t cmd_dump_msrs(cmd_group_t*); +static cmd_ret_t cmd_dump_current_frame(cmd_group_t*); +static cmd_ret_t cmd_ports(cmd_group_t*); +static cmd_ret_t cmd_idt(cmd_group_t*); +static cmd_ret_t cmd_nmi(cmd_group_t*); +static cmd_ret_t cmd_gdt(cmd_group_t*); +static cmd_ret_t cmd_cpu(cmd_group_t*); +static cmd_ret_t cmd_show_lvt(cmd_group_t*); +static cmd_ret_t cmd_dump_frame(cmd_group_t*); +static cmd_ret_t cmd_dump_ldt(cmd_group_t*); +static cmd_ret_t cmd_apic(cmd_group_t*); +static cmd_ret_t cmd_dumpvga(cmd_group_t*); diff --git a/base-okl4/contrib/generated/x86/ktcb_layout.h b/base-okl4/contrib/generated/x86/ktcb_layout.h new file mode 100644 index 000000000..ba3547749 --- /dev/null +++ b/base-okl4/contrib/generated/x86/ktcb_layout.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008 Open Kernel Labs, Inc. (Copyright Holder). + * All rights reserved. + * + * 1. Redistribution and use of OKL4 (Software) in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * (a) Redistributions of source code must retain this clause 1 + * (including paragraphs (a), (b) and (c)), clause 2 and clause 3 + * (Licence Terms) and the above copyright notice. + * + * (b) Redistributions in binary form must reproduce the above + * copyright notice and the Licence Terms in the documentation and/or + * other materials provided with the distribution. + * + * (c) Redistributions in any form must be accompanied by information on + * how to obtain complete source code for: + * (i) the Software; and + * (ii) all accompanying software that uses (or is intended to + * use) the Software whether directly or indirectly. Such source + * code must: + * (iii) either be included in the distribution or be available + * for no more than the cost of distribution plus a nominal fee; + * and + * (iv) be licensed by each relevant holder of copyright under + * either the Licence Terms (with an appropriate copyright notice) + * or the terms of a licence which is approved by the Open Source + * Initative. For an executable file, "complete source code" + * means the source code for all modules it contains and includes + * associated build and other files reasonably required to produce + * the executable. + * + * 2. THIS SOFTWARE IS PROVIDED ``AS IS'' AND, TO THE EXTENT PERMITTED BY + * LAW, ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. WHERE ANY WARRANTY IS + * IMPLIED AND IS PREVENTED BY LAW FROM BEING DISCLAIMED THEN TO THE + * EXTENT PERMISSIBLE BY LAW: (A) THE WARRANTY IS READ DOWN IN FAVOUR OF + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT) AND (B) ANY LIMITATIONS PERMITTED BY LAW (INCLUDING AS TO + * THE EXTENT OF THE WARRANTY AND THE REMEDIES AVAILABLE IN THE EVENT OF + * BREACH) ARE DEEMED PART OF THIS LICENCE IN A FORM MOST FAVOURABLE TO + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT). IN THE LICENCE TERMS, "PARTICIPANT" INCLUDES EVERY + * PERSON WHO HAS CONTRIBUTED TO THE SOFTWARE OR WHO HAS BEEN INVOLVED IN + * THE DISTRIBUTION OR DISSEMINATION OF THE SOFTWARE. + * + * 3. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ANY OTHER PARTICIPANT BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* machine-generated file - do NOT edit */ +#ifndef __KTCB_LAYOUT__H__ +#define __KTCB_LAYOUT__H__ + +//#define BUILD_KTCB_LAYOUT 1 + #define OFS_ARCH_KTCB_CONTEXT 0x00 /* 0 */ +#define OFS_ARCH_KTCB_EXCEPTION_CONTINUATION 0x4c /* 76 */ +#define OFS_ARCH_KTCB_SYSCALL_CONTINUATION 0x50 /* 80 */ +#define OFS_ARCH_KTCB_TIMER 0x54 /* 84 */ +#define OFS_ARCH_KTCB_LDT 0x68 /* 104 */ +#define OFS_ARCH_KTCB_EXC_CODE 0xb8 /* 184 */ + +#endif /* __TCB_LAYOUT__H__ */ diff --git a/base-okl4/contrib/generated/x86/linker.ld b/base-okl4/contrib/generated/x86/linker.ld new file mode 100644 index 000000000..d1ad6fdc4 --- /dev/null +++ b/base-okl4/contrib/generated/x86/linker.ld @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2008 Open Kernel Labs, Inc. (Copyright Holder). + * All rights reserved. + * + * 1. Redistribution and use of OKL4 (Software) in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * (a) Redistributions of source code must retain this clause 1 + * (including paragraphs (a), (b) and (c)), clause 2 and clause 3 + * (Licence Terms) and the above copyright notice. + * + * (b) Redistributions in binary form must reproduce the above + * copyright notice and the Licence Terms in the documentation and/or + * other materials provided with the distribution. + * + * (c) Redistributions in any form must be accompanied by information on + * how to obtain complete source code for: + * (i) the Software; and + * (ii) all accompanying software that uses (or is intended to + * use) the Software whether directly or indirectly. Such source + * code must: + * (iii) either be included in the distribution or be available + * for no more than the cost of distribution plus a nominal fee; + * and + * (iv) be licensed by each relevant holder of copyright under + * either the Licence Terms (with an appropriate copyright notice) + * or the terms of a licence which is approved by the Open Source + * Initative. For an executable file, "complete source code" + * means the source code for all modules it contains and includes + * associated build and other files reasonably required to produce + * the executable. + * + * 2. THIS SOFTWARE IS PROVIDED ``AS IS'' AND, TO THE EXTENT PERMITTED BY + * LAW, ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. WHERE ANY WARRANTY IS + * IMPLIED AND IS PREVENTED BY LAW FROM BEING DISCLAIMED THEN TO THE + * EXTENT PERMISSIBLE BY LAW: (A) THE WARRANTY IS READ DOWN IN FAVOUR OF + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT) AND (B) ANY LIMITATIONS PERMITTED BY LAW (INCLUDING AS TO + * THE EXTENT OF THE WARRANTY AND THE REMEDIES AVAILABLE IN THE EVENT OF + * BREACH) ARE DEEMED PART OF THIS LICENCE IN A FORM MOST FAVOURABLE TO + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT). IN THE LICENCE TERMS, "PARTICIPANT" INCLUDES EVERY + * PERSON WHO HAS CONTRIBUTED TO THE SOFTWARE OR WHO HAS BEEN INVOLVED IN + * THE DISTRIBUTION OR DISSEMINATION OF THE SOFTWARE. + * + * 3. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ANY OTHER PARTICIPANT BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +ENTRY(_start) +BOOTMEM_SIZE = 128K; +_start_text_phys = 0x00100000 + 0x200; +_start_text = _start_text_phys + 0xF0000000; +SECTIONS +{ + .text _start_text : AT (ADDR(.text) - 0xF0000000) + { + *(.mb_header) + *(.text) + *(.text.*) + *(.gnu.linkonce.*) + *(.spinlock) + } + .rodata . : AT (ADDR(.rodata) - 0xF0000000) + { + *(.rodata*) + } + .roinit : AT(ADDR(.roinit) - 0xF0000000) + { + *(.roinit*) + } + . = ALIGN(4K); + _start_cpu_local = .; + .cpulocal . : AT (ADDR(.cpulocal) - 0xF0000000) + { + *(.data.cpulocal.tcb) + *(.data.cpulocal.utcb) + *(.data.cpulocal) + *(.data.ia32.cpulocal) + } + _end_cpu_local = .; + . = ALIGN(4K); + .data . : AT (ADDR(.data) - 0xF0000000) + { + *(.data) + *(.data.ia32.idt); + *(.data.ia32.exc_all); + *(.data.ia32.exc_common); + *(.data.*) + _bss_start = .; + *(.bss) + _bss_end = .; + } + . = ALIGN(4K); + .kdebug . : AT(ADDR(.kdebug) - 0xF0000000) + { + *(.kdebug) + *(.kdebug-bss) + *(.kdebug.*) + } + .sets . : AT(ADDR(.sets) - 0xF0000000) + { + . = ALIGN(16); + _start_setlist = .; + *(.setlist) + _end_setlist = .; + . = ALIGN(16); + _start_sets = .; + *(SORT(set_*)) + _end_sets = .; + } + _end_text = ALIGN(4K); + _end_text_phys = _end_text - 0xF0000000; + . = ALIGN(4K); + .reserve_bootmem : AT(ADDR(.reserve_bootmem) - 0xF0000000) + { + *(.reserve_bootmem) + } + _start_init = . - 0xF0000000; + .init (. - 0xF0000000) : + { + *(.init) + *(.init.data) + *(.init.smp) + *(.init.*) + } + _end_init = .; + /DISCARD/ : + { + *(*) + *(.eh_frame) + *(.note) + *(.comment) + *(.delete) + } + _end_text_phys = _end_text - 0xF0000000; + _start_bootmem_phys = _start_bootmem - 0xF0000000; + _end_bootmem_phys = _end_bootmem - 0xF0000000; +} diff --git a/base-okl4/contrib/generated/x86/macro_sets.cc b/base-okl4/contrib/generated/x86/macro_sets.cc new file mode 100644 index 000000000..42acd8799 --- /dev/null +++ b/base-okl4/contrib/generated/x86/macro_sets.cc @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2008 Open Kernel Labs, Inc. (Copyright Holder). + * All rights reserved. + * + * 1. Redistribution and use of OKL4 (Software) in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * (a) Redistributions of source code must retain this clause 1 + * (including paragraphs (a), (b) and (c)), clause 2 and clause 3 + * (Licence Terms) and the above copyright notice. + * + * (b) Redistributions in binary form must reproduce the above + * copyright notice and the Licence Terms in the documentation and/or + * other materials provided with the distribution. + * + * (c) Redistributions in any form must be accompanied by information on + * how to obtain complete source code for: + * (i) the Software; and + * (ii) all accompanying software that uses (or is intended to + * use) the Software whether directly or indirectly. Such source + * code must: + * (iii) either be included in the distribution or be available + * for no more than the cost of distribution plus a nominal fee; + * and + * (iv) be licensed by each relevant holder of copyright under + * either the Licence Terms (with an appropriate copyright notice) + * or the terms of a licence which is approved by the Open Source + * Initative. For an executable file, "complete source code" + * means the source code for all modules it contains and includes + * associated build and other files reasonably required to produce + * the executable. + * + * 2. THIS SOFTWARE IS PROVIDED ``AS IS'' AND, TO THE EXTENT PERMITTED BY + * LAW, ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. WHERE ANY WARRANTY IS + * IMPLIED AND IS PREVENTED BY LAW FROM BEING DISCLAIMED THEN TO THE + * EXTENT PERMISSIBLE BY LAW: (A) THE WARRANTY IS READ DOWN IN FAVOUR OF + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT) AND (B) ANY LIMITATIONS PERMITTED BY LAW (INCLUDING AS TO + * THE EXTENT OF THE WARRANTY AND THE REMEDIES AVAILABLE IN THE EVENT OF + * BREACH) ARE DEEMED PART OF THIS LICENCE IN A FORM MOST FAVOURABLE TO + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT). IN THE LICENCE TERMS, "PARTICIPANT" INCLUDES EVERY + * PERSON WHO HAS CONTRIBUTED TO THE SOFTWARE OR WHO HAS BEEN INVOLVED IN + * THE DISTRIBUTION OR DISSEMINATION OF THE SOFTWARE. + * + * 3. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ANY OTHER PARTICIPANT BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* machine-generated file - do NOT edit */ +/* Types? Where we're going we don't need types... */ + +#include + +/* Begin Macro Set __kdb_group_config */ +/* externs for macro set: __kdb_group_config */ +extern word_t __setentry___kdb_group_config___kdb_config_cmd__abort; +extern word_t __setentry___kdb_group_config___kdb_config_cmd__help; +extern word_t __setentry___kdb_group_config___kdb_config_cmd__prior; +extern word_t __setentry___kdb_group_config___kdb_config_cmd_mode_switch; +extern word_t __setentry___kdb_group_config___kdb_config_cmd_tid_format; +extern word_t __setentry___kdb_group_config___kdb_config_cmd_wordsize; +/* set array for macro set: __kdb_group_config */ +word_t * __macro_set___kdb_group_config_array[] = { + &__setentry___kdb_group_config___kdb_config_cmd__abort, + &__setentry___kdb_group_config___kdb_config_cmd__help, + &__setentry___kdb_group_config___kdb_config_cmd__prior, + &__setentry___kdb_group_config___kdb_config_cmd_mode_switch, + &__setentry___kdb_group_config___kdb_config_cmd_tid_format, + &__setentry___kdb_group_config___kdb_config_cmd_wordsize, + NULL }; /* end set array for __kdb_group_config */ + +/* set count for macro set: __kdb_group_config */ +word_t __macro_set___kdb_group_config_count = (sizeof(__macro_set___kdb_group_config_array) / sizeof(word_t*)) - 1; +/* End Macro Set __kdb_group_config */ + + +/* Begin Macro Set __kmem_groups */ +/* externs for macro set: __kmem_groups */ +extern word_t __setentry___kmem_groups___kmem_group_kmem_clist; +extern word_t __setentry___kmem_groups___kmem_group_kmem_clistids; +extern word_t __setentry___kmem_groups___kmem_group_kmem_ll; +extern word_t __setentry___kmem_groups___kmem_group_kmem_misc; +extern word_t __setentry___kmem_groups___kmem_group_kmem_mutex; +extern word_t __setentry___kmem_groups___kmem_group_kmem_mutexids; +extern word_t __setentry___kmem_groups___kmem_group_kmem_pgtab; +extern word_t __setentry___kmem_groups___kmem_group_kmem_resources; +extern word_t __setentry___kmem_groups___kmem_group_kmem_root_clist; +extern word_t __setentry___kmem_groups___kmem_group_kmem_space; +extern word_t __setentry___kmem_groups___kmem_group_kmem_spaceids; +extern word_t __setentry___kmem_groups___kmem_group_kmem_stack; +extern word_t __setentry___kmem_groups___kmem_group_kmem_tcb; +extern word_t __setentry___kmem_groups___kmem_group_kmem_trace; +extern word_t __setentry___kmem_groups___kmem_group_kmem_utcb; +/* set array for macro set: __kmem_groups */ +word_t * __macro_set___kmem_groups_array[] = { + &__setentry___kmem_groups___kmem_group_kmem_clist, + &__setentry___kmem_groups___kmem_group_kmem_clistids, + &__setentry___kmem_groups___kmem_group_kmem_ll, + &__setentry___kmem_groups___kmem_group_kmem_misc, + &__setentry___kmem_groups___kmem_group_kmem_mutex, + &__setentry___kmem_groups___kmem_group_kmem_mutexids, + &__setentry___kmem_groups___kmem_group_kmem_pgtab, + &__setentry___kmem_groups___kmem_group_kmem_resources, + &__setentry___kmem_groups___kmem_group_kmem_root_clist, + &__setentry___kmem_groups___kmem_group_kmem_space, + &__setentry___kmem_groups___kmem_group_kmem_spaceids, + &__setentry___kmem_groups___kmem_group_kmem_stack, + &__setentry___kmem_groups___kmem_group_kmem_tcb, + &__setentry___kmem_groups___kmem_group_kmem_trace, + &__setentry___kmem_groups___kmem_group_kmem_utcb, + NULL }; /* end set array for __kmem_groups */ + +/* set count for macro set: __kmem_groups */ +word_t __macro_set___kmem_groups_count = (sizeof(__macro_set___kmem_groups_array) / sizeof(word_t*)) - 1; +/* End Macro Set __kmem_groups */ + + +/* Begin Macro Set __kdb_group_arch */ +/* externs for macro set: __kdb_group_arch */ +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd__abort; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd__help; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd__prior; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_apic; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_branchstep; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_breakpoint; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_cpu; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_dump_ldt; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_dump_msrs; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_dumpvga; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_gdt; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_idt; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_nmi; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_ports; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_show_ctrlregs; +extern word_t __setentry___kdb_group_arch___kdb_arch_cmd_singlestep; +/* set array for macro set: __kdb_group_arch */ +word_t * __macro_set___kdb_group_arch_array[] = { + &__setentry___kdb_group_arch___kdb_arch_cmd__abort, + &__setentry___kdb_group_arch___kdb_arch_cmd__help, + &__setentry___kdb_group_arch___kdb_arch_cmd__prior, + &__setentry___kdb_group_arch___kdb_arch_cmd_apic, + &__setentry___kdb_group_arch___kdb_arch_cmd_branchstep, + &__setentry___kdb_group_arch___kdb_arch_cmd_breakpoint, + &__setentry___kdb_group_arch___kdb_arch_cmd_cpu, + &__setentry___kdb_group_arch___kdb_arch_cmd_dump_ldt, + &__setentry___kdb_group_arch___kdb_arch_cmd_dump_msrs, + &__setentry___kdb_group_arch___kdb_arch_cmd_dumpvga, + &__setentry___kdb_group_arch___kdb_arch_cmd_gdt, + &__setentry___kdb_group_arch___kdb_arch_cmd_idt, + &__setentry___kdb_group_arch___kdb_arch_cmd_nmi, + &__setentry___kdb_group_arch___kdb_arch_cmd_ports, + &__setentry___kdb_group_arch___kdb_arch_cmd_show_ctrlregs, + &__setentry___kdb_group_arch___kdb_arch_cmd_singlestep, + NULL }; /* end set array for __kdb_group_arch */ + +/* set count for macro set: __kdb_group_arch */ +word_t __macro_set___kdb_group_arch_count = (sizeof(__macro_set___kdb_group_arch_array) / sizeof(word_t*)) - 1; +/* End Macro Set __kdb_group_arch */ + + +/* Begin Macro Set tracepoint_set */ +/* externs for macro set: tracepoint_set */ +extern word_t __setentry_tracepoint_set___tracepoint_DEADLOCK_DETECTED; +extern word_t __setentry_tracepoint_set___tracepoint_EXCEPTION_IPC; +extern word_t __setentry_tracepoint_set___tracepoint_FPAGE_MAP; +extern word_t __setentry_tracepoint_set___tracepoint_FPAGE_OVERMAP; +extern word_t __setentry_tracepoint_set___tracepoint_FPAGE_READ; +extern word_t __setentry_tracepoint_set___tracepoint_FPAGE_UNMAP; +extern word_t __setentry_tracepoint_set___tracepoint_IA32_GP; +extern word_t __setentry_tracepoint_set___tracepoint_IA32_NOMATH; +extern word_t __setentry_tracepoint_set___tracepoint_IA32_SEGRELOAD; +extern word_t __setentry_tracepoint_set___tracepoint_IA32_UD; +extern word_t __setentry_tracepoint_set___tracepoint_INTERRUPT; +extern word_t __setentry_tracepoint_set___tracepoint_IPC_TRANSFER; +extern word_t __setentry_tracepoint_set___tracepoint_KMEM_ALLOC; +extern word_t __setentry_tracepoint_set___tracepoint_KMEM_FREE; +extern word_t __setentry_tracepoint_set___tracepoint_PAGEFAULT_KERNEL; +extern word_t __setentry_tracepoint_set___tracepoint_PAGEFAULT_USER; +extern word_t __setentry_tracepoint_set___tracepoint_PREEMPTION_FAULT; +extern word_t __setentry_tracepoint_set___tracepoint_PREEMPTION_SIGNALED; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_CACHE_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_CAP_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_EXCHANGE_REGISTERS; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_INTERRUPT_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_IPC; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_MAP_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_MEMORY_COPY; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_MUTEX; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_MUTEX_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_PLATFORM_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_SCHEDULE; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_SECURITY_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_SPACE_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_SPACE_SWITCH; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_THREAD_CONTROL; +extern word_t __setentry_tracepoint_set___tracepoint_SYSCALL_THREAD_SWITCH; +extern word_t __setentry_tracepoint_set___tracepoint_TIMESLICE_EXPIRED; +extern word_t __setentry_tracepoint_set___tracepoint_UNWIND; +/* set array for macro set: tracepoint_set */ +word_t * __macro_set_tracepoint_set_array[] = { + &__setentry_tracepoint_set___tracepoint_DEADLOCK_DETECTED, + &__setentry_tracepoint_set___tracepoint_EXCEPTION_IPC, + &__setentry_tracepoint_set___tracepoint_FPAGE_MAP, + &__setentry_tracepoint_set___tracepoint_FPAGE_OVERMAP, + &__setentry_tracepoint_set___tracepoint_FPAGE_READ, + &__setentry_tracepoint_set___tracepoint_FPAGE_UNMAP, + &__setentry_tracepoint_set___tracepoint_IA32_GP, + &__setentry_tracepoint_set___tracepoint_IA32_NOMATH, + &__setentry_tracepoint_set___tracepoint_IA32_SEGRELOAD, + &__setentry_tracepoint_set___tracepoint_IA32_UD, + &__setentry_tracepoint_set___tracepoint_INTERRUPT, + &__setentry_tracepoint_set___tracepoint_IPC_TRANSFER, + &__setentry_tracepoint_set___tracepoint_KMEM_ALLOC, + &__setentry_tracepoint_set___tracepoint_KMEM_FREE, + &__setentry_tracepoint_set___tracepoint_PAGEFAULT_KERNEL, + &__setentry_tracepoint_set___tracepoint_PAGEFAULT_USER, + &__setentry_tracepoint_set___tracepoint_PREEMPTION_FAULT, + &__setentry_tracepoint_set___tracepoint_PREEMPTION_SIGNALED, + &__setentry_tracepoint_set___tracepoint_SYSCALL_CACHE_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_CAP_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_EXCHANGE_REGISTERS, + &__setentry_tracepoint_set___tracepoint_SYSCALL_INTERRUPT_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_IPC, + &__setentry_tracepoint_set___tracepoint_SYSCALL_MAP_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_MEMORY_COPY, + &__setentry_tracepoint_set___tracepoint_SYSCALL_MUTEX, + &__setentry_tracepoint_set___tracepoint_SYSCALL_MUTEX_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_PLATFORM_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_SCHEDULE, + &__setentry_tracepoint_set___tracepoint_SYSCALL_SECURITY_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_SPACE_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_SPACE_SWITCH, + &__setentry_tracepoint_set___tracepoint_SYSCALL_THREAD_CONTROL, + &__setentry_tracepoint_set___tracepoint_SYSCALL_THREAD_SWITCH, + &__setentry_tracepoint_set___tracepoint_TIMESLICE_EXPIRED, + &__setentry_tracepoint_set___tracepoint_UNWIND, + NULL }; /* end set array for tracepoint_set */ + +/* set count for macro set: tracepoint_set */ +word_t __macro_set_tracepoint_set_count = (sizeof(__macro_set_tracepoint_set_array) / sizeof(word_t*)) - 1; +/* End Macro Set tracepoint_set */ + + +/* Begin Macro Set __kdb_group_statistics */ +/* externs for macro set: __kdb_group_statistics */ +extern word_t __setentry___kdb_group_statistics___kdb_statistics_cmd__abort; +extern word_t __setentry___kdb_group_statistics___kdb_statistics_cmd__help; +extern word_t __setentry___kdb_group_statistics___kdb_statistics_cmd__prior; +extern word_t __setentry___kdb_group_statistics___kdb_statistics_cmd_kmem_stats; +/* set array for macro set: __kdb_group_statistics */ +word_t * __macro_set___kdb_group_statistics_array[] = { + &__setentry___kdb_group_statistics___kdb_statistics_cmd__abort, + &__setentry___kdb_group_statistics___kdb_statistics_cmd__help, + &__setentry___kdb_group_statistics___kdb_statistics_cmd__prior, + &__setentry___kdb_group_statistics___kdb_statistics_cmd_kmem_stats, + NULL }; /* end set array for __kdb_group_statistics */ + +/* set count for macro set: __kdb_group_statistics */ +word_t __macro_set___kdb_group_statistics_count = (sizeof(__macro_set___kdb_group_statistics_array) / sizeof(word_t*)) - 1; +/* End Macro Set __kdb_group_statistics */ + + +/* Begin Macro Set kdb_initfuncs */ +/* externs for macro set: kdb_initfuncs */ +/* set array for macro set: kdb_initfuncs */ +word_t * __macro_set_kdb_initfuncs_array[] = { + NULL }; /* end set array for kdb_initfuncs */ + +/* set count for macro set: kdb_initfuncs */ +word_t __macro_set_kdb_initfuncs_count = (sizeof(__macro_set_kdb_initfuncs_array) / sizeof(word_t*)) - 1; +/* End Macro Set kdb_initfuncs */ + + +/* Begin Macro Set __kdb_group_root */ +/* externs for macro set: __kdb_group_root */ +extern word_t __setentry___kdb_group_root___kdb_root_cmd__abort; +extern word_t __setentry___kdb_group_root___kdb_root_cmd__help; +extern word_t __setentry___kdb_group_root___kdb_root_cmd__prior; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_arch; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_config; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_dump_current_frame; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_dump_frame; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_dump_ptab; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_go; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_list_clists; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_list_spaces; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_memdump; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_memdump_phys; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_memdump_remote; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_reboot; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_reset; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_show_clist; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_show_dep_graph; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_show_mutexes; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_show_ready; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_show_space; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_show_tcb; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_show_tcbext; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_statistics; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_tracebuffer; +extern word_t __setentry___kdb_group_root___kdb_root_cmd_tracepoints; +/* set array for macro set: __kdb_group_root */ +word_t * __macro_set___kdb_group_root_array[] = { + &__setentry___kdb_group_root___kdb_root_cmd__abort, + &__setentry___kdb_group_root___kdb_root_cmd__help, + &__setentry___kdb_group_root___kdb_root_cmd__prior, + &__setentry___kdb_group_root___kdb_root_cmd_arch, + &__setentry___kdb_group_root___kdb_root_cmd_config, + &__setentry___kdb_group_root___kdb_root_cmd_dump_current_frame, + &__setentry___kdb_group_root___kdb_root_cmd_dump_frame, + &__setentry___kdb_group_root___kdb_root_cmd_dump_ptab, + &__setentry___kdb_group_root___kdb_root_cmd_go, + &__setentry___kdb_group_root___kdb_root_cmd_list_clists, + &__setentry___kdb_group_root___kdb_root_cmd_list_spaces, + &__setentry___kdb_group_root___kdb_root_cmd_memdump, + &__setentry___kdb_group_root___kdb_root_cmd_memdump_phys, + &__setentry___kdb_group_root___kdb_root_cmd_memdump_remote, + &__setentry___kdb_group_root___kdb_root_cmd_reboot, + &__setentry___kdb_group_root___kdb_root_cmd_reset, + &__setentry___kdb_group_root___kdb_root_cmd_show_clist, + &__setentry___kdb_group_root___kdb_root_cmd_show_dep_graph, + &__setentry___kdb_group_root___kdb_root_cmd_show_mutexes, + &__setentry___kdb_group_root___kdb_root_cmd_show_ready, + &__setentry___kdb_group_root___kdb_root_cmd_show_space, + &__setentry___kdb_group_root___kdb_root_cmd_show_tcb, + &__setentry___kdb_group_root___kdb_root_cmd_show_tcbext, + &__setentry___kdb_group_root___kdb_root_cmd_statistics, + &__setentry___kdb_group_root___kdb_root_cmd_tracebuffer, + &__setentry___kdb_group_root___kdb_root_cmd_tracepoints, + NULL }; /* end set array for __kdb_group_root */ + +/* set count for macro set: __kdb_group_root */ +word_t __macro_set___kdb_group_root_count = (sizeof(__macro_set___kdb_group_root_array) / sizeof(word_t*)) - 1; +/* End Macro Set __kdb_group_root */ + + +/* Begin Macro Set __kdb_group_tracepoints */ +/* externs for macro set: __kdb_group_tracepoints */ +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd__abort; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd__help; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd__prior; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_conf; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_conf_all; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_disable; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_enable; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_list; +extern word_t __setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_reset; +/* set array for macro set: __kdb_group_tracepoints */ +word_t * __macro_set___kdb_group_tracepoints_array[] = { + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd__abort, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd__help, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd__prior, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_conf, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_conf_all, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_disable, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_enable, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_list, + &__setentry___kdb_group_tracepoints___kdb_tracepoints_cmd_tp_reset, + NULL }; /* end set array for __kdb_group_tracepoints */ + +/* set count for macro set: __kdb_group_tracepoints */ +word_t __macro_set___kdb_group_tracepoints_count = (sizeof(__macro_set___kdb_group_tracepoints_array) / sizeof(word_t*)) - 1; +/* End Macro Set __kdb_group_tracepoints */ + + +/* Begin Macro Set __kdb_group_tracebuf */ +/* externs for macro set: __kdb_group_tracebuf */ +extern word_t __setentry___kdb_group_tracebuf___kdb_tracebuf_cmd__abort; +extern word_t __setentry___kdb_group_tracebuf___kdb_tracebuf_cmd__help; +extern word_t __setentry___kdb_group_tracebuf___kdb_tracebuf_cmd__prior; +extern word_t __setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_dump; +extern word_t __setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_info; +extern word_t __setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_logmask; +extern word_t __setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_reset; +/* set array for macro set: __kdb_group_tracebuf */ +word_t * __macro_set___kdb_group_tracebuf_array[] = { + &__setentry___kdb_group_tracebuf___kdb_tracebuf_cmd__abort, + &__setentry___kdb_group_tracebuf___kdb_tracebuf_cmd__help, + &__setentry___kdb_group_tracebuf___kdb_tracebuf_cmd__prior, + &__setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_dump, + &__setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_info, + &__setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_logmask, + &__setentry___kdb_group_tracebuf___kdb_tracebuf_cmd_tb_reset, + NULL }; /* end set array for __kdb_group_tracebuf */ + +/* set count for macro set: __kdb_group_tracebuf */ +word_t __macro_set___kdb_group_tracebuf_count = (sizeof(__macro_set___kdb_group_tracebuf_array) / sizeof(word_t*)) - 1; +/* End Macro Set __kdb_group_tracebuf */ + + + diff --git a/base-okl4/contrib/generated/x86/tcb_layout.h b/base-okl4/contrib/generated/x86/tcb_layout.h new file mode 100644 index 000000000..7c2c50474 --- /dev/null +++ b/base-okl4/contrib/generated/x86/tcb_layout.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2008 Open Kernel Labs, Inc. (Copyright Holder). + * All rights reserved. + * + * 1. Redistribution and use of OKL4 (Software) in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: + * + * (a) Redistributions of source code must retain this clause 1 + * (including paragraphs (a), (b) and (c)), clause 2 and clause 3 + * (Licence Terms) and the above copyright notice. + * + * (b) Redistributions in binary form must reproduce the above + * copyright notice and the Licence Terms in the documentation and/or + * other materials provided with the distribution. + * + * (c) Redistributions in any form must be accompanied by information on + * how to obtain complete source code for: + * (i) the Software; and + * (ii) all accompanying software that uses (or is intended to + * use) the Software whether directly or indirectly. Such source + * code must: + * (iii) either be included in the distribution or be available + * for no more than the cost of distribution plus a nominal fee; + * and + * (iv) be licensed by each relevant holder of copyright under + * either the Licence Terms (with an appropriate copyright notice) + * or the terms of a licence which is approved by the Open Source + * Initative. For an executable file, "complete source code" + * means the source code for all modules it contains and includes + * associated build and other files reasonably required to produce + * the executable. + * + * 2. THIS SOFTWARE IS PROVIDED ``AS IS'' AND, TO THE EXTENT PERMITTED BY + * LAW, ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. WHERE ANY WARRANTY IS + * IMPLIED AND IS PREVENTED BY LAW FROM BEING DISCLAIMED THEN TO THE + * EXTENT PERMISSIBLE BY LAW: (A) THE WARRANTY IS READ DOWN IN FAVOUR OF + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT) AND (B) ANY LIMITATIONS PERMITTED BY LAW (INCLUDING AS TO + * THE EXTENT OF THE WARRANTY AND THE REMEDIES AVAILABLE IN THE EVENT OF + * BREACH) ARE DEEMED PART OF THIS LICENCE IN A FORM MOST FAVOURABLE TO + * THE COPYRIGHT HOLDER (AND, IN THE CASE OF A PARTICIPANT, THAT + * PARTICIPANT). IN THE LICENCE TERMS, "PARTICIPANT" INCLUDES EVERY + * PERSON WHO HAS CONTRIBUTED TO THE SOFTWARE OR WHO HAS BEEN INVOLVED IN + * THE DISTRIBUTION OR DISSEMINATION OF THE SOFTWARE. + * + * 3. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ANY OTHER PARTICIPANT BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* machine-generated file - do NOT edit */ +#ifndef __TCB_LAYOUT__H__ +#define __TCB_LAYOUT__H__ + +//#define BUILD_TCB_LAYOUT 1 + #define OFS_TCB_MYSELF_GLOBAL 0x00 /* 0 */ +#define OFS_TCB_UTCB_LOCATION 0x04 /* 4 */ +#define OFS_TCB_UTCB 0x08 /* 8 */ +#define OFS_TCB_SPACE 0x0c /* 12 */ +#define OFS_TCB_SPACE_ID 0x10 /* 16 */ +#define OFS_TCB_BASE_SPACE 0x14 /* 20 */ +#define OFS_TCB_PAGE_DIRECTORY 0x18 /* 24 */ +#define OFS_TCB_PAGER 0x1c /* 28 */ +#define OFS_TCB_THREAD_LOCK 0x24 /* 36 */ +#define OFS_TCB_THREAD_STATE 0x28 /* 40 */ +#define OFS_TCB_PARTNER 0x2c /* 44 */ +#define OFS_TCB_END_POINT 0x30 /* 48 */ +#define OFS_TCB_WAITING_FOR 0x40 /* 64 */ +#define OFS_TCB_EXCEPTION_HANDLER 0x44 /* 68 */ +#define OFS_TCB_RESOURCE_BITS 0x4c /* 76 */ +#define OFS_TCB_CONT 0x50 /* 80 */ +#define OFS_TCB_PREEMPTION_CONTINUATION 0x54 /* 84 */ +#define OFS_TCB_ARCH 0x58 /* 88 */ +#define OFS_TCB_SUSPENDED 0x114 /* 276 */ +#define OFS_TCB_POST_SYSCALL_CALLBACK 0x118 /* 280 */ +#define OFS_TCB_READY_LIST 0x11c /* 284 */ +#define OFS_TCB_BLOCKED_LIST 0x124 /* 292 */ +#define OFS_TCB_MUTEXES_HEAD 0x12c /* 300 */ +#define OFS_TCB_PRESENT_LIST 0x130 /* 304 */ +#define OFS_TCB_BASE_PRIO 0x138 /* 312 */ +#define OFS_TCB_EFFECTIVE_PRIO 0x13c /* 316 */ +#define OFS_TCB_TIMESLICE_LENGTH 0x140 /* 320 */ +#define OFS_TCB_CURRENT_TIMESLICE 0x144 /* 324 */ +#define OFS_TCB_SCHEDULER 0x148 /* 328 */ +#define OFS_TCB_SAVED_PARTNER 0x150 /* 336 */ +#define OFS_TCB_SAVED_STATE 0x154 /* 340 */ +#define OFS_TCB_RESOURCES 0x158 /* 344 */ +#define OFS_TCB_THREAD_LIST 0x15c /* 348 */ +#define OFS_TCB_DEBUG_NAME 0x164 /* 356 */ +#define OFS_TCB_SAVED_SENT_FROM 0x174 /* 372 */ +#define OFS_TCB_SYS_DATA 0x178 /* 376 */ +#define OFS_TCB_TCB_IDX 0x1b0 /* 432 */ +#define OFS_TCB_MASTER_CAP 0x1b4 /* 436 */ +#define OFS_TCB_SENT_FROM 0x1b8 /* 440 */ +#define OFS_TCB_IRQ_STACK 0x1bc /* 444 */ + +#endif /* __TCB_LAYOUT__H__ */ diff --git a/base-okl4/doc/notes.txt b/base-okl4/doc/notes.txt new file mode 100644 index 000000000..842f5a4cd --- /dev/null +++ b/base-okl4/doc/notes.txt @@ -0,0 +1,863 @@ + + =================================================== + Bringing the Genode OS Framework to the OKL4 kernel + =================================================== + + Norman Feske + + +This article documents the process of bringing the Genode OS Framework to a new +kernel platform, namely the OKL4 kernel developed by OK-Labs. OKL4 is an +industry-grade kernel that is deployed in millions of mobile phones. + +For our work, we went for the OKL4 version 2.1 for two reasons. First, +whereas this version officially supports the x86 architecture, the later +version 3 is pretty much focused on the ARM architecture. At present, the x86 +architecture is our primary platform for Genode development. Second, we like +to follow the evolution of OKL4 from its genesis (L4ka::Pistachio) to the +capability-based kernel design as pursued with the later versions. On this +path, the version 2.1 is an important milestone, which we wont like to miss. +Nevertheless of having chosen version 2.1 to begin with, we plan to bring +Genode to later versions of OKL4 as well. + +In the article, we face numerous challenges such as integrating OKL4 support +into Genode's build system, exploring the OKL4 kernel interface and the +boot procedure, adapting Genode's framework libraries to the feature set +provided by the new kernel, and accessing interrupts and other hardware +resources. + +The intended audience are developers interested in exploring +the realms of the L4-microkernel world and kernel developers who consider +running Genode as user-land infrastructure on top of their kernel. +For the latter group, we laid out the article as a rough step-by-step +guide providing our proposed methodology for approaching the port of +Genode to a new kernel platform. At many places, the article refers +to the source code of Genode, in particular the 'base-okl4' repository. +You can read the code online via our subversion repository: + +[http://genode.svn.sourceforge.net/viewvc/genode/trunk/ - Browse the Genode subversion repository...] + + +Build-system support +#################### + +The first step is to create a simple hello-world program that can be executed +directly on the OKL4 kernel as roottask-replacement. This program does not rely +on any kernel features but uses port I/O to output some characters to the +serial interface. We need to understand the following things: + +* We need a program that outputs some characters to the serial interface. + This program can be developed on a known kernel platform. Once we have a + working hello program, we only need to port it to the new kernel platform + but can assume that the test program itself is correct. + +* How must the OKL4 rootask be linked in order to be executed by the kernel? + +* How does the OKL4 boot procedure work? OKL4 relies on a tool called elfweaver, + which creates a bootable ELF-image (often called single image) from multiple + binaries, in particular the kernel and roottask. We need to create a + minimalist elfweaver configuration file that just starts the kernel and our + hello example. + +The result of this first step can be found in 'src/test/okl4_01_hello_raw': + +:'crt0': is the assembly startup code taken from the L4/Fiasco version of + Genode. This code defines the initial stack, contains the entry point of + the hello program, which calls a C function called '_main'. + +:'hello.cc': is the implementation of the '_main' function, which outputs + some characters directly via the serial interface of a PC. It does not + contain any kernel-specific code nor it depends on any include files. + +:'genode.ld': is the linker script that we already use for Genode programs + on other base platforms. + +:'weaver.xml': is the description file of the single image to be created + by OKL4's elfweaver tool. It is useful to take a close look at this file. The + most important bits are the filename of the kernel specified in the + '' tag and the filename of the hello program specified in the + '' tag. + +:'Makefile': contains the steps needed to compile the hello program and + invoke elfweaver to create the bootable single image. + +To boot the single image, you can use your favorite boot loader such as +Grub. The single-image file must be specified as kernel. When booted, the +program should print a message over the serial line. + +The next step is the proper integration of the hello example into the +Genode build system. For this, we create a new source-code repository called +'base-okl4' with the following structure: +! base-okl4/lib/mk/x86/startup.mk +! base-okl4/mk/spec-okl4.mk +! base-okl4/mk/spec-okl4_x86.mk +! base-okl4/src/test/okl4_02_hello/target.mk +! base-okl4/src/test/okl4_02_hello/hello.cc +! base-okl4/src/platform/x86/_main.cc +! base-okl4/src/platform/x86/crt0.s +! base-okl4/src/platform/genode.ld +! base-okl4/etc/specs.conf + +The OKL4-specific build-system support is contained in the files 'specs.conf', +'spec-okl4.mk', and 'spec-okl_x86.mk'. The 'specs.conf' file steers the build +process once the 'base-okl4' repository is specified in the 'REPOSITORIES' +declaration in the 'etc/build.conf' file in the build directory. +The 'spec-okl4_x86.mk' file describes the build specifics via the mechanism +described in Genode's getting-started documentation: +! SPECS = genode okl4_x86 + +Driven by the content of this 'SPECS' declaration, the build system first +includes the 'spec' files for 'spec-genode.mk' (found in the 'base/' repository) +and 'spec-okl4_x86.mk' (found in the 'base-okl4/' repository). +The latter file contains all build options for OKL4 on the x86 architecture, +extends the 'SPECS' declaration by the platform specifics 'x86_32' and 'okl4' +(which both apply for 'okl4_x86'), and aggregates the corresponding 'spec' +files: +! SPECS += x86_32 okl4 +! +! LD_SCRIPT ?= $(call select_from_repositories,src/platform/genode.ld) +! CXX_LINK_OPT += -Wl,-T$(LD_SCRIPT) -Wl,-Ttext=0x01000000 +! +! include $(call select_from_repositories,mk/spec-x86_32.mk) +! include $(call select_from_repositories,mk/spec-okl4.mk) + +The 'spec' file for 'x86_32' is contained in the 'base/' +repository. The one for 'okl4' is provided by 'base-okl4/'. It contains +all build options that are independent from the hardware platform, OKL4 +is deployed on: +! -include $(call select_from_repositories,etc/okl4.conf) +! -include $(BUILD_BASE_DIR)/etc/okl4.conf +! +! INC_DIR += $(OKL4_DIR)/build/iguana/include +! INC_DIR += $(REP_DIR)/include +! +! PRG_LIBS += startup +! +! CC_OPT_NOSTDINC += -nostdinc +! CXX_LINK_OPT += -static -nostdlib -Wl,-nostdlib +! EXT_OBJECTS += $(shell $(CUSTOM_CXX_LIB) -print-file-name=libsupc++.a) \ +! $(shell $(CUSTOM_CXX_LIB) -print-file-name=libgcc_eh.a) \ +! $(shell $(CUSTOM_CXX_LIB) -print-libgcc-file-name) +! +! EXT_OBJECTS += $(OKL4_DIR)/build/iguana/lib/libl4.a + +The most interesting point is that this file reads an OKL4-specific config +file from the 'etc/' subdirectory of the build directory. From this file, +it obtains the location of the OKL4 distribution via the 'OKL4_DIR' +declaration. The 'spec-okl4.mk' file above adds the 'build/iguana/include' +path to the default include search locations. We need this path for including +the headers from the 'l4/' subdirectory. Unfortunately, 'build/iguana/include/' +contains a lot of further includes, which we don't want to use. In contrary, +these includes pollute our include-search space. This is particularly problematic +for headers such as 'stdio.h', which will inevitably collide with Genode's own +libC headers. Hence we need to find a way, to isolate the 'l4/' headers from +the remaining Iguna headers. One elegant way is to shadow the 'build/iguana/include/l4' +directory in our local Genode build directory. This can be accomplished either +manually by creating a symbolic link from OKL4's 'build/iguana/include/l4' to +an include file within our Genode build directory, or by letting 'make' create +such a link automatically. The corresponding rules for this approach can be +found in the 'spec-okl4.mk' file. + +On Genode, the startup code is encapsulated in a library called 'startup', +which is linked to each program by default. This library essentially consists +of a little snipped of assembly startup code 'crt0.s', which calls a platform- +independent C startup function called '_main' implemented in '_main.cc'. The +library-description file for the startup library is called 'startup.mk' +and has the following content: +! REQUIRES = okl4 x86 +! SRC_S = crt0.s +! SRC_CC = _main.cc +! +! vpath crt0.s $(REP_DIR)/src/platform/x86 +! vpath _main.cc $(REP_DIR)/src/platform/x86 + +We will use a '_main.cc' from another platform as template for the OKL4- +specific startup code but strip it down to an absolute minimum (leaving +out everything except the call the actual 'main' function. Note that +for this simple setup, we need to explicitly reference a symbol of 'crt0.s' +from '_main.cc' to prevent the linker from discarding the otherwise +unreferenced object file (which only contains our entry point). The easiest +way is to reference the '__dso_handle' variable, which is defined in +'crt0.s'. However, this is an intermediate work-around, which we will +remove in the next step. Alternatively, we could rely on the '-u' option +of the linker to prevent the entry symbol ('_start') from being discarded. + +The implementation of the hello program equals the version of +'okl4_01_hello_raw' except that the main function is actually called +'main' rather than '_main'. The corresponding target description file +'target.mk' is straight forward: +! TARGET = hello +! REQUIRES = okl4 +! SRC_CC = hello.cc + + +Creating dummy versions of the 'env' and 'cxx' libraries +######################################################## + +So far, the hello program does rely neither on OKL4-specific nor +Genode-specific code. The goal of the next step is to remove the +differences between the '_main.cc' file in our repository and the +'_main.cc' file of the other base platforms. We will add proper +C++ initialization, the calling of static constructors, and a +proper console implementation. + +The first step is to include the 'cxx' libary to our target. +This is a Genode-specific C++ support library, which contains +functions used as back end of the GCC's 'libsupc++' and 'libgcc_eh'. +To include the 'cxx' library for building our hello program, we +add the following declaration to the 'target.mk' file: + +! LIBS = cxx + +On a rebuild, the build system will try to compile the 'cxx' library, +which, in turn, depends on a number of Genode header files. Most +of these header files are generic and hence contained in the 'base/' +repositories. However, the following header files are specific for +the actual base platform and, therefore, must be provided by ourself: + +:'base/capability.h': This file defines the representation of an object + capability on the actual platform. For now, we can use the following + version, which we will expand later on (at the current stage, the + Capability class is not actually used but we need its definition for + successful compilation. The OKL4-specific 'capability.h' file must + be placed in 'include/base/' of the 'base-okl4/' repository. + ! #ifndef _INCLUDE__BASE__CAPABILITY_H_ + ! #define _INCLUDE__BASE__CAPABILITY_H_ + ! + ! namespace Genode { + ! class Capability { + ! public: bool valid() const { return false; } + ! } + ! typedef int Connection_state; + ! } + ! + ! #endif /* _INCLUDE__BASE__CAPABILITY_H_ */ + +:'base/native_types.h': This file defines platform representations of + thread IDs, locks etc. Please take a look at the 'native_types.h' file + of another platform to get an overview on these types. For now, the + following simple version suffices: + ! #ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ + ! #define _INCLUDE__BASE__NATIVE_TYPES_H_ + ! + ! namespace Genode { + ! typedef volatile int Native_lock; + ! typedef int Native_thread_id; + ! typedef int Native_thread; + ! } + ! + ! #endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ + + In fact, at this point, the types are just dummies, which we will + replace later when porting further parts of the framework. + +:'base/ipc.h': This is a platform-specific wrapper for Genode's + IPC API. Usually, this file just includes 'base/ipc_generic.h'. + Optionally, it can host platform-specific IPC functionality. + ! #ifndef _INCLUDE__BASE__IPC_H_ + ! #define _INCLUDE__BASE__IPC_H_ + ! + ! #include + ! + ! #endif /* _INCLUDE__BASE__IPC_H_ */ + +:'base/ipc_msgbuf.h': This file defines the IPC message-buffer layout. + Naturally, it is highly platform specific. For now, the following dummy + message-buffer layout will do: + ! #ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ + ! #define _INCLUDE__BASE__IPC_MSGBUF_H_ + ! + ! namespace Genode { + ! class Msgbuf_base { }; + ! + ! template + ! class Msgbuf : public Msgbuf_base { }; + ! } + ! + ! #endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ + +Once, we have created these platform-specific header files, the 'cxx' libary +should compile successfully. However, there are a number of unresolved +symbols when linking the hello program. The 'cxx' library uses Genode's +'env()->heap()' as back end for its local malloc implementation. But so far, +we do not have ported Genode's 'env' library. Furthermore, there are +unresolved references to 'Genode::printf' as provided by Genodes console +implementation and some functions of the IPC framework. + +Let us first resolve the 'Genode::printf' references by creating an +OKL4-specific version of Genode's console library. For this, we create +a new back end in 'src/base/console/okl4_console.cc' that uses the +serial output mechanism that we employed for our first 'hello_raw' program. +The corresponding library description file 'lib/mk/printf_okl4.mk' looks +as follows: +! SRC_CC = okl4_console.cc +! LIBS = cxx console +! +! vpath %.cc $(REP_DIR)/src/base/console + +Now, we can add 'printf_okl4' to the 'LIBS' declaration of hello's 'target.mk' +file. When recompiling the hello program, the new 'printf_okl4' library will +be built and resolve the 'Genode::printf' symbols. There remain the unresolved +references to 'Genode::env()' and parts of the IPC framework. + +The IPC implementation in 'src/base/ipc/ipc.cc' is not straight forward +and we defer it for now. Hence, we place only the following dummy functions +into the 'ipc.cc' file: + +! #include +! +! using namespace Genode; +! +! Ipc_ostream::Ipc_ostream(Capability dst, Msgbuf_base *snd_msg) : +! Ipc_marshaller(0, 0) { } +! +! void Ipc_istream::_wait() { } +! +! Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) : +! Ipc_unmarshaller(0, 0) { } +! +! Ipc_istream::~Ipc_istream() { } +! +! void Ipc_client::_call() { } +! +! Ipc_client::Ipc_client(Capability &srv, Msgbuf_base *snd_msg, +! Msgbuf_base *rcv_msg) : +! Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) { } +! +! void Ipc_server::_wait() { } +! +! void Ipc_server::_reply() { } +! +! void Ipc_server::_reply_wait() { } +! +! Ipc_server::Ipc_server(Msgbuf_base *snd_msg, +! Msgbuf_base *rcv_msg) : +! Ipc_istream(rcv_msg), Ipc_ostream(Capability(), snd_msg) { } + +The corresponding library-description file 'lib/mk/ipc.mk' looks as +follows: +! SRC_CC = ipc.cc +! vpath ipc.cc $(REP_DIR)/src/base/ipc + +By adding 'ipc' to the 'LIBS' declaration in hello's 'target.mk' file, the +IPC-related linker errors should disappear and only the reference to +'Genode::env()' remains. To resolve this symbol, we add the following dummy +function directly into the code of 'hello.cc'. +! namespace Genode { +! void *env() { return 0; } +! } + +Before we can use the Genode framework, which is written in C++, we need to +make sure that all static constructors are executed in the startup code +('_main'). Therefore, we add the following code to the '_main' function: +! void (**func)(); +! for (func = &_ctors_end; func != &_ctors_start; (*--func)()); + +The referenced symbols '_ctors_start' and '_ctors_end' are created by the +linker script. The corresponding declarations are provided by +'base/include/base/crt0'.. + +Now, its time to replace the direct I/O port access in 'hello.cc' by +Genode's 'printf' implementation. Just add the following line to the main +function of 'hello.cc' and make sure to include '': +! Genode::printf("This is Genode's printf\n"); + +When starting the resulting program, this message should appear via the +serial interface comport 0. + + +Initializing the C++ exception handling +####################################### + +The Genode OS Framework makes use of C++ exceptions. Hence, we need to +make sure to properly initialize the 'libsupc++'. This initialization +comes down to calling the function +! __register_frame(__eh_frame_start__); +which is performed by the function 'init_exception_handling' as provided +by the generic 'cxx' library. Normally, 'init_exception_handling' is called +from '_main'. It is important to know that the initialization code does +use 'malloc', which is mapped to Genode's 'env()->heap()' by the 'cxx' +library. Consequently, we need a working heap to successfully initialize +the exception handling. + +Therefore, we have to replace the dummy 'env()' function in our hello +program with something more useful. The header file 'src/test/minimal_env.h' +provides the heap functionality by using a minimalistic custom environment, +which contains a heap with static pool of memory. With such an environment +in place, we can safely call 'init_exception_handling' from the '_main' +startup code. The test 'okl4_02_hello' is the result of this step. It +first prints some text via Genode's 'printf' implementation and then triggers +a C++ exception. + + +Thread creation +############### + +So far, we have not performed any OKL4 system call. The first system call that +we will explore is the 'L4_ThreadControl' to create a thread. A corresponding +test for this functionality is implemented in the 'test/okl4_03_thread' +example. This example creates a new thread with the thread number 1. Note that +the matching L4 thread ID uses the lowest 14 bits as version number, which is +always set to 1. Hence, the L4 thread ID of thread number 1 will be 0x4001. If +you happen to need to look up this thread in OKL4's kernel debugger, you will +find its thread control block (TCB) via this number. + +Another important thing to note is that rootask's main thread runs initially +at the priority of 255 whereas newly created threads get assigned a default +priority of 100. To make OKL4's preemtive scheduling to work as expected, we +need to assign the same priority to both threads by calling 'L4_Set_Priority'. + + +IPC framework +############# + +Now that we can start multiple threads, we can fill Genode's IPC framework with +life. + +However, before we can get started with communication between threads, the +communication partners must have a way to get to know each other. In particular, +a receiver of IPC communication needs a way to make its communication address +known to a sender. OKL4 uses 'L4_ThreadId_t' as communication address. The +thread's ID is assigned to each thread by its creator. The thread itself however, +does not know its own identity when started up. In contrast to other L4 kernels +that provide a way for thread to determine its own identity via a 'L4_Myself' +call, this functionality is not supported on OKL4. Therefore, the creator of +a new thread must communicate the assigned thread ID to the new thread via +a startup protocol. We use OKL4's 'UserDefinedHandle' for this purpose. This +is an entry of the threads UTCB that can be remotely accessed by the creating +thread. Before starting the new thread, the creator writes the assigned thread +ID to the new thread's user-defined handle. In turn, the startup code of the +new thread copies the supplied value from the user-defined handle to a +thread-local entry of the UTCB (a designated 'ThreadWord'). In the following, +the thread can always determine its own global ID by reading this 'ThreadWord' +from its UTCB. We declare the convention about which 'ThreadWord' to use for +this purpose in Genode's 'base/native_types.h' ('UTCB_TCR_THREAD_WORD_MYSELF'). + + +IPC send and wait +================= + +The test program 'okl4_04_ipc_send_wait' sends an IPC messages via Genode's +'Ipc_istream' and 'Ipc_ostream' framework. To make this example functional, +we have to work on the following parts of the 'base-okl4/' repository. + +:'include/base/capability.h': + Genode uses the 'Capability' class to address an IPC communication and a + referenced object. Therefore, we must provide a valid representation of these + information. Because all IPC operations on OKL4 always address threads, we + use 'L4_ThreadId_t' as representation of communication address. There are no + kernel objects representing user-level objects in OKL4 (version 2). So we + need to manage object identities on the user level, unprotected by the + kernel. For now, we simply use a globally unique object ID for this purpose. + +:'include/base/ipc_msgbuf.h': + The message-buffer representation used for OKL4 does not use any + kernel-specific layout because IPC payload is always transferred through the + communicating thread's UTCBs. Hence, the 'Msgbuf' template does only need to + provide some space for storing messages but no control information. + +:'src/base/ipc/ipc.cc': + For the send-and-wait test, we need to implement the 'Ipc_istream' and + 'Ipc_ostream' class functions: the constructors of 'Ipc_istream' and + 'Ipc_ostream', the '_wait' function, and the '_send' function. It is useful + to take a look at the other platform's implementations for reference. + Because the Genode IPC Framework provides the functionality for marshalling + and unmarshalling of messages, we skip OKL4 'message.h' convenience + abstraction in favor of addressing UTCB message registers 'ipc.h' directly. + + +IPC call +======== + +The test program 'okl4_05_ipc_call' performs IPC communication using Genode's +'Ipc_client' and 'Ipc_server' classes. To make this test work, the corresponding +functions in 'src/base/ipc/ipc.cc' must be implemented, in particular the +functions '_reply_wait' and '_call'. + + +Address-space creation and page-fault handling +############################################## + +There are the following Peculiarities of OKL4 with regard to address-spaces. + +OKL4 does not use IPC to establish memory mappings but an independent +system call 'L4_MapControl' to configure the local or an remote address +space. In the line of other L4 kernels, page faults are handled via +an IPC-based pager protocol. The typical mode of operation of a pager +looks like: +# A page fault occurs, the kernel translates the page fault into a + page-fault message delivered to the pager of the faulting thread. +# The pager receives a page-fault message, decodes the page-fault + address, the fault type (read, write, execute), and the instruction + pointer of the faulter from the page-fault message. +# The pager resolves the page fault by populating the faulter's + address spaces with valid pages via 'L4_MapControl'. +# The pager answers the page-fault message with an empty IPC to + resume the operation of the faulter. +In contrast to L4/Fiasco and L4ka::Pistachio, which incorporate the +memory mapping into the reply message, this procedure involves +an additional system call. However, it is more flexible and allows +the construction of a fully populated address space without employing +an IPC-based protocol. Furthermore, the permissions for establishing +memory mappings are well separated from IPC-communication rights. + +In contrast to the L4/Fiasco and L4ka::Pistachio kernels, which take +a virtual address of the mapper as argument, the OKL4 map operation +always refers to a physical page. This enables the configuration of a +remote address space without having all the used pages locally mapped +as well. For specifying a local virtual address for a mapping, we +can use the 'L4_ReadFpage' function to look up a physical-memory +descriptor for a given virtual address. + +The test 'okl4_06_pager' creates an address space to be one-to-one +mapped with roottask. In the new address space, a thread is created. +For the new thread, we use the roottask thread as pager. Once started, +the new that raises a number page faults: +# Reading the first instruction of the entry point +# Accessing the first stack element +# Reading data +# Writing data +The pager receives the corresponding page-fault messages, prints +the decoded information, and resolves the page faults accordingly. + + +Determining the memory configuration and boot modules +##################################################### + +OKL4 provides its boot information to roottask via a boot-info structure, which +is located at the address provided in roottask's UTCB message register 1. This +structure is created by OKL4's elfweaver during the creation of the boot image. +It has no fixed layout but it contains a batch of operations such as "add +memory pool" or "create protection domain". In short, it (loosely) resembles +the content of the elfweaver XML config file in binary form. Most of +elfweaver's features will remain unused when running Genode on OKL4. However, +there are some important bits of information we need to know: +* Memory configuraion +* Information on the boot modules +For parsing the boot-info structure, there exists a convenient library located +in the OKL4 source tree at 'libs/bootinfo'. The test program +'okl4_07_boot_info' uses this library to obtain the information we are +interested in. + +Note that we link the library directly to the test program by using the +'EXT_OBJECTS' declaration in the 'target.mk' file. We are not adding this +library to the global 'spec-okl4.mk' file because we need the bootinfo-library +only at a very few places (this test program and core). + +We obtain the memory configuration by assigning a callback function to the +'init_mem' entry of the 'bi_callbacks_t' structure supplied to the parser +library. There are indeed two 'init_mem' function called 'init_mem' and +'init_mem2'. The second instance is called during a second parsing stage. +However, both functions seem to be called with the same values. So we just +disregard the values supplied to 'init_mem2' at this point. + +To include other modules than the 'rootprogram' to the boot image, we use the +help of elfweaver's '' declaration. We create a pseudo protection domain as +a container for several memory sections, each section loaded with the content +of a file. An example declaration for including the files 'init' and 'config' +into the boot image looks like this: +! +! +! +! +The 'direct="true"' attribute has the effect that the memory section will +have equal physical and virtual addresses. + +When observing the output of 'okl4_07_boot_info', the relevant information +are the 'new_ms' (new memory section) lines with owner != 0 (another PD +than roottask) and virtpool != 1. These memory sections correspond to +the files. However, the association of the memory sections with their file +names is still missing at this point. To resolve this problem, we also observe +the 'export_object' calls. For each memory section, 'export_object' gets +called with the type parameter set to 'BI_EXPORT_MEMSECTION_CAP' and the key +parameter set to the name of the file. Note that the file name is converted to +upper case. For associating memory sections with file names, we assume that +the order of 'new_ms' calls corresponds to the order of matching +'export_object' calls. + + +Interrupt handling and time source +################################## + +In contrast to most of the classical L4 kernels, OKL4 provides no means +for accessing wall-clock time from the user land. Internally, OKL4 uses +a scheduling timer to perform preemptive scheduling but it does not expose +a time source to the user land via IPC timeouts. Hence, we need an alternative +way to obtain a user-level time source. We follow the same path as Iguana +by driving the programmable interval timer (PIT) directly from a +user-level service. Because OKL4 uses the more modern APIC timer, which is +completely independent of the PIT, both the kernel and the user land +can use entirely different timer devices as their respective time source. + +The PIT is connected to the interrupt line 0 of the programmable interrupt +controller (PIC). The test program 'okl4_08_timer_pit' switches the PIT +into one-shot mode and waits for timer interrupts. Each time a timer +interrupt occurs, the next one-shot is scheduled. The program tests two +important things: How does the interrupt handling work on OKL4 and +how to provide a user-level time source? + +The following things are worth mentioning with regard to IRQ handling: + +* By default, no one (roottask included) has the right to handle interrupts. + We have to explicitly grant ourself the right to handle a particular + interrupt by calling 'L4_AllowInterruptControl'. +* When calling 'L4_RegisterInterrupt', the kernel expects a real global + thread ID, not the magic ID returned by 'L4_Myself()'. +* Interrupts are delivered in an asynchronous fashion by using OKL4's + notification mechanism. To block for incoming asynchronous messages, + the corresponding notification bit must be unmasked and notifications + must be accepted. +* The interrupt-handler loop invokes two system calls per interrupt, + 'L4_ReplyWait' for blocking for the next interrupt and 'L4_AcknowledgeInterrupt' + for interrupt acknowledgement. Both syscalls could be consolidated into a + call of 'L4_AcknowledgeWaitInterrupt'. + + +Porting core +############ + +Now that we have discovered the most functional prerequisites for running +Genode on OKL4, we can start porting Genode's core. I suggest to take +another platform's core version as a template. For OKL4, the 'base-pistachio' +version becomes handy. First, make a copy of 'src/core' to the 'base-okl4/' +repository. Then we revisit all individual files and remove all +platform-specific code with the goal to create a skeleton of core that +compiles successfully. Thereby, we can already apply some simple type +substitutions, for example by using the types declared in 'native_types.h' +we can avoid using platform-specific types such as 'L4_ThreadId_t'. + +By trying to compile core, we will see that there are still a few framework +libraries missing, namely 'pager', 'lock', and 'raw_signal'. For resolving the +dependency on the _lock library_, we can use a simple spinlock implementation +as an intermediate step. The implementation at 'src/base/lock/lock.cc' looks +like this: +!#include +!#include +! +!using namespace Genode; +! +!Cancelable_lock::Cancelable_lock(Cancelable_lock::State initial) +!: _native_lock(UNLOCKED) +!{ +! if (initial == LOCKED) +! lock(); +!} +! +!void Cancelable_lock::lock() +!{ +! while (!cmpxchg(&_native_lock, UNLOCKED, LOCKED)); +!} +! +!void Cancelable_lock::unlock() +!{ +! _native_lock = UNLOCKED; +!} +Note that this implementation does not fully implement the 'Cancelable_lock' +semantics but it is useful to get things started. The corresponding 'lib/mk/lock.mk' +can be based on another platform's variant: +!SRC_CC = lock.cc +!vpath lock.cc $(REP_DIR)/src/base/lock +The OKL4-specific _signal library_ can be taken almost unmodified from +'base-pistachio/'. The _pager library_ is a bit more complicated because +it depends on 'ipc_pager.h' and the corresponding part of the ipc library, +which we have not yet implemented yet. However, based on the knowledge +gained from the 'okl4_06_pager' test, the adaption of another platform's +implementation of 'src/base/ipc/pager.cc' becomes straight-forward. For now, +it actually suffices to leave the functions in 'pager.cc' blank. + +Once, we get the skeleton of core linked, we can work on the OKL4-specific +code, starting with core's platform initialization in 'platform.cc'. +Configuring core's memory allocators: + +:'region_alloc': This is the allocator containing the virtual address + regions that are usable within core. The boot-info parser reports these + regions via the callbacks 'init_mem' and 'add_virt_mem'. +:'ram_alloc': This is the allocator containing the available physical + memory pages. It must be initialized with the physical-memory ranges + provided via the 'init_mem' and 'add_phys_mem' callbacks. +:'core_mem_alloc': This is an allocator for available virtual address + ranges within core. In contrast to 'region_alloc' and 'ram_alloc', which + both are operating at page-granularity, 'core_mem_alloc' can be used to + allocate arbitrarily-sized memory objects. The implementation uses + 'region_alloc' and 'ram_alloc' as back ends. The core-local mapping + of physical memory pages to core's virtual address space is done in a + similar way as practiced in the 'okl4_06_pager' test program. + +For implementing the allocators, special care must be taken to make their +interfaces thread safe as they may be used concurrently by different core +threads. With the memory configuration in place, core will pass the first +initialization steps and tries to initialize 'Core_env', which is a +core-specific variant of the Genode environment. A part of 'Core_env' is a +server-activation, which is indeed a thread. Upon the creation of this thread, +the main thread of core will stop executing until the new thread's startup +protocol is finished. So we have to implement core's thread-creating facility, +which is 'platform_thread.cc'. + +After core successfully creates its secondary threads (called 'activation' and +'pager'), and finishes the initialization of 'Core_env()', it starts executing +the 'main' function, which uses plain Genode APIs such as the 'env()->heap()'. +The heap however relies on a working 'env()->rm_session()' and +'env()->ram_session()'. To make 'env()->rm_session()' functional, we need to +provide a working implementation of the 'Core_rm_session::attach()' function, +which maps the content of a dataspace to core's local address space. Once, +core starts using its 'Env', it will try to use 'env()->rm_session()' to attach +dataspaces into its local address space. Therefore, we need an implementation +of a core version of the 'Rm_session' interface, which we call +'Core_rm_session'. This implementation uses the OKL4 kernel API to map the +physical pages of a dataspace into core's local address space. With the +working core environment, core will look for the binary of the init process. +Init is supplied to core as a boot module via the elfweaver mechanism we +just explored with the 'okl4_07_boot_info' test. Within core, all boot modules +are registered to an instance of the 'Rom_fs' class. Hence, we will need to +call OKL4's boot-info parser with the right callback functions supplied and put +the collected information into 'Rom_fs'. It is useful to take the other +platforms as reference. + + +Starting init +############# + +To enable core to successfully load and start the init process, we first need +to build the init binary. For compiling 'init' we have to implement the still +missing functionality of determining the parent capability at the startup code. +The needed function is called 'parent_cap()' and should be implemented in the +'_main' function. For OKL4, the implementation looks exactly like the Pistachio +version. On both kernels, the parent capability is supplied at predefined +locations declared in the linker script. The corresponding symbols are called +'_parent_cap_thread_id' and '_parent_cap_local_name'. + +After successfully having started init, we can proceed with starting further +instances of init as a children of the first instance. This can be achieved by the +following config file: + +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! + +To successfully execute the creation of this nested process tree, we need +a correct implementation of 'unmap' functionality within core. +Furthermore, if starting multiple processes, we will soon run into the problem +of starting too many threads in core. This is caused by the default +implementation of Genode's signal API. +Within core, each 'Rm_session_component' within core is a signal transmitter, +used for signalling address-space faults. +With the default implementation, each signal transmitter employs one thread. +Because OKL4's roottask is limited to 8 threads, the number of RM sessions +becomes quite limited. Therefore, we disable signal support on OKL4 for now +by the means of a dummy implementation of the signal interface. Later, we can +create a OKL4-specific signal implementation, which will hopefully be able to +utilize OKL4's asynchronous notification mechanism. + + +Hardware access and the Genode demo scenario +############################################ + +The default demo scenario of Genode requires hardware access performed by the +following components: + +* The timer driver needs access to a hardware timer. On x86, the programmable + interval timer (PIT) is available for this use case. + However, for the first version of Genode on OKL4, we can use a simple dummy + driver that ignores the argument of 'msleep' and just returns. + +* The PS/2 driver and the timer driver rely on interrupts. We already exercised + interrupt handling in 'okl4_08_timer_pit'. So it is relatively straight-forward + to implement the IRQ service in core. (taking the other platforms such as + Pistachio as reference) + +* The VESA driver requires several hardware facilities, in particular access + to the VGA registers via I/O ports, the frame buffer via memory-mapped I/O + and other resources such as the PIC (at least some VESA BIOSes rely on the + PIT to implement proper delays during the PLL initialization). + However, with a working implementation of the I/O-port service and + I/O-memory service in core, these requirements become satisfied. + +If all the hardware-access services within core are in place, we should be able +to start 'vesa_drv', 'ps2_drv', 'nitpicker', 'launchpad'. Furthermore starting +and killing of an additional 'testnit' process via the launchpad should work. +However, we will observe that starting another instance of testnit after +killing it will not work. In order to fully support restartable components, +we have to implement thread destruction, and the cancel-blocking mechanism within core. +The interesting bits about thread destruction are 'Platform_thread::unbind' and +'Platform_pd::_destroy_pd'. For implementing the cancel-blocking mechanism, we +have to revisit core's 'Platform_thread::cancel_blocking', the IPC framework +('src/base/ipc/ipc.cc') and the lock implementation ('src/base/lock/lock.cc'). + +With this work done, we are able to run the full Genode demonstration scenario +including the Scout tutorial browser, user-level device drivers for PS/2 +input and video, and the dynamic creation and destruction of process trees. + + +Outlook +####### + +We consider the result of the porting work as described in this article as the +first working version of Genode on OKL4. Of course, there are several areas +for possible improvements, which we will address in a demand-driven way. +The following list gives some hints: + +* Exploring OKL4's kernel mutex for Genode's lock implementation, + paying special attention to the cancel-blocking semantics +* Increasing the flexibility of the UTCB allocator in core. Right now, the UTCB + area of each PD is equally sized, defined by the 'THREAD_BITS' definition. + In the future, we could support differently sized UTCB areas to tailor the + number of threads per protection domain. +* Checking the privileges of non-core tasks +* Supporting RM faults and nested region-manager sessions +* Replacing the dummy timer implementation with a proper PIT-based + timer +* Virtualizing the PIT in the VESA frame-buffer driver, otherwise + the PIT-based timer service won't be usable because of both + components needing access to the PIT. Fortunately, the VESA BIOS of Qemu + does not access the PIT but we are aware that other BIOSes do. +* Eventually optimize I/O port access. Right now, we perform an RPC call + to core for each I/O port access, which is ok for the other platforms + because I/O ports are rarely used (mostly for the PS/2 driver, but at + a low rate). On OKL4 however, we provide the user-level time source + via the timer driver that accesses the PIT via I/O ports. We could + optimize these accesses by lazily mapping the I/O ports from core to + the timer driver the first time, an RPC call to the I/O service is + performed. + + diff --git a/base-okl4/doc/okl4.txt b/base-okl4/doc/okl4.txt new file mode 100644 index 000000000..c78a07103 --- /dev/null +++ b/base-okl4/doc/okl4.txt @@ -0,0 +1,131 @@ + + ============================== + Genode on the OKL4 microkernel + ============================== + + + Stefan Kalkowski + + +OKl4 is a microkernel developed and distributed by Open Kernel Labs. It is +focused on embedded devices. Genode support the OKL4 kernel version 2.1 +on the x86_32 platform. + +This document provides brief instructions about downloading, building and +booting the OKL4 version of Genode. + + +Prerequisites +############# + +You need Python 2.4 to use the OKL4 build system. On Debian/Ubuntu systems +simply type: + +! apt-get install python2.4 + +Since Ubuntu 10.04, the python2.4 package is no longer part of the official +repositories. However, you can manually add the repository via: + +! add-apt-repository ppa:python24-team/python24 +! apt-get update + +Moreover, you need to download and install the tool-chain used by Genode. Have +a look at this page: + +:[http://genode.org/download/tool-chain]: + Genode tool-chain + + +Downloading and building the OKL4 kernel +######################################## + +To download the OKL4 source code, issue + +! make prepare + +from within the 'base-okl4' directory. The Makefile within this directory will +take care of downloading the kernel's source code and applying the patches +found at 'base-okl4/patches'. + +To create a build directory for Genode running on OKL4, use the 'create_builddir' +tool: + +! /tool/create_builddir okl4_x86 BUILD_DIR= + +Once, you have created the build directory, the OKL4 kernel can be built from +within '' via + +! make kernel + + +Running the Genode demonstration scenario +######################################### + +For a quick test drive of the OKL4 kernel, issue 'make run/demo' from the build +directory. + + +Manually building a boot image +############################## + +This section is not needed when using Genode's run-script mechanism. The manual +steps described below are automatically executed via the OKL4 run environment +as found at 'base-okl4/run/env'. + +To practically use the OKL4 kernel and applications running on top of it, Open +Kernel Labs provide a tool called 'elfweaver', that is used to merge different +application binaries and the kernel itself into one single elf binary that can +be executed by your bootloader, e.g. Grub. + +To configure 'elfweaver' to merge the appropriated elf binaries you have to +provide an XML file. A good starting point is the 'weaver_x86.xml' file that +includes the Genode demo example. Simply copy that file to your Genode build +directory and adapt the 'file' attribute of the 'kernel' tag to the absolute +path of the OKL4 kernel we build previously. + +! cp /base-okl4/tool/weaver_x86.xml weaver.xml + +The corresponding line in your weaver.xml should look like this: + +! + +Before creating the image, we need to supply a Genode config file as well. +For a quick start, you can copy and rename the template provided 'os/config/demo' +to '/bin/config'. Alternatively, you can assign another file to the +'filename' of the 'memsection' declaration for the config file in 'weaver.xml'. +Now, we can use 'elfweaver' to create the image. Go to the 'bin' directory in +the Genode build directory that contains all the binaries and invoke the +script. + +! cd bin +! /tools/pyelf/elfweaver merge --output=weaver.elf ../weaver.xml +! strip weaver.elf + +Note: the given paths to the resulting elf file and the input xml file have to +be relative. + +*Bug alert:* Elfweaver triggers an assertion when too many memsections are +declared in the 'weaver.xml' file and just outputs the following message +! An error occurred: +Apparently, elfweaver has a problem with calculating the size of the boot info +section. As a quick fix, you can increase the value of 'BOOTINFO_GUESS_OPS' in +'/tools/pyelf/weaver/bootinfo.py'. + +The resulting elf image can be loaded by Grub now. + + +Further Information +################### + +:[http://genode.org/documentation/articles/genode-on-okl4]: + Article about the porting work of Genode to OKL4, featuring many technical + insights that are useful to understand the peculiarities of this base platform. + +:okl4_2.1.1-patch.9/README: + OKL4 building guide + +:[http://wiki.ok-labs.com]: + OKL4 developer wiki + +:[http://wiki.ok-labs.com/downloads/release-3.0/elfweaver-ref-manual-3.0.pdf]: + Elfweaver reference manual diff --git a/base-okl4/etc/specs.conf b/base-okl4/etc/specs.conf new file mode 100644 index 000000000..5fa30f2ff --- /dev/null +++ b/base-okl4/etc/specs.conf @@ -0,0 +1 @@ +SPECS = genode okl4_x86 diff --git a/base-okl4/include/base/ipc_msgbuf.h b/base-okl4/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..8ac0d647a --- /dev/null +++ b/base-okl4/include/base/ipc_msgbuf.h @@ -0,0 +1,68 @@ +/* + * \brief OKL4-specific layout of IPC message buffer + * \author Norman Feske + * \date 2009-03-25 + * + * On OKL4, we do not directly use the a kernel-specific message-buffer layout. + * The IPC goes through the UTCBs of the sending and receiving threads. Because + * on Genode, message buffers are decoupled from threads, we need to copy-in + * and copy-out the message payload between the message buffers and the used + * UTCBs. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +namespace Genode { + + /** + * IPC message buffer layout + */ + class Msgbuf_base + { + protected: + + size_t _size; + char _msg_start[]; /* symbol marks start of message */ + + public: + + /* + * Begin of actual message buffer + */ + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; }; + + /** + * Return address of message buffer + */ + inline void *addr() { return &_msg_start[0]; }; + }; + + + /** + * Instance of IPC message buffer with specified buffer size + */ + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-okl4/include/base/ipc_pager.h b/base-okl4/include/base/ipc_pager.h new file mode 100644 index 000000000..23e4dd010 --- /dev/null +++ b/base-okl4/include/base/ipc_pager.h @@ -0,0 +1,180 @@ +/* + * \brief OKL4 pager support for Genode + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +#include +#include +#include + +namespace Okl4 { extern "C" { +#include +#include +#include +} } + +namespace Genode { + + class Mapping + { + private: + + addr_t _phys_addr; + Okl4::L4_Fpage_t _fpage; + Okl4::L4_PhysDesc_t _phys_desc; + + public: + + /** + * Constructor + */ + Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, unsigned l2size = 12, bool rw = true); + + /** + * Construct invalid mapping + */ + Mapping(); + + /** + * Return flexpage describing the virtual destination address + */ + Okl4::L4_Fpage_t fpage() const { return _fpage; } + + /** + * Return physical-memory descriptor describing the source location + */ + Okl4::L4_PhysDesc_t phys_desc() const { return _phys_desc; } + + /** + * Prepare map operation + * + * On OKL4, we do not need to map a page core-locally to be able to + * map it into another address space. Therefore, we can leave this + * function blank. + */ + void prepare_map_operation() { } + }; + + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + private: + + Okl4::L4_MsgTag_t _faulter_tag; /* fault flags */ + Okl4::L4_ThreadId_t _last; /* faulted thread */ + Okl4::L4_Word_t _fault_addr; /* page-fault address */ + Okl4::L4_Word_t _fault_ip; /* instruction pointer of faulter */ + Mapping _reply_mapping; /* page-fault answer */ + + protected: + + /** + * Wait for short-message (register) IPC -- fault + */ + void _wait(); + + /** + * Send short flex page and + * wait for next short-message (register) IPC -- fault + */ + void _reply_and_wait(); + + public: + + /** + * Constructor + */ + Ipc_pager(); + + /** + * Wait for a new fault received as short message IPC + */ + void wait_for_fault(); + + /** + * Reply current fault and wait for a new one + * + * Send short flex page and wait for next short-message (register) + * IPC -- pagefault + */ + void reply_and_wait_for_fault(); + + /** + * Request instruction pointer of current fault + */ + addr_t fault_ip() { return _fault_ip; } + + /** + * Request fault address of current fault + */ + addr_t fault_addr() { return _fault_addr & ~3; } + + /** + * Set parameters for next reply + */ + void set_reply_mapping(Mapping m) { _reply_mapping = m; } + + /** + * Set destination for next reply + */ + void set_reply_dst(Native_capability pager_object) { + _last.raw = pager_object.local_name(); } + + /** + * Answer call without sending a flex-page mapping + * + * This function is used to acknowledge local calls from one of + * core's region-manager sessions. + */ + void acknowledge_wakeup(); + + /** + * Return thread ID of last faulter + */ + Native_thread_id last() const { return _last; } + + /** + * Return badge for faulting thread + * + * Because OKL4 has no server-defined badges for fault messages, we + * interpret the sender ID as badge. + */ + unsigned long badge() const { return _last.raw; } + + /** + * Return true if last fault was a write fault + */ + bool is_write_fault() const { return L4_Label(_faulter_tag) & 2; } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + /* + * A page-fault message has one of the op bits (lower 3 bits of the + * label) set. If those bits are zero, we got an exception message. + * If the label is zero, we got an IPC wakeup message from within + * core. + */ + return L4_Label(_faulter_tag) && (L4_Label(_faulter_tag) & 0xf) == 0; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-okl4/include/base/native_types.h b/base-okl4/include/base/native_types.h new file mode 100644 index 000000000..627d98e9f --- /dev/null +++ b/base-okl4/include/base/native_types.h @@ -0,0 +1,127 @@ +/* + * \brief Native types on OKL4 + * \author Norman Feske + * \date 2008-07-26 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +namespace Okl4 { extern "C" { +#include +} } + +namespace Genode { + + class Platform_thread; + + /** + * Index of the UTCB's thread word used for storing the own global + * thread ID + */ + enum { UTCB_TCR_THREAD_WORD_MYSELF = 0 }; + + namespace Thread_id_bits { + + /* + * L4 thread ID has 18 bits for thread number and 14 bits for + * version info. + */ + enum { PD = 8, THREAD = 5 }; + } + + typedef Okl4::L4_ThreadId_t Native_thread_id; + + inline bool operator == (Native_thread_id t1, Native_thread_id t2) { + return t1.raw == t2.raw; } + + inline bool operator != (Native_thread_id t1, Native_thread_id t2) { + return t1.raw != t2.raw; } + + struct Native_thread + { + Native_thread_id l4id; + + /** + * Only used in core + * + * For 'Thread' objects created within core, 'pt' points to + * the physical thread object, which is going to be destroyed + * on destruction of the 'Thread'. + */ + Platform_thread *pt; + }; + + inline unsigned long convert_native_thread_id_to_badge(Native_thread_id tid) + { + /* + * OKL4 has no server-defined badges for page-fault messages. + * Therefore, we have to interpret the sender ID as badge. + */ + return tid.raw; + } + + /** + * Empty UTCB type expected by the thread library, unused on OKL4 + * + * On this kernel, UTCBs are not placed within the the context area. Each + * thread can request its own UTCB pointer using the kernel interface. + */ + typedef struct { } Native_utcb; + + /* + * On OKL4, the local_name member of a capability is global to the whole + * system. Therefore, capabilities are to be created at a central place + * that prevents id clashes. + */ + class Native_capability + { + protected: + + Okl4::L4_ThreadId_t _tid; + long _local_name; + + public: + + /** + * Default constructor + */ + Native_capability() : _local_name(0) { + _tid = Okl4::L4_nilthread; } + + long local_name() const { return _local_name; } + Okl4::L4_ThreadId_t dst() const { return _tid; } + + bool valid() const { return !Okl4::L4_IsNilThread(_tid); } + + + /******************************************************** + ** Functions to be used by the Pistachio backend only ** + ********************************************************/ + + /** + * Constructor + * + * Creates a L4 capability manually. This must not be called from + * generic code. + */ + Native_capability(Okl4::L4_ThreadId_t tid, long local_name) + : _tid(tid), _local_name(local_name) { } + + /** + * Access raw capability data + */ + Okl4::L4_ThreadId_t tid() const { return _tid; }; + }; + + typedef Okl4::L4_ThreadId_t Native_connection_state; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-okl4/include/base/thread_state.h b/base-okl4/include/base/thread_state.h new file mode 100644 index 000000000..8490de99a --- /dev/null +++ b/base-okl4/include/base/thread_state.h @@ -0,0 +1,33 @@ +/* + * \brief Thread state + * \author Stefan Kalkowski + * \date 2007-07-30 + * + * This file contains the OKL4 specific part of the thread state. + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__THREAD_STATE_H_ +#define _INCLUDE__BASE__THREAD_STATE_H_ + +namespace Okl4 { extern "C" { +#include +} } + +#include + +namespace Genode { + + struct Thread_state : public Cpu_state + { + Okl4::L4_ThreadId_t tid; /* OKL4 specific thread id */ + }; +} + +#endif /* _INCLUDE__BASE__THREAD_STATE_H_ */ diff --git a/base-okl4/include/okl4_pd_session/client.h b/base-okl4/include/okl4_pd_session/client.h new file mode 100644 index 000000000..29dfe1146 --- /dev/null +++ b/base-okl4/include/okl4_pd_session/client.h @@ -0,0 +1,41 @@ +/* + * \brief Client-side OKL4 specific pd session interface + * \author Stefan Kalkowski + * \date 2009-06-03 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__OKL4_PD_SESSION__CLIENT_H_ +#define _INCLUDE__OKL4_PD_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Okl4_pd_session_client : Rpc_client + { + explicit Okl4_pd_session_client(Pd_session_capability cap) + : Rpc_client(static_cap_cast(cap)) { } + + int bind_thread(Thread_capability thread) { + return call(thread); } + + int assign_parent(Parent_capability parent) { + return call(parent); } + + Okl4::L4_SpaceId_t space_id() { + return call(); } + + void space_pager(Thread_capability thread) { + call(thread); } + }; +} + +#endif /* _INCLUDE__OKL4_PD_SESSION__CLIENT_H_ */ diff --git a/base-okl4/include/okl4_pd_session/connection.h b/base-okl4/include/okl4_pd_session/connection.h new file mode 100644 index 000000000..c567a8e80 --- /dev/null +++ b/base-okl4/include/okl4_pd_session/connection.h @@ -0,0 +1,41 @@ +/* + * \brief Connection to OKL4-specific PD service + * \author Stefan Kalkowski + * \date 2009-06-22 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__OKL4_PD_SESSION__CONNECTION_H_ +#define _INCLUDE__OKL4_PD_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Pd_connection : Connection, Okl4_pd_session_client + { + /** + * Constructor + * + * \param args additional session arguments + */ + Pd_connection(const char *args = 0) + : + Connection( + session("ram_quota=4K%s%s", + args ? ", " : "", + args ? args : "")), + + Okl4_pd_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__OKL4_PD_SESSION__CONNECTION_H_ */ diff --git a/base-okl4/include/okl4_pd_session/okl4_pd_session.h b/base-okl4/include/okl4_pd_session/okl4_pd_session.h new file mode 100644 index 000000000..7d4660617 --- /dev/null +++ b/base-okl4/include/okl4_pd_session/okl4_pd_session.h @@ -0,0 +1,62 @@ +/* + * \brief OKL4 specific extension of the PD session interface + * \author Stefan Kalkowski + * \date 2009-06-03 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__OKL4_PD_SESSION__OKL4_PD_SESSION_H_ +#define _INCLUDE__OKL4_PD_SESSION__OKL4_PD_SESSION_H_ + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +} } + +/* Genode includes */ +#include + +namespace Genode { + + struct Okl4_pd_session : Pd_session + { + virtual ~Okl4_pd_session() { } + + /** + * Get the OKL4 specific space ID of the PD + * + * Should be used only by OKLinux, as it will be removed + * in the future! + * + * \return the space ID + */ + virtual Okl4::L4_SpaceId_t space_id() = 0; + + /** + * Set the thread/space allowed to page the PD + * + * (have a look at SpaceControl in OKL4) + * Should be used only by OKLinux, as it will be removed + * in the future! + */ + virtual void space_pager(Thread_capability) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_space_id, Okl4::L4_SpaceId_t, space_id); + GENODE_RPC(Rpc_space_pager, void, space_pager, Thread_capability); + + GENODE_RPC_INTERFACE_INHERIT(Pd_session, Rpc_space_id, Rpc_space_pager); + }; +} + +#endif /* _INCLUDE__OKL4_PD_SESSION__OKL4_PD_SESSION_H_ */ diff --git a/base-okl4/lib/mk/bootinfo.mk b/base-okl4/lib/mk/bootinfo.mk new file mode 100644 index 000000000..13cebd141 --- /dev/null +++ b/base-okl4/lib/mk/bootinfo.mk @@ -0,0 +1,5 @@ +SRC_C = bootinfo.c +INC_DIR += $(REP_DIR)/src/base/bootinfo +CC_WARN = -Wall -Wno-attributes + +vpath bootinfo.c $(OKL4_DIR)/libs/bootinfo/src diff --git a/base-okl4/lib/mk/core_printf.mk b/base-okl4/lib/mk/core_printf.mk new file mode 100644 index 000000000..663cf64b9 --- /dev/null +++ b/base-okl4/lib/mk/core_printf.mk @@ -0,0 +1,5 @@ +SRC_CC = core_printf.cc +LIBS = cxx console +INC_DIR += $(REP_DIR)/src/base/console + +vpath core_printf.cc $(BASE_DIR)/src/base/console diff --git a/base-okl4/lib/mk/ipc.mk b/base-okl4/lib/mk/ipc.mk new file mode 100644 index 000000000..6e6443bb9 --- /dev/null +++ b/base-okl4/lib/mk/ipc.mk @@ -0,0 +1,3 @@ +SRC_CC = ipc.cc pager.cc + +vpath %.cc $(REP_DIR)/src/base/ipc diff --git a/base-okl4/lib/mk/kernel.inc b/base-okl4/lib/mk/kernel.inc new file mode 100644 index 000000000..21495f2e2 --- /dev/null +++ b/base-okl4/lib/mk/kernel.inc @@ -0,0 +1,97 @@ +INC_SYMLINKS += atomic_ops/atomic_ops.h \ + atomic_ops/unsafe_generic.h \ + compat/c.h \ + compat/toolchain/ads/c.h \ + compat/toolchain/flint/c.h \ + compat/toolchain/gnu/c.h \ + compat/toolchain/rvct/c.h \ + compat/toolchain/rvct_gnu/c.h \ + kdb/cmd.h \ + kdb/console.h \ + kdb/init.h \ + kdb/input.h \ + kdb/kdb.h \ + kdb/macro_set.h \ + kdb/print.h \ + kdb/tid_format.h \ + kdb/tracepoints.h \ + kernel/bitmap.h \ + kernel/bitmask.h \ + kernel/cache.h \ + kernel/caps.h \ + kernel/clist.h \ + kernel/config.h \ + kernel/debug.h \ + kernel/endpoint.h \ + kernel/fpage.h \ + kernel/generic/lib.h \ + kernel/idtable.h \ + kernel/init.h \ + kernel/interrupt.h \ + kernel/ipc.h \ + kernel/kdb/console.h \ + kernel/kdb/macro_set.h \ + kernel/kdb/names.h \ + kernel/kdb/tracepoints.h \ + kernel/kmemory.h \ + kernel/l4.h \ + kernel/macros.h \ + kernel/map.h \ + kernel/memdesc.h \ + kernel/mp.h \ + kernel/mutex.h \ + kernel/mutexid.h \ + kernel/platform.h \ + kernel/preempt.h \ + kernel/profile.h \ + kernel/queueing.h \ + kernel/read_write_lock.h \ + kernel/resources.h \ + kernel/rootserver.h \ + kernel/schedule.h \ + kernel/smallalloc.h \ + kernel/space.h \ + kernel/spaceid.h \ + kernel/sync.h \ + kernel/syncpoint.h \ + kernel/syscalls.h \ + kernel/tcb.h \ + kernel/tcb_syscall_data.h \ + kernel/thread.h \ + kernel/threadstate.h \ + kernel/tracebuffer.h \ + kernel/traceids.h \ + kernel/types.h \ + kernel/utcb.h \ + l4/config.h \ + l4/macros.h \ + l4/types.h + +SYMLINK_TARGETS = $(addprefix $(OKL4_BUILD_DIR)/include/,$(INC_SYMLINKS)) +SYMLINK_DIRS = $(sort $(dir $(SYMLINK_TARGETS))) + +all: $(SYMLINK_TARGETS) + +$(SYMLINK_TARGETS): $(filter-out $(wildcard $(SYMLINK_DIRS)), $(SYMLINK_DIRS)) + +$(SYMLINK_DIRS): + $(VERBOSE)mkdir -p $@ + +$(OKL4_BUILD_DIR)/include/kernel/kdb/%.h: $(OKL4_SRC_DIR)/pistachio/kdb/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/kdb/%.h: $(OKL4_SRC_DIR)/pistachio/kdb/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/kernel/%.h: $(OKL4_SRC_DIR)/pistachio/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/compat/%.h: $(OKL4_SRC_DIR)/libs/compat/include/compat/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/atomic_ops/%.h: $(OKL4_SRC_DIR)/libs/atomic_ops/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/l4/%.h: $(OKL4_SRC_DIR)/libs/l4/include/%.h + $(VERBOSE)ln -s $< $@ + diff --git a/base-okl4/lib/mk/lock.mk b/base-okl4/lib/mk/lock.mk new file mode 100644 index 000000000..a79c1d9a1 --- /dev/null +++ b/base-okl4/lib/mk/lock.mk @@ -0,0 +1,4 @@ +SRC_CC = lock.cc +INC_DIR += $(REP_DIR)/src/base/lock + +vpath lock.cc $(BASE_DIR)/src/base/lock diff --git a/base-okl4/lib/mk/pager.mk b/base-okl4/lib/mk/pager.mk new file mode 100644 index 000000000..c22e66d22 --- /dev/null +++ b/base-okl4/lib/mk/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-okl4/lib/mk/platform.inc b/base-okl4/lib/mk/platform.inc new file mode 100644 index 000000000..e33e29caa --- /dev/null +++ b/base-okl4/lib/mk/platform.inc @@ -0,0 +1,57 @@ +# +# Create prerequisites for building Genode for OKL4 +# +# Prior building Genode programs for OKL4, the kernel bindings needed are +# symlinked to the build directory. +# + +# +# Execute the rules in this file only at the second build stage when we know +# about the complete build settings, e.g., the 'CROSS_DEV_PREFIX'. +# +ifeq ($(called_from_lib_mk),yes) + +# +# Make OKL4 kernel API headers available to the Genode build system +# +# We have to create a symbolic link of OKL's 'include/l4' directory into our +# local build directory. If we just added the original 'iguana/include' +# directory to the include-search locations, we would pollute the include +# search space with all Iguana include files, not just the OKL4 API includes. +# + +OKL4_L4_INCLUDES = arch.h cache.h caps.h config.h interrupt.h ipc.h kdebug.h \ + macros.h map.h memregion.h message.h misc.h mutex.h \ + pagefault.h procdesc.h profile.h schedule.h security.h \ + space.h thread.h time.h types.h utcb.h + +OKL4_INCLUDE_SYMLINKS += $(addprefix $(BUILD_BASE_DIR)/include/l4/,$(OKL4_L4_INCLUDES)) +OKL4_INCLUDE_SYMLINKS += $(addprefix $(BUILD_BASE_DIR)/include/,compat bootinfo) + +OKL4_INCLUDE_DIRS = $(sort $(dir $(OKL4_INCLUDE_SYMLINKS))) + +# make sure to create the 'include/l4' directory before the symbolic links +all: $(OKL4_INCLUDE_SYMLINKS) + +$(OKL4_INCLUDE_SYMLINKS): $(filter-out $(wildcard $(OKL4_INCLUDE_DIRS)), $(OKL4_INCLUDE_DIRS)) + +$(OKL4_INCLUDE_DIRS): + $(VERBOSE)mkdir -p $@ + +$(OKL4_DIR): + $(VERBOSE)$(ECHO) "--> Please, execute 'make prepare' in $(REP_DIR)" + $(VERBOSE)$(ECHO) "--> before compiling Genode apps for OKL4." + $(VERBOSE)exit 1 + +$(OKL4_DIR)/%: $(filter-out $(wildcard $(OKL4_DIR)), $(OKL4_DIR)) + +$(BUILD_BASE_DIR)/include/l4/%.h: $(OKL4_DIR)/libs/l4/include/%.h + $(VERBOSE)ln -sf $< $@ + +$(BUILD_BASE_DIR)/include/compat: $(OKL4_DIR)/libs/compat/include/compat + $(VERBOSE)ln -sf $< $@ + +$(BUILD_BASE_DIR)/include/bootinfo: $(OKL4_DIR)/libs/bootinfo/include + $(VERBOSE)ln -sf $< $@ + +endif diff --git a/base-okl4/lib/mk/thread.mk b/base-okl4/lib/mk/thread.mk new file mode 100644 index 000000000..08888f1bc --- /dev/null +++ b/base-okl4/lib/mk/thread.mk @@ -0,0 +1,5 @@ +SRC_CC = thread.cc thread_start.cc thread_bootstrap.cc + +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath thread_start.cc $(BASE_DIR)/src/base/thread +vpath thread_bootstrap.cc $(REP_DIR)/src/base/thread diff --git a/base-okl4/lib/mk/x86/kernel.mk b/base-okl4/lib/mk/x86/kernel.mk new file mode 100644 index 000000000..88f5f35a3 --- /dev/null +++ b/base-okl4/lib/mk/x86/kernel.mk @@ -0,0 +1,112 @@ +OKL4_BUILD_DIR = $(BUILD_BASE_DIR)/kernel +OKL4_SRC_DIR = $(REP_DIR)/contrib/okl4 +ARCH_DIR = $(OKL4_SRC_DIR)/arch/ia32 +PLAT_DIR = $(OKL4_SRC_DIR)/platform/pc99 +INC_SYMLINKS = arch/apic.h \ + arch/arch_idt.h \ + arch/asm.h \ + arch/bootdesc.h \ + arch/config.h \ + arch/context.h \ + arch/cpu.h \ + arch/fpu.h \ + arch/hwirq.h \ + arch/hwspace.h \ + arch/idt.h \ + arch/init.h \ + arch/intctrl.h \ + arch/interrupt.h \ + arch/ioport.h \ + arch/ldt.h \ + arch/memory.h \ + arch/mmu.h \ + arch/offsets.h \ + arch/pgent.h \ + arch/platform.h \ + arch/platsupport.h \ + arch/profile_asm.h \ + arch/ptab.h \ + arch/resource_functions.h \ + arch/schedule.h \ + arch/segdesc.h \ + arch/smp.h \ + arch/space.h \ + arch/special.h \ + arch/syscalls.h \ + arch/sysdesc.h \ + arch/tcb.h \ + arch/timer.h \ + arch/trapgate.h \ + arch/traphandler.h \ + arch/traps.h \ + arch/tss.h \ + arch/user_access.h \ + atomic_ops/arch/atomic_ops.h \ + cpu/8259.h \ + cpu/intctrl-pic.h \ + kernel/arch/cache.h \ + kernel/arch/config.h \ + kernel/arch/context.h \ + kernel/arch/cpu.h \ + kernel/arch/debug.h \ + kernel/arch/hwspace.h \ + kernel/arch/ia32.h \ + kernel/arch/intctrl.h \ + kernel/arch/ioport.h \ + kernel/arch/ktcb.h \ + kernel/arch/ldt.h \ + kernel/arch/mmu.h \ + kernel/arch/offsets.h \ + kernel/arch/pgent.h \ + kernel/arch/platform.h \ + kernel/arch/platsupport.h \ + kernel/arch/profile.h \ + kernel/arch/ptab.h \ + kernel/arch/resource_functions.h \ + kernel/arch/resources.h \ + kernel/arch/segdesc.h \ + kernel/arch/space.h \ + kernel/arch/special.h \ + kernel/arch/sync.h \ + kernel/arch/syscalls.h \ + kernel/arch/tcb.h \ + kernel/arch/traceids.h \ + kernel/arch/tss.h \ + kernel/arch/types.h \ + l4/arch/config.h \ + l4/arch/kdebug.h \ + l4/arch/specials.h \ + l4/arch/syscalls.h \ + l4/arch/thread.h \ + l4/arch/types.h \ + l4/arch/vregs.h \ + l4/ipc.h \ + l4/kdebug.h \ + l4/memregion.h \ + l4/message.h \ + l4/security.h \ + l4/thread.h \ + l4/utcb.h \ + plat/nmi.h \ + plat/rtc.h + +include $(REP_DIR)/lib/mk/kernel.inc + +$(OKL4_BUILD_DIR)/include/atomic_ops/arch/%.h: $(ARCH_DIR)/libs/atomic_ops/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/l4/arch/%.h: $(ARCH_DIR)/libs/l4/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/kernel/arch/%.h: $(ARCH_DIR)/pistachio/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/arch/%.h: $(ARCH_DIR)/pistachio/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/cpu/%.h: $(ARCH_DIR)/pistachio/cpu/idt/include/%.h + $(VERBOSE)ln -s $< $@ + +$(OKL4_BUILD_DIR)/include/plat/%.h: $(PLAT_DIR)/pistachio/include/%.h + $(VERBOSE)ln -s $< $@ + diff --git a/base-okl4/lib/mk/x86/platform.mk b/base-okl4/lib/mk/x86/platform.mk new file mode 100644 index 000000000..577b721b4 --- /dev/null +++ b/base-okl4/lib/mk/x86/platform.mk @@ -0,0 +1,16 @@ +# +# Create prerequisites for building Genode for OKL4 +# +# Prior building Genode programs for OKL4, the kernel bindings needed are +# symlinked to the build directory. +# + +# +# Create mirror for architecture-specific L4 header files +# +OKL4_INCLUDE_SYMLINKS = $(BUILD_BASE_DIR)/include/l4/arch + +include $(REP_DIR)/lib/mk/platform.inc + +$(BUILD_BASE_DIR)/include/l4/arch: $(OKL4_DIR)/arch/ia32/libs/l4/include + $(VERBOSE)ln -sf $< $@ diff --git a/base-okl4/lib/mk/x86/startup.mk b/base-okl4/lib/mk/x86/startup.mk new file mode 100644 index 000000000..78d4d7cea --- /dev/null +++ b/base-okl4/lib/mk/x86/startup.mk @@ -0,0 +1,8 @@ +REQUIRES = okl4 x86 +LIBS = cxx lock +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(BASE_DIR)/src/platform $(REP_DIR)/src/platform + +vpath crt0.s $(dir $(call select_from_repositories,src/platform/x86_32/crt0.s)) +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-okl4/mk/spec-okl4.mk b/base-okl4/mk/spec-okl4.mk new file mode 100644 index 000000000..067c7d14b --- /dev/null +++ b/base-okl4/mk/spec-okl4.mk @@ -0,0 +1,50 @@ +# +# Specifics for the OKL4 kernel API +# + +# +# Read default and builddir-specific config files +# +-include $(call select_from_repositories,etc/okl4.conf) +-include $(BUILD_BASE_DIR)/etc/okl4.conf + +# +# If no OKL4 source directory is set, we use the standard contrib directory +# +OKL4_DIR ?= $(BASE_DIR)/../base-okl4/contrib/okl4 + +# +# Make sure that symlink modification times are handled correctly. +# Otherwise, the creation of symlinks that depend on their own directory +# behaves like a phony rule. This is because the directory mtime is +# determined by taking the mtimes of containing symlinks into account. +# Hence, all symlinks (except for the youngest) depend on a directory +# with a newer mtime. The make flag -L fixes the problem. Alternatively, +# we could use 'cp' instead of 'ln'. +# +MAKEFLAGS += -L + +# +# OKL4-specific Genode headers +# +INC_DIR += $(BUILD_BASE_DIR)/include + +# +# Startup code to be used when building a program +# +STARTUP_LIB ?= startup +PRG_LIBS += $(STARTUP_LIB) + +# +# Define maximum number of threads, needed by the OKL4 kernel headers +# +CC_OPT += -DCONFIG_MAX_THREAD_BITS=10 + +# +# Clean rules for removing the side effects of building the platform +# library +# +clean_includes: + $(VERBOSE)rm -rf $(BUILD_BASE_DIR)/include + +clean cleanall: clean_includes diff --git a/base-okl4/mk/spec-okl4_x86.mk b/base-okl4/mk/spec-okl4_x86.mk new file mode 100644 index 000000000..e1c1a92c1 --- /dev/null +++ b/base-okl4/mk/spec-okl4_x86.mk @@ -0,0 +1,17 @@ +# +# Specifics for OKL4 on x86 +# + +SPECS += x86_32 okl4 +SPECS += pci ps2 vesa + +# +# Linker options specific for x86 +# +LD_TEXT_ADDR ?= 0x00200000 + +# +# Also include less-specific configuration last +# +include $(call select_from_repositories,mk/spec-x86_32.mk) +include $(call select_from_repositories,mk/spec-okl4.mk) diff --git a/base-okl4/patches/README b/base-okl4/patches/README new file mode 100644 index 000000000..10540b368 --- /dev/null +++ b/base-okl4/patches/README @@ -0,0 +1,71 @@ +This directory contains patches for the OKL4 kernel version 2.1.1-patch.9. + +:'syscall_pic.patch': + + The original distribution of the OKL4 kernel comes with x86 syscall bindings + that use absolute addressing modes. Therefore, code using L4 syscalls + cannot be compiled as position-independent code (gcc option '-fPIC'). + Unfortunately, shared libraries must be compiled as position independent + because the location of such a library's text segment is not known at + compile time. Consequently, L4 syscalls cannot be issued by shared + libraries, which is a severe limitation. The patch fixes the problem + by changing all L4 syscall bindings by removing PIC-incompatible + addressing modes. It does not affect the functionality of the kernel. + +:'eabi_build.patch': + + The build system of the orignal OKL4 distribution is not prepared to + compile ARM EABI binaries as generated by modern tool chains such as the + Codesourcery GCC. The patch applies the needed changes to the OKL4 build + infrastructure. + +:'reply_tid.patch': + + The original OKL4 kernel does not report the global thread ID of the + sender to the receiver of an IPC. Instead, the so called "threadhandle" + of the sender thread is provided. This value is the KTCB index of the + thread. It can be used as IPC destination when sending the reply but + is otherwise meaningless to the userland. However, this becomes a + problem when handing page faults because the page-fault handler is not + able to identify the faulting thread - only the faulting space. There + is no way for the pager to lookup the thread context of the faulting + thread with the information of the page-fault message. The patch changes + OKL4 such that the global thread ID of the sender is provided to the + receiver. + +:'kdb_reboot.patch': + + This patch enables machine reboot from the kernel debugger. + +:'char_bit.patch': + + This patch resolves the conflict of definitions of 'CHAR_BIT' between + libc and the OKL4 headers. 'CHAR_BIT' is normally defined by the libc + ('limits.h') but it also appears in OKL4's 'types.h'. The patch relaxes + the conflict by making 'CHAR_BIT' an enum value rather then a '#define'. + This way, OKL4's headers included into a dedicated 'Okl4' C++ namespace + (as done by Genode) will result in a 'Okl4::CHAR_BIT' name, not causing + trouble with libc headers included by the same compilation unit. + +:'gdt_init.patch': + + This patch fixes a off-by-one bug that prevents OKL4 from running on + VirtualBox with VT-x disabled. The original kernel calculates the + last segment address in a wrong way, causing a conflict between + GDT and TSS. As a result, VirtualBox stops with a 'GURU_MEDITATION' + error. + + +Applying the patches +-------------------- + +To apply a patch to the OKL4 kernel, use the 'patch' command. First check +the directory given at the header of the patch. It may contain a directory +prefix (such as 'a/'), which does not actually exist. This prefix is usually +generated by the tool used to create the patch. In this case, use the '-p' +option of the patch command. To apply the patch with the first part of the +path stripped, issue the following command (make sure that you changed to +the base directory of the OKL4 kernel): + +! patch -p1 < /path/to/syscall_pic.patch + diff --git a/base-okl4/patches/char_bit.patch b/base-okl4/patches/char_bit.patch new file mode 100644 index 000000000..6553e42e9 --- /dev/null +++ b/base-okl4/patches/char_bit.patch @@ -0,0 +1,25 @@ +--- a/libs/l4/include/types.h 2008-06-16 07:16:55.000000000 +0200 ++++ b/libs/l4/include/types.h 2010-11-24 12:48:52.000000000 +0200 +@@ -180,9 +180,19 @@ + #endif + + /** @todo FIXME Source from libc's limit.h once we can - awiggins. */ +-#define CHAR_BIT 8 +-#define WORD_T_BIT (sizeof (word_t) * CHAR_BIT) +-#define L4_BITS_PER_WORD WORD_T_BIT ++#ifndef CHAR_BIT ++/* ++ * Do not use a #define for 'CHAR_BIT' because such a definition ++ * would ultimately collide with libc headers (see the predictive ++ * comment above). If OKL4 headers are included into a dedicated ++ * C++ namespace, this conflict can be avoided. OKL4's CHAR_BIT ++ * will then end up being 'Okl4::CHAR_BIT' wheras libc's CHAR_BIT ++ * will populate the root name space. ++ */ ++enum { CHAR_BIT = 8 }; ++#endif ++enum { WORD_T_BIT = sizeof(word_t) * CHAR_BIT }; ++enum { L4_BITS_PER_WORD = WORD_T_BIT }; + + // XXX: magpie workaround + // # define __PLUS32 + (sizeof (L4_Word_t) * 8 - 32) diff --git a/base-okl4/patches/eabi_build.patch b/base-okl4/patches/eabi_build.patch new file mode 100644 index 000000000..e57e3a3b3 --- /dev/null +++ b/base-okl4/patches/eabi_build.patch @@ -0,0 +1,232 @@ +diff -r 71343ce52545 platform/s3c2410/tools/machines.py +--- a/platform/s3c2410/tools/machines.py Tue Aug 03 13:04:38 2010 +0200 ++++ b/platform/s3c2410/tools/machines.py Tue Aug 03 13:09:16 2010 +0200 +@@ -92,7 +92,7 @@ + skyeye = "gta01.skyeye" + device_core = "gta01" + memory = s3c2410.memory.copy() +- memory['physical'] = [Region(0x30000000L, 0x38000000L)] ++ memory['physical'] = [Region(0x33000000L, 0x38000000L)] + # memory['reserved'] = [Region(0x60000000L, 0xffffffffL, "reserved")] + # memory['sfr'] = [Region(0x48000000L, 0x60000000L, "dedicated")] + memory['rom1'] = [Region(0x08000000L, 0x30000000L, "dedicated")] +diff -r 71343ce52545 tools/pyelf/elf/constants.py +--- a/tools/pyelf/elf/constants.py Tue Aug 03 13:04:38 2010 +0200 ++++ b/tools/pyelf/elf/constants.py Tue Aug 03 13:09:16 2010 +0200 +@@ -237,9 +237,11 @@ + _show = {} + + class ArmFlags(IntString): ++ """IntString for Arm Flags field""" + _show = {} + + class MipsFlags(IntString): ++ """IntString for Mips Flags field""" + _show = {} + + EF_MIPS_NOREORDER = MipsFlags(1, "noreorder") +@@ -315,6 +317,7 @@ + PT_MIPS_REGINFO = ElfPhType(PT_LOPROC + 0, "MIPS_REGINFO") + + ++PT_ARM_EXIDX = ElfPhType(PT_LOPROC + 1, "ARM_EXIDX") + PT_PAX_FLAGS = ElfPhType(PT_LOOS + 0x5041580L, "PAX_FLAGS") + PT_GNU_EH_FRAME = ElfPhType(PT_LOOS + 0x474e550L, "GNU_EH_FRAME") + PT_GNU_STACK = ElfPhType(PT_LOOS + 0x474e551L, "GNU_STACK") +@@ -329,13 +332,18 @@ + PF_MASKOS = 0x0FF00000L + PF_MASKPROC = 0xF0000000L + +-SHN_UNDEF = 0 +-SHN_LORESERVE = 0xff00 +-SHN_LOPROC = 0xff00 +-SHN_HIPROC = 0xff1f +-SHN_ABS = 0xfff1 +-SHN_COMMON = 0xfff2 +-SHN_HIRESERVE = 0xffff ++class ElfShIndex(IntString): ++ """IntString for ELF section indexes""" ++ _show = {} ++ _default_string = "%3d" ++ ++SHN_UNDEF = ElfShIndex(0, "UND") ++SHN_LORESERVE = ElfShIndex(0xff00, "RSV") ++SHN_LOPROC = ElfShIndex(0xff00, "PRC") ++SHN_HIPROC = ElfShIndex(0xff1f, "PRC") ++SHN_ABS = ElfShIndex(0xfff1, "ABS") ++SHN_COMMON = ElfShIndex(0xfff2, "COM") ++SHN_HIRESERVE = ElfShIndex(0xffff, "RCV") + + class ElfShType(IntString): + """IntString for ELF section header types""" +@@ -356,8 +364,7 @@ + + SHT_INIT_ARRAY = ElfShType(14, "INIT_ARRAY") + SHT_FINI_ARRAY = ElfShType(15, "FINI_ARRAY") +-SHT_MIPS_REGINFO = ElfShType(0x70000006, "MIPS_REGINFO") +- ++SHT_GROUP = ElfShType(17, "GROUP") + + SHT_LOPROC = 0x70000000L + SHT_HIPROC = 0x7fffffffL +@@ -365,7 +372,11 @@ + SHT_HIUSER = 0xffffffffL + + ++SHT_ARM_EXIDX = ElfShType(SHT_LOPROC + 1, "ARM_EXIDX") ++ ++SHT_ARM_EXIDX = ElfShType(SHT_LOPROC + 1, 'ARM_EXIDX') + SHT_IA_64_UNWIND = ElfShType(SHT_LOPROC + 1, "IA_64_UNWIND") ++SHT_MIPS_REGINFO = ElfShType(SHT_LOPROC + 6, "MIPS_REGINFO") + + SHT_VERNEED = ElfShType(0x6ffffffe, "VERNEED") + SHT_VERSYM = ElfShType(0x6fffffff, "VERSYM") +@@ -380,22 +391,43 @@ + + SHF_LINK_ORDER = (1 << 7) + ++SHF_GROUP = (1 << 9) ++ + SHF_MASKOS = 0x0f000000L + SHF_MASKPROC = 0xf0000000L + + STN_UNDEF = 0 + +-STB_LOCAL = 0 +-STB_GLOBAL = 1 +-STB_WEAK = 2 +-STB_LOPROC = 13 +-STB_HIPROC = 15 ++class ElfSymbolBinding(IntString): ++ """IntString for the ELF Symbol Table Binding.""" ++ _show = {} ++ _default_string = ": %d" + +-STT_NOTYPE = 0 +-STT_OBJECT = 1 +-STT_FUNC = 2 +-STT_SECTION = 3 +-STT_FILE = 4 +-STT_LOPROC = 13 +-STT_HIPROC = 15 ++STB_LOCAL = ElfSymbolBinding(0, "LOCAL") ++STB_GLOBAL = ElfSymbolBinding(1, "GLOBAL") ++STB_WEAK = ElfSymbolBinding(2, "WEAK") ++STB_LOPROC = ElfSymbolBinding(13, "processor specific") ++STB_HIPROC = ElfSymbolBinding(15, "processor specific") + ++class ElfSymbolType(IntString): ++ """IntString for the ELF Symbol Table Type.""" ++ _show = {} ++ _default_string = ": %d" ++ ++STT_NOTYPE = ElfSymbolType(0, "NOTYPE") ++STT_OBJECT = ElfSymbolType(1, "OBJECT") ++STT_FUNC = ElfSymbolType(2, "FUNC") ++STT_SECTION = ElfSymbolType(3, "SECTION") ++STT_FILE = ElfSymbolType(4, "FILE") ++STT_LOPROC = ElfSymbolType(13, "processor specific") ++STT_HIPROC = ElfSymbolType(15, "processor specific") ++ ++class ElfSymbolVisibility(IntString): ++ """IntString for the Elf Symbol Table Visibility.""" ++ _show = {} ++ _default_string = ": %" ++ ++STV_DEFAULT = ElfSymbolVisibility(0, "DEFAULT") ++STV_INTERNAL = ElfSymbolVisibility(1, "INTERNAL") ++STV_HIDDEN = ElfSymbolVisibility(2, "HIDDEN") ++STV_PROTECTED = ElfSymbolVisibility(3, "PROTECTED") +diff -r 71343ce52545 tools/pyelf/elf/segment.py +--- a/tools/pyelf/elf/segment.py Tue Aug 03 13:04:38 2010 +0200 ++++ b/tools/pyelf/elf/segment.py Tue Aug 03 13:09:16 2010 +0200 +@@ -67,7 +67,7 @@ + __revision__ = 1.0 + + from elf.ByteArray import ByteArray +-from elf.constants import PT_NULL, PT_PHDR, SHT_NOBITS ++from elf.constants import PT_NULL, PT_PHDR, SHT_NOBITS, SHT_ARM_EXIDX + from elf.structures import InvalidArgument, ELF_PH_CLASSES + from elf.util import Span, Unprepared + +@@ -246,6 +246,9 @@ + """Return a copy of the Elf segment.""" + + for sect in self.sections: ++ # special handling for ARM EABI exception tables ++ if sect.type == SHT_ARM_EXIDX: ++ sect.link = None + assert sect.link is None + + sections = [sect.copy() for sect in self.sections] +diff -r 71343ce52545 tools/pyelf/weaver/image.py +--- a/tools/pyelf/weaver/image.py Tue Aug 03 13:04:38 2010 +0200 ++++ b/tools/pyelf/weaver/image.py Tue Aug 03 13:09:16 2010 +0200 +@@ -60,8 +60,8 @@ + import os.path + from elf.core import UnpreparedElfFile, SectionedElfSegment + from elf.section import UnpreparedElfSection +-from elf.constants import PT_PAX_FLAGS, PT_GNU_STACK, PT_PHDR, \ +- PT_LOAD, PT_MIPS_REGINFO, ET_EXEC, \ ++from elf.constants import PT_PAX_FLAGS, PT_GNU_STACK, PT_PHDR, SHT_ARM_EXIDX, \ ++ PT_LOAD, PT_MIPS_REGINFO, PT_ARM_EXIDX, ET_EXEC, \ + SHT_PROGBITS, SHF_WRITE, SHF_ALLOC, \ + PF_R, PF_W, EF_MIPS_ABI_O64, ElfMachine + from elf.ByteArray import ByteArray +@@ -79,8 +79,26 @@ + PT_GNU_STACK, # PT_GNU_STACK. Indicates stack executability. + PT_MIPS_REGINFO, # + PT_PHDR, # PT_PHDR. Entry for header table itself ++ SHT_ARM_EXIDX, + ] + ++# List of symbols that should be changed when they're added to the ++# image symbol table. ++# Probably should be list of regexs, but that's not needed yet. ++NO_PREFIX_SYMBOLS = ( ++ # ARM mapping symbols. ++ "$a", # ARM code ++ "$t", # THUMB code. ++ "$d", # Data items ++ ) ++ ++def can_prefix_symbol(symbol): ++ """ ++ Return whether or not a symbol should have a prefix added to it in ++ the final image. ++ """ ++ return symbol.name not in NO_PREFIX_SYMBOLS ++ + def valid_segment(segment): + if segment.type in skipped_types: + return False +@@ -88,7 +106,7 @@ + if segment.get_memsz() == 0: + return False + +- if segment.type != PT_LOAD: ++ if segment.type != PT_LOAD and segment.type != PT_ARM_EXIDX: + raise MergeError, "Unable to handle segments that aren't " \ + "of type LOAD (found type 0x%x)." % (segment.type) + +@@ -1109,13 +1127,17 @@ + if obj.attrs.phys_addr is not None: + pbase = obj.attrs.phys_addr + pend = pbase + obj.attrs.size - 1 +- physical_objects[pbase, pend] = obj.attrs.abs_name() ++ if (pbase, pend) in physical_objects: ++ physical_objects[pbase, pend].append(obj.attrs.abs_name()) ++ else: ++ physical_objects[pbase, pend] = [obj.attrs.abs_name()] + + print "VIRTUAL:" + for (base, end), name in sorted(virtual_objects.items()): + print " <%08x:%08x> %s" % (base, end, name) + + print "PHYSICAL:" +- for (base, end), name in sorted(physical_objects.items()): +- print " <%08x:%08x> %s" % (base, end, name) ++ for (base, end), names in sorted(physical_objects.items()): ++ for name in names: ++ print " <%08x:%08x> %s" % (base, end, name) + diff --git a/base-okl4/patches/elfweaver.patch b/base-okl4/patches/elfweaver.patch new file mode 100644 index 000000000..600f8a8c9 --- /dev/null +++ b/base-okl4/patches/elfweaver.patch @@ -0,0 +1,11 @@ +--- a/tools/pyelf/weaver/bootinfo.py 2010-10-28 15:37:01.000000000 +0200 ++++ b/tools/pyelf/weaver/bootinfo.py 2008-06-16 07:16:56.000000000 +0200 +@@ -80,7 +80,7 @@ + + # A guess at the number of extra ADD_*_MEM ops needed to hold the + # free-list. +-BOOTINFO_GUESS_OPS = 20 ++BOOTINFO_GUESS_OPS = 200 + + class BootInfoObject: + """Common interfaces for bootinfo objects.""" diff --git a/base-okl4/patches/gcc_4.4.5.patch b/base-okl4/patches/gcc_4.4.5.patch new file mode 100644 index 000000000..96b3060db --- /dev/null +++ b/base-okl4/patches/gcc_4.4.5.patch @@ -0,0 +1,54 @@ +--- a/arch/ia32/pistachio/include/segdesc.h 2008-06-16 07:16:54.000000000 +0200 ++++ b/arch/ia32/pistachio/include/segdesc.h 2011-07-04 10:59:54.251729822 +0200 +@@ -144,7 +144,7 @@ INLINE void ia32_segdesc_t::set_raw(word + INLINE void ia32_segdesc_t::dump(bool global, int index) + { + printf("%s[%d] = %p:%p", global ? "GDT" : "LDT", index, +- x.raw[0], x.raw[1]); ++ (void*)x.raw[0], (void*)x.raw[1]); + if ( (x.raw[0] == 0 && x.raw[1] == 0) || + (! x.d.s) ) + { +@@ -152,12 +152,12 @@ INLINE void ia32_segdesc_t::dump(bool gl + return; + } + printf(" <%p,%p> ", +- x.d.base_low + (x.d.base_high << 24), +- x.d.base_low + (x.d.base_high << 24) + ++ (void*)(x.d.base_low + (x.d.base_high << 24)), ++ (void*)(x.d.base_low + (x.d.base_high << 24) + + (x.d.g ? 0xfff | + (x.d.limit_low + (x.d.limit_high << 16)) << 12 : +- (x.d.limit_low + (x.d.limit_high << 16)))); +- printf("dpl=%d %d-bit ", x.d.dpl, x.d.d ? 32 : 16); ++ (x.d.limit_low + (x.d.limit_high << 16))))); ++ printf("dpl=%ld %d-bit ", x.d.dpl, x.d.d ? 32 : 16); + if ( x.d.type & 0x8 ) + printf("code %cC %cR ", + x.d.type & 0x4 ? ' ' : '!', +--- a/pistachio/include/stdarg.h 2008-06-16 07:16:55.000000000 +0200 ++++ b/pistachio/include/stdarg.h 2011-07-04 12:03:21.767314266 +0200 +@@ -80,7 +80,11 @@ typedef __builtin_va_list va_list; + #define va_arg(ap, type) __builtin_va_arg((ap), type) + #define va_copy(dest, src) __builtin_va_copy((ap), type) + #define va_end(ap) __builtin_va_end((ap)) +-#define va_start(ap, parmN) __builtin_stdarg_start((ap), (parmN)) ++#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) ++#define va_start(ap, parmN)__builtin_va_start((ap),parmN) ++#else ++#define va_start(ap, parmN)__builtin_stdarg_start((ap),parmN) ++#endif + + #endif + +--- a/pistachio/src/tracebuffer.cc 2008-06-16 07:16:55.000000000 +0200 ++++ b/pistachio/src/tracebuffer.cc 2011-07-04 12:04:16.597394680 +0200 +@@ -96,7 +96,7 @@ SECTION(SEC_INIT) void init_tracebuffer( + trace_buffer->buffers = TBUF_BUFFERS; + + /* Calculate buffer size */ +- buffer_first = sizeof(trace_buffer_t) + 7 & (~7UL); ++ buffer_first = sizeof(trace_buffer_t) + (7 & (~7UL)); + trace_buffer->buffer_size = + ((TBUFF_SIZE - buffer_first) / TBUF_BUFFERS) & (~7UL); + diff --git a/base-okl4/patches/gdt_init.patch b/base-okl4/patches/gdt_init.patch new file mode 100644 index 000000000..3b2e71b66 --- /dev/null +++ b/base-okl4/patches/gdt_init.patch @@ -0,0 +1,12 @@ +diff -r ac48ec8ffd86 arch/ia32/pistachio/src/init.cc +--- a/arch/ia32/pistachio/src/init.cc Tue Aug 03 13:10:36 2010 +0200 ++++ b/arch/ia32/pistachio/src/init.cc Wed Nov 24 12:01:30 2010 +0100 +@@ -230,7 +230,7 @@ + + /* create a temporary GDT descriptor to load the GDTR from */ + /*lint -e529 gdt_desc is only used inside __asm__ blocks */ +- ia32_sysdesc_t gdt_desc = {sizeof(gdt), (u32_t)gdt, 0} ; ++ ia32_sysdesc_t gdt_desc = {sizeof(gdt) - 1, (u32_t)gdt, 0} ; + + __asm__ __volatile__("lgdt %0 \n" /* load descriptor table */ + "ljmp %1,$1f \n" /* refetch code segment descr. */ diff --git a/base-okl4/patches/kdb_reboot.patch b/base-okl4/patches/kdb_reboot.patch new file mode 100644 index 000000000..fa4b6d348 --- /dev/null +++ b/base-okl4/patches/kdb_reboot.patch @@ -0,0 +1,54 @@ +diff -r ac48ec8ffd86 platform/pc99/pistachio/src/reboot.cc +--- a/platform/pc99/pistachio/src/reboot.cc Tue Aug 03 13:10:36 2010 +0200 ++++ b/platform/pc99/pistachio/src/reboot.cc Tue Oct 05 15:30:32 2010 +0200 +@@ -58,7 +58,9 @@ + /* + * Description: PC99 reset + */ ++ + #include ++#include + + /* + * Reboot the box +@@ -66,6 +68,39 @@ + + void Platform::reboot(void) + { ++ asm volatile ("cli \n" :); + +- for (;;); ++ /* i8042: store the next byte at port 0x60 as command byte */ ++ while (in_u8(0x64) & 0x2) ; ++ out_u8(0x64, 0x60); ++ ++ /* i8042 command byte (PS/2-compatible mode): ++ b0=0 ... no IRQ 1 is generated when data available in ++ input buffer ++ b1=0 ... no IRQ 1 is generated when mouse data available ++ in input buffer ++ b2=1 ... set SYS flag in status register -- tells POST ++ to perform "warm boot" tests/initiailization ++ b3=0 ... reserved ++ b4=0 ... keyboard interface enabled ++ b5=0 ... auxillary PS/2 device (mouse) interface ++ enabled ++ b6=0 ... translation disabled -- data appears at ++ input buffer exactly as read from keyboard ++ b7=0 ... reserved ++ */ ++ while (in_u8(0x64) & 0x2) ; ++ out_u8(0x60, 0x4); ++ ++ /* i8042: pulse output port with 1110b ++ b0=0 ... reset computer ++ b1=1 ... set gate A20 ++ b2=1 ... pull mouse data low ++ b3=1 ... pull mouse clock low ++ */ ++ while (in_u8(0x64) & 0x2) ; ++ out_u8(0x64, 0xfe); ++ ++ for (;;) ++ asm volatile ("hlt \n" :); + } diff --git a/base-okl4/patches/reply_tid.patch b/base-okl4/patches/reply_tid.patch new file mode 100644 index 000000000..345b20c23 --- /dev/null +++ b/base-okl4/patches/reply_tid.patch @@ -0,0 +1,21 @@ +diff -ru /tmp/okl4_2.1.1-patch.9/pistachio/src/ipc.cc pistachio/src/ipc.cc +--- a/pistachio/src/ipc.cc 2008-06-16 07:16:55.000000000 +0200 ++++ b/pistachio/src/ipc.cc 2010-09-21 12:35:41.000000000 +0200 +@@ -424,8 +424,6 @@ + + TRACE_IPC("send phase curr=%t, to=%t\n", current, to_tcb); + +- threadid_t sender_handle = threadhandle(current->tcb_idx); +- + check_waiting: + okl4_atomic_barrier_smp(); + // not waiting || (not waiting for me && not waiting for any) +@@ -500,7 +498,7 @@ + /* set sent_from to be thread handle of the sender. */ + TRACE_IPC("set sent_from of tcb(tid) 0x%lx(0x%lx) to handle of tcb %lx which is 0x%lx\n", + to_tcb, to_tid.get_raw(), current, sender_handle.get_raw()); +- to_tcb->sent_from = sender_handle; ++ to_tcb->sent_from = current->myself_global; + } + + if (EXPECT_FALSE(!transfer_message(current, to_tcb))) diff --git a/base-okl4/patches/suspend_resume.patch b/base-okl4/patches/suspend_resume.patch new file mode 100644 index 000000000..c540815f7 --- /dev/null +++ b/base-okl4/patches/suspend_resume.patch @@ -0,0 +1,28 @@ +diff --git a/arch/ia32/pistachio/src/trap.spp b/arch/ia32/pistachio/src/trap.spp +--- a/arch/ia32/pistachio/src/trap.spp ++++ b/arch/ia32/pistachio/src/trap.spp +@@ -142,6 +142,24 @@ + PROFILE_KERNEL_TIME_STOP + + orl $STACK_TOP, %esp ++ ++ /* Determine if there is any work required before returning ++ * back to userspace. */ ++ ++ /* Get address of current TCB */ ++ IA32_GET_CURRENT_TCB %edx ++ /* Add offset of the callback function pointer */ ++ addl $(OFS_TCB_POST_SYSCALL_CALLBACK), %edx ++ /* If the callback function pointer is set... */ ++ cmpl $0, (%edx) ++ je 1f ++ /* ...call start_post_syscall_callback() */ ++ pusha /* %esp = stack_top - 32 */ ++ call start_post_syscall_callback /* %esp = stack top */ ++ sub $32, %esp /* %esp = stack top - 32 */ ++ popa ++ 1: ++ + movl (%esp), %edx + addl $(OFS_TCB_ARCH), %edx + diff --git a/base-okl4/patches/syscall_pic.patch b/base-okl4/patches/syscall_pic.patch new file mode 100644 index 000000000..34c0ac8ba --- /dev/null +++ b/base-okl4/patches/syscall_pic.patch @@ -0,0 +1,411 @@ +diff -r e2bca488e43b arch/ia32/libs/l4/include/syscalls.h +--- a/arch/ia32/libs/l4/include/syscalls.h Tue Aug 03 12:58:05 2010 +0200 ++++ b/arch/ia32/libs/l4/include/syscalls.h Mon Aug 16 15:51:08 2010 +0200 +@@ -97,22 +97,13 @@ + #define L4_FlushDCache 30 + #define L4_FlushCache 31 + +-#if defined(__pic__) + # define __L4_SAVE_REGS " pushl %%ebx; pushl %%ebp\n" + # define __L4_RESTORE_REGS " popl %%ebp; popl %%ebx\n" + # define __L4_CLOBBER_REGS "cc" +-#else +-# define __L4_SAVE_REGS " pushl %%ebp \n" +-# define __L4_RESTORE_REGS " popl %%ebp \n" +-# define __L4_CLOBBER_REGS "ebx", "cc" +-#endif + +- +-#define __SYSCALL_SAVE_REGS \ +- " push %%ebp\n" +- +-#define __SYSCALL_RESTORE_REGS \ +- " pop %%ebp\n" ++#define __SYSCALL_SAVE_REGS __L4_SAVE_REGS ++#define __SYSCALL_RESTORE_REGS __L4_RESTORE_REGS ++#define __SYSCALL_CLOBBER_REGS __L4_CLOBBER_REGS + + L4_INLINE L4_ThreadId_t + L4_ExchangeRegisters(L4_ThreadId_t dest, +@@ -129,7 +120,6 @@ + L4_Word_t *old_UserDefhandle, L4_ThreadId_t *old_pager) + { + L4_ThreadId_t result; +- L4_Word_t dummy; + L4_Word_t *utcb = __L4_X86_Utcb() + (__L4_TCR_SYSCALL_ARGS); + + utcb[0] = flags; +@@ -137,21 +127,26 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" +- " movl %%esp, %%ebp\n" +- " movl $0x8000000a, %%eax\n" +- " sysenter\n" +- "0:\n" +- " movl %%ebp, %%ecx\n" +- __SYSCALL_RESTORE_REGS ++ " movl %%edi, %%ebx\n" //set IP ++ " call 0f \n" ++ " 0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" ++ " movl %%esp, %%ebp\n" ++ " movl $0x8000000a, %%eax\n" ++ " sysenter\n" ++ " 1:\n" ++ " movl %%ebp, %%ecx\n" //old flags ++ " movl %%ebx, %%edx\n" //old ip ++ " popl %%ebp\n" ++ __SYSCALL_RESTORE_REGS + : +- "=a"(result), "=S"(*old_control), "=D"(*old_sp), "=b"(*old_ip), +- "=c"(*old_flags), "=d"(dummy) ++ "=a"(result), "=S"(*old_control), "=D"(*old_sp), ++ "=c"(*old_flags), "=d"(*old_ip) + : +- "S"(dest), "d"(control), "c"(sp), "b"(ip) ++ "S"(dest), "d"(control), "c"(sp), "D"(ip) + : "memory" + ); +- + old_pager->raw = utcb[0]; + *old_UserDefhandle = utcb[1]; + +@@ -175,11 +170,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000006, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=b"(dummy), "=S"(dummy), "=d"(dummy), "=c"(dummy) +@@ -194,22 +193,28 @@ + L4_INLINE void + L4_ThreadSwitch(L4_ThreadId_t dest) + { ++ + L4_Word_t dummy; + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp),%%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000004, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=S"(dummy) + : + "S"(dest) + : +- "eax", "ebx", "ecx", "edx", "edi" ++ "eax", "ecx", "edx", "edi", ++ __SYSCALL_CLOBBER_REGS + ); + } + +@@ -230,11 +235,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000009, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(res_ts), "=d"(dummy), "=c"(dummy), "=b"(dummy) +@@ -262,18 +271,22 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x8000000b, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(tag_out), "=d"(dummy), "=c"(dummy) + : + "S"(to), "d"(FromSpecifier), "c"(tag) + : +- "edi", "ebx" ++ "edi", __SYSCALL_CLOBBER_REGS + ); + + if (!L4_IsNilThread(FromSpecifier)) { +@@ -296,11 +309,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x8000000b, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=S"(tag_out), "=d"(dummy), "=c"(dummy) +@@ -324,11 +341,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp),%%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x8000000b, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=S"(tag_out), "=d"(dummy), "=c"(dummy) +@@ -350,18 +371,22 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp),%%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000002, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy) + : + "S"(SpaceSpecifier), "d"(control) + : +- "ebx", "ecx", "edi" ++ "ecx", "edi", __SYSCALL_CLOBBER_REGS + ); + + return result; +@@ -382,11 +407,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000005, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(res_resources), "=d"(dummy), "=c"(dummy), "=b"(dummy) +@@ -410,11 +439,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000001, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy) +@@ -434,11 +467,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp),%%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000003, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy) +@@ -459,11 +496,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000007, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy), "=c"(dummy), "=b"(dummy) +@@ -484,11 +525,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000008, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy), "=c"(dummy) +@@ -508,11 +553,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x8000000e, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy), "=c"(dummy) +@@ -532,11 +581,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x8000000f, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy) +@@ -556,11 +609,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000010, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy) +@@ -583,11 +640,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000011, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(dummy), "=d"(dummy) +@@ -611,11 +672,15 @@ + + __asm__ __volatile__ ( + __SYSCALL_SAVE_REGS +- " movl $0f, %%edi\n" ++ " call 0f \n" ++ "0:\n" ++ " addl $(1f-0b), (%%esp)\n" ++ " movl (%%esp), %%edi\n" + " movl %%esp, %%ebp\n" + " movl $0x80000012, %%eax\n" + " sysenter\n" +- "0:\n" ++ "1:\n" ++ " popl %%ebp\n" + __SYSCALL_RESTORE_REGS + : + "=a"(result), "=S"(*size) diff --git a/base-okl4/run/env b/base-okl4/run/env new file mode 100644 index 000000000..3b255d7b3 --- /dev/null +++ b/base-okl4/run/env @@ -0,0 +1,200 @@ +# +# \brief OKL4-specific test-environment supplements +# \author Norman Feske +# \date 2010-08-16 +# +# This file is meant to be used as '--include' argument for 'tool/run'. +# + +## +# Get the base-okl4 repository +# +proc base_okl4_dir {} { return [repository_contains mk/spec-okl4.mk] } + +## +# Read the location of the OKL4 directory from 'etc/okl4.conf' +# +proc okl4_dir { } { + global _okl4_dir + + if {![info exists _okl4_dir]} { + if {[file exists etc/okl4.conf]} { + set _okl4_dir [exec sed -n "/^OKL4_DIR/s/^.*=\\s*//p" etc/okl4.conf] + if {[file exists $_okl4_dir]} { return $_okl4_dir } + } + + set _okl4_dir [base_okl4_dir]/contrib/okl4 + } + + return $_okl4_dir +} + +## +# Return the location of the OKL4 kernel +# +proc okl4 { } { + if {[okl4_external]} { return [okl4_dir]/build/pistachio/bin/kernel } + return bin/kernel +} + +## +# Return whether okl4 kernel is provided from the outside +# +proc okl4_external { } { + if {"[okl4_dir]" == "[base_okl4_dir]/contrib/okl4"} { return 0 } + return 1 +} + +################################## +## Test framework API functions ## +################################## + +proc create_boot_directory { } { + exec rm -rf [run_dir] + exec mkdir -p [run_dir]/genode +} + + +set weaver_xml_template { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +proc build_boot_image {binaries} { + global weaver_xml_template + + # + # Strip binaries + # + copy_and_strip_genode_binaries_to_run_dir $binaries + + # + # Build kernel if needed + # + # Once the kernel is exists, it gets never revisited automatically. + # Consequently, when changing the kernel sources, the kernel build must be + # issued explicitly via 'make kernel'. This way, the rare case of changing + # the kernel does not stand in the way of the everyday's work flow of + # executing run scripts as quick as possible. + # + if {![okl4_external] && ![file exists [okl4]]} { build { kernel } } + + exec cp [okl4] [run_dir]/kernel + + # + # Generate ELF weaver config + # + set fh [open "[run_dir].weaver.xml" "WRONLY CREAT TRUNC"] + puts $fh {} + puts $fh {} + puts $fh {} + regsub okl4_kernel $weaver_xml_template "[run_dir]/kernel" weaver_xml_template + regsub core $weaver_xml_template "[run_dir]/genode/core" weaver_xml_template + puts $fh $weaver_xml_template + puts $fh { } + puts $fh " " + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " " } + } + puts $fh { } + puts $fh {} + close $fh + + # + # Run ELF Weaver to create a boot image + # + set ret [exec "[okl4_dir]/tools/pyelf/elfweaver" merge --output "[run_dir]/image.elf" "[run_dir].weaver.xml"] + if {[regexp "error" $ret dummy]} { + puts stderr "Elfweaver failed: $ret" + exit -6 + } + exec [cross_dev_prefix]strip [run_dir]/image.elf + exec cp [run_dir]/image.elf [run_dir].elf + exec gzip [run_dir]/image.elf + + # + # Keep only the ELF boot image, but remove stripped binaries + # + exec rm -r [run_dir]/genode + + # + # Install GRUB + # + install_iso_bootloader_to_run_dir + + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "hiddenmenu" + puts $fh "\ntitle Genode on OKL4" + puts $fh "kernel /image.elf.gz" + puts $fh "vbeset 0x117" + close $fh + + create_iso_image_from_run_dir +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + spawn_qemu $wait_for_re $timeout_value } diff --git a/base-okl4/run/priority.run b/base-okl4/run/priority.run new file mode 100644 index 000000000..f544ce9f7 --- /dev/null +++ b/base-okl4/run/priority.run @@ -0,0 +1,42 @@ +assert_spec okl4_x86 + +build "core init" + +create_boot_directory + +install_config "[exec cat [genode_dir]/os/config/priority]" + +build_boot_image "core init" + +append qemu_args "-nographic -m 256" + +# run genode until the init->init.2 process gives us a life sign +run_genode_until "init.2.*abort called.*\n" 100 + +puts "dumping priorities using the kernel debugger..." + +# send escape key to break into the kernel debugger, wait for prompt +send "\x1b" +expect "> " + +# send commend for dumping the scheduling queue +send "q" +expect "idle : idle_thread" + +set output $expect_out(buffer) + +# +# The 'output' variable contains the kernel-debugger output since +# the last prompt until the current expect string. But we care +# only for the lines with the actual scheduler queues. Each +# line of interest starts with a '[' character. +# +grep_output {^\[} + +compare_output_to { + [128]: (roottask) (activati) (pager) (ioport) (init) (init) (init.1) (init.1) (init.2) (init.11) (init.12) + [112]: (init.11) + [ 96]: (init.12) (init.121) {init.121} + [ 64]: (init.2) +} + diff --git a/base-okl4/src/base/bootinfo/README b/base-okl4/src/base/bootinfo/README new file mode 100644 index 000000000..f62768eae --- /dev/null +++ b/base-okl4/src/base/bootinfo/README @@ -0,0 +1,2 @@ +This directory contains support code for building Iguana's bootinfo +library from within Genode's build process. diff --git a/base-okl4/src/base/bootinfo/stdint.h b/base-okl4/src/base/bootinfo/stdint.h new file mode 100644 index 000000000..6635209b0 --- /dev/null +++ b/base-okl4/src/base/bootinfo/stdint.h @@ -0,0 +1,20 @@ +/* + * \brief Hand-selected type definitions required by the bootinfo library + * \author Norman Feske + * \date 2009-07-09 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __BOOTINFO__STDINT_H__ +#define __BOOTINFO__STDINT_H__ + +typedef unsigned long uintptr_t; +typedef long intptr_t; + +#endif /* __BOOTINFO__STDINT_H__ */ diff --git a/base-okl4/src/base/bootinfo/stdio.h b/base-okl4/src/base/bootinfo/stdio.h new file mode 100644 index 000000000..40054e3c3 --- /dev/null +++ b/base-okl4/src/base/bootinfo/stdio.h @@ -0,0 +1,12 @@ +/* + * \brief Dummy header to avoid build warning of the bootinfo library + * \author Norman Feske + * \date 2009-07-09 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ diff --git a/base-okl4/src/base/console/core_console.h b/base-okl4/src/base/console/core_console.h new file mode 100644 index 000000000..8c1ac8deb --- /dev/null +++ b/base-okl4/src/base/console/core_console.h @@ -0,0 +1,31 @@ +/* + * \brief Console backend for OKL4 + * \author Norman Feske + * \date 2009-03-25 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +namespace Okl4 { extern "C" { +#include +} } + +#include + + +namespace Genode +{ + class Core_console : public Console + { + protected: + + void _out_char(char c) { Okl4::L4_KDB_PrintChar(c); } + }; +} + + diff --git a/base-okl4/src/base/ipc/ipc.cc b/base-okl4/src/base/ipc/ipc.cc new file mode 100644 index 000000000..f32d59ef1 --- /dev/null +++ b/base-okl4/src/base/ipc/ipc.cc @@ -0,0 +1,287 @@ +/* + * \brief IPC implementation for OKL4 + * \author Norman Feske + * \date 2009-03-25 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include + +namespace Okl4 { extern "C" { +#include +#include +#include +#include +} } + +using namespace Genode; +using namespace Okl4; + + +/*************** + ** Utilities ** + ***************/ + +/** + * Print string, bypassing Genode's LOG mechanism + * + * This function is used in conditions where Genode's base mechanisms may fail. + */ +static void kdb_emergency_print(const char *s) +{ + for (; s && *s; s++) + Okl4::L4_KDB_PrintChar(*s); +} + + +/** + * Copy message registers from UTCB to destination message buffer + */ +static void copy_utcb_to_msgbuf(L4_MsgTag_t rcv_tag, Msgbuf_base *rcv_msg) +{ + int num_msg_words = (int)L4_UntypedWords(rcv_tag); + if (num_msg_words <= 0) return; + + /* look up and validate destination message buffer to receive the payload */ + L4_Word_t *msg_buf = (L4_Word_t *)rcv_msg->buf; + if (num_msg_words*sizeof(L4_Word_t) > rcv_msg->size()) { + PERR("receive message buffer too small msg size=%zd, buf size=%zd", + num_msg_words*sizeof(L4_Word_t), rcv_msg->size()); + num_msg_words = rcv_msg->size()/sizeof(L4_Word_t); + } + + /* read message payload into destination message buffer */ + L4_StoreMRs(1, num_msg_words, msg_buf); +} + + +/** + * Copy message payload to UTCB message registers + * + * The message tag contains the information about the number of message words + * to send. The tag is always supplied in message register 0. Message register + * 1 is used for the local name. All subsequent message registers hold the + * message payload. + */ +static void copy_msgbuf_to_utcb(Msgbuf_base *snd_msg, unsigned num_msg_words, + L4_Word_t local_name) +{ + /* look up address and size of message payload */ + L4_Word_t *msg_buf = (L4_Word_t *)snd_msg->buf; + + num_msg_words += 1; + + if (num_msg_words >= L4_GetMessageRegisters()) { + kdb_emergency_print("Message does not fit into UTCB message registers\n"); + num_msg_words = L4_GetMessageRegisters() - 1; + } + + L4_MsgTag_t snd_tag; + snd_tag.raw = 0; + snd_tag.X.u = num_msg_words; + L4_LoadMR (0, snd_tag.raw); + L4_LoadMR (1, local_name); + L4_LoadMRs(2, num_msg_words - 1, msg_buf + 1); +} + + +/***************** + ** Ipc_ostream ** + *****************/ + +void Ipc_ostream::_send() +{ + copy_msgbuf_to_utcb(_snd_msg, _write_offset/sizeof(L4_Word_t), + _dst.local_name()); + + /* perform IPC send operation */ + L4_MsgTag_t rcv_tag = L4_Send(_dst.tid()); + + if (L4_IpcFailed(rcv_tag)) { + PERR("ipc error in _send."); + throw Genode::Ipc_error(); + } + + _write_offset = sizeof(umword_t); +} + + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) +: + Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()), + _snd_msg(snd_msg), _dst(dst) +{ + _write_offset = sizeof(umword_t); +} + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() +{ + /* + * Wait for IPC message + * + * The message tag (holding the size of the message) is located at + * message register 0 and implicitly addressed by 'L4_UntypedWords()'. + */ + L4_MsgTag_t rcv_tag = L4_Wait(&_rcv_cs); + + /* copy message from the UTCBs message registers to the receive buffer */ + copy_utcb_to_msgbuf(rcv_tag, _rcv_msg); + + /* reset unmarshaller */ + _read_offset = sizeof(umword_t); +} + + +/** + * Return the global thread ID of the calling thread + * + * On OKL4 we cannot use 'L4_Myself()' to determine our own thread's + * identity. By convention, each thread stores its global ID in a + * defined entry of its UTCB. + */ +static inline Okl4::L4_ThreadId_t thread_get_my_global_id() +{ + Okl4::L4_ThreadId_t myself; + myself.raw = Okl4::__L4_TCR_ThreadWord(UTCB_TCR_THREAD_WORD_MYSELF); + return myself; +} + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) +: + Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), + Native_capability(thread_get_my_global_id(), 0), + _rcv_msg(rcv_msg) +{ + _rcv_cs = Okl4::L4_nilthread; + _read_offset = sizeof(umword_t); +} + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() +{ + /* copy call message to the UTCBs message registers */ + copy_msgbuf_to_utcb(_snd_msg, _write_offset/sizeof(L4_Word_t), + _dst.local_name()); + + L4_Accept(L4_UntypedWordsAcceptor); + L4_MsgTag_t rcv_tag = L4_Call(_dst.tid()); + + enum { ERROR_MASK = 0xe, ERROR_CANCELED = 3 << 1 }; + if (L4_IpcFailed(rcv_tag) && + ((L4_ErrorCode() & ERROR_MASK) == ERROR_CANCELED)) + throw Genode::Blocking_canceled(); + + if (L4_IpcFailed(rcv_tag)) + kdb_emergency_print("Ipc failed\n"); + + /* copy request message from the UTCBs message registers */ + copy_utcb_to_msgbuf(rcv_tag, _rcv_msg); + + _write_offset = _read_offset = sizeof(umword_t); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, + Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) +: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) { } + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_prepare_next_reply_wait() +{ + /* now we have a request to reply */ + _reply_needed = true; + + /* leave space for return value at the beginning of the msgbuf */ + _write_offset = 2*sizeof(umword_t); + + /* receive buffer offset */ + _read_offset = sizeof(umword_t); +} + + +void Ipc_server::_wait() +{ + /* wait for new server request */ + try { Ipc_istream::_wait(); } catch (Blocking_canceled) { } + + /* define destination of next reply */ + _dst = Native_capability(_rcv_cs, badge()); + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() +{ + /* copy reply to the UTCBs message registers */ + copy_msgbuf_to_utcb(_snd_msg, _write_offset/sizeof(L4_Word_t), + _dst.local_name()); + + /* perform non-blocking IPC send operation */ + L4_MsgTag_t rcv_tag = L4_Reply(_dst.tid()); + + if (L4_IpcFailed(rcv_tag)) + PERR("ipc error in _reply - gets ignored"); + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply_wait() +{ + if (_reply_needed) { + + /* copy reply to the UTCBs message registers */ + copy_msgbuf_to_utcb(_snd_msg, _write_offset/sizeof(L4_Word_t), + _dst.local_name()); + + L4_MsgTag_t rcv_tag = L4_ReplyWait(_dst.tid(), &_rcv_cs); + + /* + * TODO: Check for IPC error + */ + + /* copy request message from the UTCBs message registers */ + copy_utcb_to_msgbuf(rcv_tag, _rcv_msg); + + /* define destination of next reply */ + _dst = Native_capability(_rcv_cs, badge()); + + _prepare_next_reply_wait(); + + } else + _wait(); +} + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) : + Ipc_istream(rcv_msg), + Ipc_ostream(Native_capability(), snd_msg), + _reply_needed(false) +{ } diff --git a/base-okl4/src/base/ipc/pager.cc b/base-okl4/src/base/ipc/pager.cc new file mode 100644 index 000000000..8b29f5bfd --- /dev/null +++ b/base-okl4/src/base/ipc/pager.cc @@ -0,0 +1,143 @@ +/* + * \brief Pager support for OKL4 + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +namespace Okl4 { extern "C" { +#include +#include +#include +#include +} } + +static const bool verbose_page_fault = false; +static const bool verbose_exception = false; + + +using namespace Genode; +using namespace Okl4; + +/** + * Print page-fault information in a human-readable form + */ +static inline void print_page_fault(L4_Word_t type, L4_Word_t addr, L4_Word_t ip, + unsigned long badge) +{ + printf("page (%s%s%s) fault at fault_addr=%lx, fault_ip=%lx, from=%lx\n", + type & L4_Readable ? "r" : "-", + type & L4_Writable ? "w" : "-", + type & L4_eXecutable ? "x" : "-", + addr, ip, badge); +} + + +/** + * Return the global thread ID of the calling thread + * + * On OKL4 we cannot use 'L4_Myself()' to determine our own thread's + * identity. By convention, each thread stores its global ID in a + * defined entry of its UTCB. + */ +static inline Okl4::L4_ThreadId_t thread_get_my_global_id() +{ + Okl4::L4_ThreadId_t myself; + myself.raw = Okl4::__L4_TCR_ThreadWord(UTCB_TCR_THREAD_WORD_MYSELF); + return myself; +} + + +/************* + ** Mapping ** + *************/ + +Mapping::Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, unsigned l2size, bool rw) +: + _fpage(L4_FpageLog2(dst_addr, l2size)), + /* + * OKL4 does not support write-combining as mapping attribute. + */ + _phys_desc(L4_PhysDesc(src_addr, 0)) +{ + L4_Set_Rights(&_fpage, rw ? L4_ReadWriteOnly : L4_ReadeXecOnly); +} + + +Mapping::Mapping() { } + + +/*************** + ** IPC pager ** + ***************/ + +void Ipc_pager::wait_for_fault() +{ + /* wait for fault */ + _faulter_tag = L4_Wait(&_last); + + /* + * Read fault information + */ + + /* exception */ + if (is_exception()) { + L4_StoreMR(1, &_fault_ip); + + if (verbose_exception) + PERR("Exception (label 0x%x) occured in space %d at IP 0x%p", + (int)L4_Label(_faulter_tag), (int)L4_SenderSpace().raw, + (void *)_fault_ip); + } + + /* page fault */ + else { + L4_StoreMR(1, &_fault_addr); + L4_StoreMR(2, &_fault_ip); + + if (verbose_page_fault) + print_page_fault(L4_Label(_faulter_tag), _fault_addr, _fault_ip, _last.raw); + } +} + + +void Ipc_pager::reply_and_wait_for_fault() +{ + L4_SpaceId_t to_space; + to_space.raw = L4_ThreadNo(_last) >> Thread_id_bits::THREAD; + + /* map page to faulting space */ + int ret = L4_MapFpage(to_space, _reply_mapping.fpage(), + _reply_mapping.phys_desc()); + + if (ret != 1) + PERR("L4_MapFpage returned %d, error_code=%d", + ret, (int)L4_ErrorCode()); + + /* reply to page-fault message to resume the faulting thread */ + acknowledge_wakeup(); + + wait_for_fault(); +} + + +void Ipc_pager::acknowledge_wakeup() +{ + /* answer wakeup call from one of core's region-manager sessions */ + L4_LoadMR(0, 0); + L4_Send(_last); +} + + +Ipc_pager::Ipc_pager() : Native_capability(thread_get_my_global_id(), 0) { } + diff --git a/base-okl4/src/base/lock/lock_helper.h b/base-okl4/src/base/lock/lock_helper.h new file mode 100644 index 000000000..ad66b78b4 --- /dev/null +++ b/base-okl4/src/base/lock/lock_helper.h @@ -0,0 +1,102 @@ +/* + * \brief OKL4-specific helper functions for the Lock implementation + * \author Norman Feske + * \date 2009-07-09 + * + * This file serves as adapter between the generic lock implementation + * in 'lock.cc' and the underlying kernel. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +} } + + +/** + * Yield CPU time + */ +static inline void thread_yield() { Okl4::L4_Yield(); } + + +/** + * Custom ExchangeRegisters wrapper for waking up a thread + * + * When waking up an lock applicant, we need to make sure that the thread was + * stopped beforehand. Therefore, we evaluate the previous thread state as + * returned by the 'L4_ExchangeRegisters' call. + * + * \return true if the thread was in blocking state + */ +static bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + using namespace Okl4; + + L4_Word_t dummy; + L4_ThreadId_t dummy_id; + L4_ThreadState_t state; + + L4_ExchangeRegisters(tid, L4_ExReg_Resume + L4_ExReg_AbortIPC, 0, 0, 0, + 0, L4_nilthread, &state.raw, &dummy, &dummy, &dummy, + &dummy, &dummy_id); + + return L4_ThreadWasHalted(state); +} + + +/* + * XXX Avoid duplicating this function, see 'ipc.cc', 'pager.cc', and + * 'irq_session_component.cc' + */ +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + Okl4::L4_ThreadId_t myself; + myself.raw = Okl4::__L4_TCR_ThreadWord(Genode::UTCB_TCR_THREAD_WORD_MYSELF); + return myself; +} + + +static inline Genode::Native_thread_id thread_invalid_id() +{ + return Okl4::L4_nilthread; +} + + +/** + * Check if a native thread ID is initialized + * + * \return true if ID is initialized + */ +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return (tid.raw != 0); +} + + +/** + * Yield CPU time to the specified thread + */ +static inline void thread_switch_to(Genode::Native_thread_id tid) +{ + Okl4::L4_ThreadSwitch(tid); +} + + +/** + * Unconditionally block the calling thread + */ +static inline void thread_stop_myself() +{ + Okl4::L4_Stop(thread_get_my_native_id()); +} diff --git a/base-okl4/src/base/pager/pager.cc b/base-okl4/src/base/pager/pager.cc new file mode 100644 index 000000000..9fd4d2193 --- /dev/null +++ b/base-okl4/src/base/pager/pager.cc @@ -0,0 +1,120 @@ +/* + * \brief OKL4-specific pager framework + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + + +/********************** + ** Pager activation ** + **********************/ + +void Pager_activation_base::entry() +{ + Ipc_pager pager; + _cap = pager; + _cap_valid.unlock(); + + bool reply_pending = false; + while (1) { + + if (reply_pending) + pager.reply_and_wait_for_fault(); + else + pager.wait_for_fault(); + + reply_pending = false; + + /* lookup referenced object */ + Pager_object *obj = _ep ? _ep->obj_by_id(pager.badge()) : 0; + + /* handle request */ + if (obj) { + if (pager.is_exception()) { + obj->submit_exception_signal(); + continue; + } + + /* send reply if page-fault handling succeeded */ + if (!obj->pager(pager)) + reply_pending = true; + + continue; + + } else { + + /* prevent threads outside of core to mess with our wake-up interface */ +// enum { CORE_TASK_ID = 4 }; +// if (pager.last() != CORE_TASK_ID) { + +#warning Check for messages from outside of core + if (0) { + + } else { + + /* + * We got a request from one of cores region-manager sessions + * to answer the pending page fault of a resolved region-manager + * client. Hence, we have to send the page-fault reply to the + * specified thread and answer the call of the region-manager + * session. + * + * When called from a region-manager session, we receive the + * core-local address of the targeted pager object via the + * first message word, which corresponds to the 'fault_ip' + * argument of normal page-fault messages. + */ + obj = reinterpret_cast(pager.fault_ip()); + + /* send reply to the calling region-manager session */ + pager.acknowledge_wakeup(); + + /* answer page fault of resolved pager object */ + pager.set_reply_dst(obj->cap()); + pager.acknowledge_wakeup(); + } + } + }; +} + + +/********************** + ** Pager entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *, Pager_activation_base *a) +: _activation(a) +{ _activation->ep(this); } + + +void Pager_entrypoint::dissolve(Pager_object *obj) +{ + remove(obj); +} + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + /* return invalid capability if no activation is present */ + if (!_activation) return Pager_capability(); + + Native_capability cap = Native_capability(_activation->cap().tid(), obj->badge()); + + /* add server object to object pool */ + obj->cap(cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return reinterpret_cap_cast(cap); +} diff --git a/base-okl4/src/base/thread/thread_bootstrap.cc b/base-okl4/src/base/thread/thread_bootstrap.cc new file mode 100644 index 000000000..47f2cf72a --- /dev/null +++ b/base-okl4/src/base/thread/thread_bootstrap.cc @@ -0,0 +1,29 @@ +/* + * \brief Default thread bootstrap code + * \author Norman Feske + * \date 2009-04-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +namespace Okl4 { extern "C" { +#include +#include +} } + +namespace Okl4 { + extern L4_Word_t copy_uregister_to_utcb(void); +} + + +void Genode::Thread_base::_thread_bootstrap() +{ + _tid.l4id.raw = Okl4::copy_uregister_to_utcb(); +} diff --git a/base-okl4/src/core/core_rm_session.cc b/base-okl4/src/core/core_rm_session.cc new file mode 100644 index 000000000..36b0763e6 --- /dev/null +++ b/base-okl4/src/core/core_rm_session.cc @@ -0,0 +1,62 @@ +/* + * \brief OKL4-specific implementation of core-local RM session + * \author Norman Feske + * \date 2009-04-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include +#include + +using namespace Genode; + + +Rm_session::Local_addr +Core_rm_session::attach(Dataspace_capability ds_cap, size_t size, + off_t offset, bool use_local_addr, + Rm_session::Local_addr) +{ + using namespace Okl4; + + Dataspace_component *ds = static_cast(_ds_ep->obj_by_cap(ds_cap)); + if (!ds) + throw Invalid_dataspace(); + + if (size == 0) + size = ds->size(); + + size_t page_rounded_size = (size + get_page_size() - 1) & get_page_mask(); + + if (use_local_addr) { + PERR("Parameter 'use_local_addr' not supported within core"); + return 0; + } + + if (offset) { + PERR("Parameter 'offset' not supported within core"); + return 0; + } + + /* allocate range in core's virtual address space */ + void *virt_addr; + if (!platform()->region_alloc()->alloc(page_rounded_size, &virt_addr)) { + PERR("Could not allocate virtual address range in core of size %zd\n", + page_rounded_size); + return false; + } + + /* map the dataspace's physical pages to corresponding virtual addresses */ + unsigned num_pages = page_rounded_size >> get_page_size_log2(); + if (!map_local(ds->phys_addr(), (addr_t)virt_addr, num_pages)) + return 0; + + return virt_addr; +} diff --git a/base-okl4/src/core/include/core_rm_session.h b/base-okl4/src/core/include/core_rm_session.h new file mode 100644 index 000000000..165c5d13a --- /dev/null +++ b/base-okl4/src/core/include/core_rm_session.h @@ -0,0 +1,57 @@ +/* + * \brief OKL4-specific core-local region manager session + * \author Norman Feske + * \date 2009-04-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_RM_SESSION_H_ +#define _CORE__INCLUDE__CORE_RM_SESSION_H_ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +namespace Genode { + + /** + * Region manager that uses the physical dataspace + * addresses directly as virtual addresses. + */ + class Core_rm_session : public Rm_session + { + private: + + Rpc_entrypoint *_ds_ep; + + public: + + Core_rm_session(Rpc_entrypoint *ds_ep): _ds_ep(ds_ep) { } + + Local_addr attach(Dataspace_capability ds_cap, size_t size=0, + off_t offset=0, bool use_local_addr = false, + Local_addr local_addr = 0); + + void detach(Local_addr) { } + + Pager_capability add_client(Thread_capability thread) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability handler) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_RM_SESSION_H_ */ diff --git a/base-okl4/src/core/include/map_local.h b/base-okl4/src/core/include/map_local.h new file mode 100644 index 000000000..8323d73ff --- /dev/null +++ b/base-okl4/src/core/include/map_local.h @@ -0,0 +1,126 @@ +/* + * \brief Core-local mapping + * \author Norman Feske + * \date 2010-02-15 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__MAP_LOCAL_H_ +#define _CORE__INCLUDE__MAP_LOCAL_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +#include +} } + +namespace Genode { + + inline void unmap_local_log2_range(Okl4::L4_Word_t base, Okl4::L4_Word_t size_log2) + { + using namespace Okl4; + L4_Fpage_t fpage = L4_FpageLog2(base, size_log2); + L4_FpageAddRightsTo(&fpage, L4_FullyAccessible); + int ret = L4_UnmapFpage(L4_rootspace, fpage); + if (ret != 1) + PERR("could not unmap page at %p from core (Error Code %ld)", + (void *)base, L4_ErrorCode()); + } + + /** + * Map physical pages to core-local virtual address range + * + * On OKL4v2, all mappings originate from the physical address space. + * + * \param from_phys physical source address + * \param to_virt core-local destination address + * \param num_pages number of pages to map + * + * \return true on success + */ + inline bool map_local(addr_t from_phys, addr_t to_virt, size_t num_pages) + { + using namespace Okl4; + + for (unsigned i = 0, offset = 0; i < num_pages; i++) { + L4_Fpage_t fpage = L4_FpageLog2(to_virt + offset, get_page_size_log2()); + L4_PhysDesc_t phys_desc = L4_PhysDesc(from_phys + offset, 0); + fpage.X.rwx = 7; + + if (L4_MapFpage(L4_rootspace, fpage, phys_desc) != 1) { + PERR("Core-local memory mapping failed, Error Code=%d\n", (int)L4_ErrorCode()); + return false; + } + offset += get_page_size(); + } + return true; + } + + /** + * Unmap pages from core's address space + * + * \param virt_addr first core-local address to unmap, must be page-aligned + * \param num_pages number of pages to unmap + * + * \return true on success + */ + inline bool unmap_local(addr_t virt_addr, size_t num_pages) + { + using namespace Okl4; + + L4_Word_t addr = virt_addr; + L4_Word_t remaining_size = num_pages << get_page_size_log2(); + L4_Word_t size_log2 = get_page_size_log2(); + + /* + * Let unmap granularity ('size_log2') grow + */ + while (remaining_size >= (1UL << size_log2)) { + + enum { SIZE_LOG2_MAX = 22 /* 4M */ }; + + /* issue 'unmap' for the current address if flexpage aligned */ + if (addr & (1 << size_log2)) { + unmap_local_log2_range(addr, size_log2); + + remaining_size -= 1 << size_log2; + addr += 1 << size_log2; + } + + /* increase flexpage size */ + size_log2++; + } + + /* + * Let unmap granularity ('size_log2') shrink + */ + while (remaining_size > 0) { + + if (remaining_size >= (1UL << size_log2)) { + unmap_local_log2_range(addr, size_log2); + + remaining_size -= 1 << size_log2; + addr += 1 << size_log2; + } + + /* decrease flexpage size */ + size_log2--; + } + return true; + } +} + +#endif /* _CORE__INCLUDE__MAP_LOCAL_H_ */ diff --git a/base-okl4/src/core/include/pd_session_component.h b/base-okl4/src/core/include/pd_session_component.h new file mode 100644 index 000000000..8235b8a95 --- /dev/null +++ b/base-okl4/src/core/include/pd_session_component.h @@ -0,0 +1,59 @@ +/* + * \brief Core-specific instance of the PD session interface for OKL4 + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__OKL4__PD_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__OKL4__PD_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Pd_session_component : public Rpc_object + { + private: + + Platform_pd _pd; + Rpc_entrypoint *_thread_ep; + + public: + + Pd_session_component(Rpc_entrypoint *thread_ep, const char *args) + : _thread_ep(thread_ep) { } + + + /************************** + ** Pd session interface ** + **************************/ + + int bind_thread(Thread_capability); + int assign_parent(Parent_capability); + + + /***************************** + ** OKL4-specific additions ** + *****************************/ + + void space_pager(Thread_capability thread); + + Okl4::L4_SpaceId_t space_id() { + return Okl4::L4_SpaceId(_pd.pd_id()); } + }; +} + +#endif /* _CORE__INCLUDE__OKL4__PD_SESSION_COMPONENT_H_ */ diff --git a/base-okl4/src/core/include/platform.h b/base-okl4/src/core/include/platform.h new file mode 100644 index 000000000..16da21ac6 --- /dev/null +++ b/base-okl4/src/core/include/platform.h @@ -0,0 +1,134 @@ +/* + * \brief OKL4 platform + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_H_ +#define _CORE__INCLUDE__PLATFORM_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include +#include +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +} } + +namespace Genode { + + class Platform : public Platform_generic + { + private: + + typedef Core_mem_allocator::Phys_allocator Phys_allocator; + + Platform_pd *_core_pd; /* core protection domain */ + Platform_thread *_core_pager; /* pager for core threads */ + Core_mem_allocator _core_mem_alloc; /* core-accessible memory */ + Phys_allocator _io_mem_alloc; /* MMIO allocator */ + Phys_allocator _io_port_alloc; /* I/O port allocator */ + Phys_allocator _irq_alloc; /* IRQ allocator */ + Rom_fs _rom_fs; /* ROM file system */ + + /* + * Virtual-memory range for non-core address spaces. + * The virtual memory layout of core is maintained in + * '_core_mem_alloc.virt_alloc()'. + */ + addr_t _vm_start; + size_t _vm_size; + + /* + * Start of address range used for the UTCBs + */ + addr_t _utcb_base; + + public: + + /** + * Constructor + */ + Platform(); + + /** + * Accessor for core pd object + */ + Platform_pd *core_pd() { return _core_pd; } + + /** + * Accessor for core pager thread object + */ + Platform_thread *core_pager() { return _core_pager; } + + + /********************************************** + ** Callbacks used for parsing the boot info ** + **********************************************/ + + static int bi_init_mem(Okl4::uintptr_t, Okl4::uintptr_t, + Okl4::uintptr_t, Okl4::uintptr_t, + const Okl4::bi_user_data_t *); + + static int bi_add_virt_mem(Okl4::bi_name_t, + Okl4::uintptr_t, Okl4::uintptr_t, + const Okl4::bi_user_data_t *); + + static int bi_add_phys_mem(Okl4::bi_name_t, + Okl4::uintptr_t, Okl4::uintptr_t, + const Okl4::bi_user_data_t *); + + static int bi_export_object(Okl4::bi_name_t, Okl4::bi_name_t, + Okl4::bi_export_type_t, char *, + Okl4::size_t, + const Okl4::bi_user_data_t *); + + static Okl4::bi_name_t bi_new_ms(Okl4::bi_name_t, + Okl4::uintptr_t, Okl4::uintptr_t, + Okl4::uintptr_t, Okl4::uintptr_t, + Okl4::bi_name_t, Okl4::bi_name_t, + Okl4::bi_name_t, + const Okl4::bi_user_data_t *); + + /******************************** + ** Generic platform interface ** + ********************************/ + + Range_allocator *ram_alloc() { return _core_mem_alloc.phys_alloc(); } + Range_allocator *io_mem_alloc() { return &_io_mem_alloc; } + Range_allocator *io_port_alloc() { return &_io_port_alloc; } + Range_allocator *irq_alloc() { return &_irq_alloc; } + Range_allocator *region_alloc() { return _core_mem_alloc.virt_alloc(); } + Allocator *core_mem_alloc() { return &_core_mem_alloc; } + addr_t vm_start() const { return _vm_start; } + size_t vm_size() const { return _vm_size; } + Rom_fs *rom_fs() { return &_rom_fs; } + + void wait_for_exit(); + + bool supports_direct_unmap() const { return true; } + + + /************************************** + ** OKL4-specific platform interface ** + **************************************/ + + addr_t utcb_base() { return _utcb_base; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-okl4/src/core/include/platform_pd.h b/base-okl4/src/core/include/platform_pd.h new file mode 100644 index 000000000..51dc984db --- /dev/null +++ b/base-okl4/src/core/include/platform_pd.h @@ -0,0 +1,191 @@ +/* + * \brief OKL4-specific protection-domain facility + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_PD_H_ +#define _CORE__INCLUDE__PLATFORM_PD_H_ + +#include + +namespace Okl4 { extern "C" { +#include +} } + +namespace Genode { + + class Platform_thread; + class Platform_pd + { + private: + + friend class Platform_thread; + + enum { PD_INVALID = -1, + PD_FIRST = 0, + PD_MAX = (1 << Thread_id_bits::PD) - 1, + THREAD_MAX = (1 << Thread_id_bits::THREAD) - 1 }; + + unsigned _pd_id; /* plain pd number */ + Platform_thread *_space_pager; /* pager of the new pd */ + + /** + * Manually construct L4 thread ID from its components + */ + static Native_thread_id make_l4_id(unsigned space_no, + unsigned thread_no) + { + /* + * On OKL4, version must be set to 1 + */ + return Okl4::L4_GlobalId((space_no << Thread_id_bits::THREAD) | thread_no, 1); + } + + + /********************************************** + ** Threads of this protection domain object ** + **********************************************/ + + Platform_thread *_threads[THREAD_MAX]; + + /** + * Initialize thread allocator + */ + void _init_threads(); + + /** + * Thread iteration for one task + */ + Platform_thread *_next_thread(); + + /** + * Thread allocation + * + * Again a special case for Core thread0. + */ + int _alloc_thread(int thread_id, Platform_thread *thread); + + /** + * Thread deallocation + * + * No special case for Core thread0 here - we just never call it. + */ + void _free_thread(int thread_id); + + + /****************** + ** PD allocator ** + ******************/ + + struct Pd_alloc + { + unsigned reserved : 1; + unsigned free : 1; + + Pd_alloc(bool r, bool f) + : reserved(r), free(f) { } + + Pd_alloc() : reserved(0), free(0) { } + }; + + static Pd_alloc *_pds() + { + static Pd_alloc static_pds[PD_MAX + 1]; + return static_pds; + } + + /** + * Protection-domain creation + * + * The syscall parameter propagates if any L4 kernel function + * should be used. We need the special case for the Core startup. + */ + void _create_pd(bool syscall); + + /** + * Protection domain destruction + * + * No special case for Core here - we just never call it. + */ + void _destroy_pd(); + + /** + * Protection domain allocation + * + * Find free L4 task and use it. We need the special case for Core + * startup. + */ + int _alloc_pd(signed pd_id); + + /** + * Protection domain deallocation + * + * No special case for Core here - we just never call it. + */ + void _free_pd(); + + /** + * Setup UTCB area + */ + void _setup_address_space(); + + + /*************** + ** Debugging ** + ***************/ + + void _debug_log_pds(void); + void _debug_log_threads(void); + + public: + + /** + * Constructors + */ + Platform_pd(bool core); + Platform_pd(signed pd_id = PD_INVALID, bool create = true); + + /** + * Destructor + */ + ~Platform_pd(); + + /** + * Bind thread to protection domain + * + * \return 0 on success or + * -1 if thread ID allocation failed. + * + * This function allocates the physical L4 thread ID. + */ + int bind_thread(Platform_thread *thread); + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread *thread); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent) { return 0; } + + Platform_thread* space_pager() const { return _space_pager; } + + void space_pager(Platform_thread *pd); + + int pd_id() const { return _pd_id; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ diff --git a/base-okl4/src/core/include/platform_thread.h b/base-okl4/src/core/include/platform_thread.h new file mode 100644 index 000000000..fd48715fb --- /dev/null +++ b/base-okl4/src/core/include/platform_thread.h @@ -0,0 +1,150 @@ +/* + * \brief OKL4 thread facility + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__PLATFORM_THREAD_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Platform_pd; + class Platform_thread + { + private: + + int _thread_id; /* plain thread number */ + Native_thread_id _l4_thread_id; /* L4 thread ID */ + char _name[32]; /* thread name that will be + registered at the kernel + debugger */ + Platform_pd *_platform_pd; /* protection domain thread + is bound to */ + unsigned _priority; /* thread priority */ + Pager_object *_pager; + + public: + + enum { THREAD_INVALID = -1 }; /* invalid thread number */ + enum { DEFAULT_PRIORITY = 128 }; + + /** + * Constructor + */ + Platform_thread(const char *name = 0, + unsigned priority = 0, + int thread_id = THREAD_INVALID); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * \param cpu_no target cpu + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp, unsigned int cpu_no = 0); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * This thread is about to be bound + * + * \param thread_id local thread ID + * \param l4_thread_id final L4 thread ID + * \param pd platform pd, thread is bound to + */ + void bind(int thread_id, Native_thread_id l4_thread_id, + Platform_pd *pd); + + /** + * Unbind this thread + */ + void unbind(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Return/set pager + */ + Pager_object *pager() const { return _pager; } + void pager(Pager_object *pager) { _pager = pager; } + + /** + * Get the 'Platform_pd' object this thread belongs to + */ + Platform_pd* pd() { return _platform_pd; } + + /** + * Return identification of thread when faulting + */ + unsigned long pager_object_badge() const; + + /** + * Set the executing CPU for this thread. + */ + void set_cpu(unsigned int cpu_no); + + + /***************************** + ** OKL4-specific Accessors ** + *****************************/ + + int thread_id() const { return _thread_id; } + Native_thread_id native_thread_id() const { return _l4_thread_id; } + const char *name() const { return _name; } + + void set_l4_thread_id(Native_thread_id id) { _l4_thread_id = id; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-okl4/src/core/include/stdint.h b/base-okl4/src/core/include/stdint.h new file mode 100644 index 000000000..0421e00ca --- /dev/null +++ b/base-okl4/src/core/include/stdint.h @@ -0,0 +1,22 @@ +/* + * \brief Integer types required for using OKL4's boot-info parser + * \author Norman Feske + * \date 2009-04-04 + * + * This file is indirectly included by OKL4's 'bootinfo.h'. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__STDINT_H_ +#define _CORE__INCLUDE__STDINT_H_ + +typedef unsigned long uintptr_t; +typedef signed long intptr_t; + +#endif /* _CORE__INCLUDE__STDINT_H_ */ diff --git a/base-okl4/src/core/include/util.h b/base-okl4/src/core/include/util.h new file mode 100644 index 000000000..e6cb6f494 --- /dev/null +++ b/base-okl4/src/core/include/util.h @@ -0,0 +1,131 @@ +/* + * \brief OKL4 utilities + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__UTIL_H_ +#define _CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +} } + +/* + * The binding for 'L4_KDB_Enter' on ARM takes a 'char *' as argument, which + * prevents us from passing a plain const "string". However, on x86, the + * binding is a preprocessor macro accepting only a "string" as argument. + * Unless the OKL4 bindings get fixed, we have to handle both cases separately. + */ +#ifdef __L4__X86__KDEBUG_H__ +#define ENTER_KDB(msg) L4_KDB_Enter(msg); +#else +#define ENTER_KDB(msg) L4_KDB_Enter((char *)msg); +#endif + + +namespace Genode { + + inline void log_event(const char *s) { } + inline void log_event(const char *s, unsigned v1, unsigned v2, unsigned v3) { } + + inline void panic(const char *s) + { + using namespace Okl4; + PDBG("Panic: %s", s); + ENTER_KDB("> panic <"); + } + + inline void assert(const char *s, bool val) + { + using namespace Okl4; + if (!val) { + PERR("Assertion failed: %s", s); + ENTER_KDB("Assertion failed"); + } + } + + inline size_t get_page_size_log2() { return 12; } + inline size_t get_page_size() { return 1 << get_page_size_log2(); } + inline addr_t get_page_mask() { return ~(get_page_size() - 1); } + + inline size_t get_super_page_size_log2() + { + enum { SUPER_PAGE_SIZE_LOG2 = 22 }; + if (get_page_mask() & (1 << SUPER_PAGE_SIZE_LOG2)) + return SUPER_PAGE_SIZE_LOG2; + + /* if super pages are not supported, return default page size */ + return get_page_size(); + } + + inline void touch_ro(const void *addr, unsigned size) + { + using namespace Okl4; + unsigned char const volatile *bptr; + unsigned char const *eptr; + L4_Word_t mask = get_page_mask(); + L4_Word_t psize = get_page_size(); + + bptr = (unsigned char const volatile *)(((unsigned)addr) & mask); + eptr = (unsigned char const *)(((unsigned)addr + size - 1) & mask); + for ( ; bptr <= eptr; bptr += psize) + touch_read(bptr); + } + + inline void touch_rw(const void *addr, unsigned size) + { + using namespace Okl4; + unsigned char volatile *bptr; + unsigned char const *eptr; + L4_Word_t mask = get_page_mask(); + L4_Word_t psize = get_page_size(); + + bptr = (unsigned char volatile *)(((unsigned)addr) & mask); + eptr = (unsigned char const *)(((unsigned)addr + size - 1) & mask); + for(; bptr <= eptr; bptr += psize) + touch_read_write(bptr); + } + + inline addr_t trunc_page(addr_t page) + { + return page & get_page_mask(); + } + + inline addr_t round_page(addr_t page) + { + return trunc_page(page + get_page_size() - 1); + } + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long faulter_badge) + { + printf("%s (%s pf_addr=%p pf_ip=%p from %02lx)\n", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, + faulter_badge); + } + + inline addr_t map_src_addr(addr_t core_local, addr_t phys) { return phys; } + + inline size_t constrain_map_size_log2(size_t size_log2) { return size_log2; } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-okl4/src/core/io_mem_session_support.cc b/base-okl4/src/core/io_mem_session_support.cc new file mode 100644 index 000000000..3e3091e26 --- /dev/null +++ b/base-okl4/src/core/io_mem_session_support.cc @@ -0,0 +1,27 @@ +/* + * \brief OKL4-specific implementation of the IO_MEM session interface + * \author Norman Feske + * \date 2009-03-29 + * + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include + + +using namespace Genode; + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ } + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ return 0; } diff --git a/base-okl4/src/core/irq_session_component.cc b/base-okl4/src/core/irq_session_component.cc new file mode 100644 index 000000000..c9b8e2ff9 --- /dev/null +++ b/base-okl4/src/core/irq_session_component.cc @@ -0,0 +1,316 @@ +/* + * \brief OKL4-specific implementation of IRQ sessions + * \author Norman Feske + * \author Christian Helmuth + * \date 2009-12-15 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +#include +#include +#include +} } + +using namespace Genode; +using namespace Okl4; + + +/* XXX move this functionality to a central place instead of duplicating it */ +static inline Okl4::L4_ThreadId_t thread_get_my_global_id() +{ + Okl4::L4_ThreadId_t myself; + myself.raw = Okl4::__L4_TCR_ThreadWord(UTCB_TCR_THREAD_WORD_MYSELF); + return myself; +} + + +/****************************** + ** Shared-interrupt support ** + ******************************/ + +class Irq_blocker : public List::Element +{ + private: + + Lock _wait_lock; + + public: + + Irq_blocker() : _wait_lock(Lock::LOCKED) { } + + void block() { _wait_lock.lock(); } + void unblock() { _wait_lock.unlock(); } +}; + + +/* + * Proxy thread that associates to the interrupt and unblocks waiting irqctrl + * threads. Maybe, we should utilize our signals for interrupt delivery... + * + * XXX resources are not accounted as the interrupt is shared + */ +class Irq_proxy : public Thread<0x1000>, + public List::Element +{ + private: + + char _name[32]; + Lock _startup_lock; + + long _irq_number; + + Lock _mutex; /* protects this object */ + int _num_sharers; /* number of clients sharing this IRQ */ + Semaphore _sleep; /* wake me up if aspired blockers return */ + List _blocker_list; + int _num_blockers; /* number of currently blocked clients */ + bool _woken_up; /* client decided to wake me up - + this prevents multiple wakeups + to happen during initialization */ + + const char *_construct_name(long irq_number) + { + snprintf(_name, sizeof(_name), "irqproxy%02lx", irq_number); + return _name; + } + + bool _associate() + { + /* allow roottask (ourself) to handle the interrupt */ + L4_LoadMR(0, _irq_number); + int ret = L4_AllowInterruptControl(L4_rootspace); + if (ret != 1) { + PERR("L4_AllowInterruptControl returned %d, error code=%ld\n", + ret, L4_ErrorCode()); + return false; + } + + /* bit to use for IRQ notifications */ + enum { IRQ_NOTIFY_BIT = 13 }; + + /* + * Note: 'L4_Myself()' does not work for the thread argument of + * 'L4_RegisterInterrupt'. We have to specify our global ID. + */ + L4_LoadMR(0, _irq_number); + ret = L4_RegisterInterrupt(thread_get_my_global_id(), IRQ_NOTIFY_BIT, 0, 0); + if (ret != 1) { + PERR("L4_RegisterInterrupt returned %d, error code=%ld\n", + ret, L4_ErrorCode()); + return false; + } + + /* prepare ourself to receive asynchronous IRQ notifications */ + L4_Set_NotifyMask(1 << IRQ_NOTIFY_BIT); + L4_Accept(L4_NotifyMsgAcceptor); + + return true; + } + + void _loop() + { + /* wait for first blocker */ + _sleep.down(); + + while (1) { + /* wait for asynchronous interrupt notification */ + L4_ThreadId_t partner = L4_nilthread; + L4_ReplyWait(partner, &partner); + + { + Lock::Guard lock_guard(_mutex); + + /* inform blocked clients */ + Irq_blocker *b; + while ((b = _blocker_list.first())) { + _blocker_list.remove(b); + b->unblock(); + } + + /* reset blocker state */ + _num_blockers = 0; + _woken_up = false; + } + + /* + * We must wait for all clients to ack their interrupt, + * otherwise level-triggered interrupts will occur immediately + * after acknowledgement. That's an inherent security problem + * with shared IRQs and induces problems with dynamic driver + * load and unload. + */ + _sleep.down(); + + /* acknowledge previous interrupt */ + L4_LoadMR(0, _irq_number); + L4_AcknowledgeInterrupt(0, 0); + } + } + + public: + + Irq_proxy(long irq_number) + : + Thread<0x1000>(_construct_name(irq_number)), + _startup_lock(Lock::LOCKED), _irq_number(irq_number), + _mutex(Lock::UNLOCKED), _num_sharers(0), _num_blockers(0), _woken_up(false) + { + start(); + _startup_lock.lock(); + } + + /** + * Thread interface + */ + void entry() + { + if (_associate()) { + _startup_lock.unlock(); + _loop(); + } + } + + /** + * Block until interrupt occured + */ + void wait_for_irq() + { + Irq_blocker blocker; + { + Lock::Guard lock_guard(_mutex); + + _blocker_list.insert(&blocker); + _num_blockers++; + + /* + * The proxy thread is woken up if no client woke it up before + * and this client is the last aspired blocker. + */ + if (!_woken_up && _num_blockers == _num_sharers) { + _sleep.up(); + _woken_up = true; + } + } + blocker.block(); + } + + long irq_number() const { return _irq_number; } + + void add_sharer() + { + Lock::Guard lock_guard(_mutex); + ++_num_sharers; + } +}; + + +static Irq_proxy *get_irq_proxy(long irq_number, Range_allocator *irq_alloc = 0) +{ + static List proxies; + static Lock proxies_lock; + + Lock::Guard lock_guard(proxies_lock); + + /* lookup proxy in database */ + for (Irq_proxy *p = proxies.first(); p; p = p->next()) + if (p->irq_number() == irq_number) + return p; + + /* try to create proxy */ + if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK) + return 0; + + Irq_proxy *new_proxy = new (env()->heap()) Irq_proxy(irq_number); + proxies.insert(new_proxy); + + return new_proxy; +} + + +/*************************** + ** IRQ session component ** + ***************************/ + +bool Irq_session_component::Irq_control_component::associate_to_irq(unsigned irq) +{ + return true; +} + + +void Irq_session_component::wait_for_irq() +{ + /* block at interrupt proxy */ + Irq_proxy *p = get_irq_proxy(_irq_number); + if (!p) { + PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number); + return; + } + + p->wait_for_irq(); + + /* interrupt ocurred and proxy woke us up */ +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _ep(cap_session, STACK_SIZE, "irqctrl"), + _irq_attached(false), + _control_client(Capability()) +{ + /* + * XXX Removed irq_shared argument as this is the default now. If we need + * exclusive later on, we should add this as new argument. + */ + + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + if (irq_number == -1) { + PERR("invalid IRQ number requested"); + + throw Root::Unavailable(); + } + + /* check if IRQ thread was started before */ + Irq_proxy *irq_proxy = get_irq_proxy(irq_number, irq_alloc); + if (!irq_proxy) { + PERR("unavailable IRQ %lx requested", irq_number); + + throw Root::Unavailable(); + } + + irq_proxy->add_sharer(); + _irq_number = irq_number; + + /* initialize capability */ + _irq_cap = _ep.manage(this); +} + + +Irq_session_component::~Irq_session_component() +{ + PERR("not yet implemented"); + /* TODO del_sharer() resp. put_sharer() */ +} + diff --git a/base-okl4/src/core/okl4_pd_session_component.cc b/base-okl4/src/core/okl4_pd_session_component.cc new file mode 100644 index 000000000..48bcaacfa --- /dev/null +++ b/base-okl4/src/core/okl4_pd_session_component.cc @@ -0,0 +1,28 @@ +/* + * \brief Core implementation of the PD session interface extension + * \author Stefan Kalkowski + * \date 2009-06-21 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include +#include + +using namespace Genode; + + +void Pd_session_component::space_pager(Thread_capability thread) +{ + Cpu_thread_component *cpu_thread = dynamic_cast + (_thread_ep->obj_by_cap(thread)); + if (!cpu_thread) return; + _pd.space_pager(cpu_thread->platform_thread()); +} diff --git a/base-okl4/src/core/platform.cc b/base-okl4/src/core/platform.cc new file mode 100644 index 000000000..7836e1a3a --- /dev/null +++ b/base-okl4/src/core/platform.cc @@ -0,0 +1,318 @@ +/* + * \brief OKL4 platform interface implementation + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include +#include + +/* OKL4 includes */ +namespace Okl4 { +#include +#include +} + +using namespace Genode; + + +static const bool verbose_boot_info = false; + +enum { MAX_BOOT_MODULES = 64 }; +enum { MAX_BOOT_MODULE_NAME_LEN = 32 }; +static struct +{ + char name[MAX_BOOT_MODULE_NAME_LEN]; + addr_t base; + size_t size; +} boot_modules[MAX_BOOT_MODULES]; + +static int num_boot_module_memsects; +static int num_boot_module_objects; + + +/**************************************** + ** Support for core memory management ** + ****************************************/ + +bool Core_mem_allocator::Mapped_mem_allocator::_map_local(addr_t virt_addr, + addr_t phys_addr, + unsigned size_log2) +{ + return map_local(phys_addr, virt_addr, 1 << (size_log2 - get_page_size_log2())); +} + + +/********************** + ** Boot-info parser ** + **********************/ + +int Platform::bi_init_mem(Okl4::uintptr_t virt_base, Okl4::uintptr_t virt_end, + Okl4::uintptr_t phys_base, Okl4::uintptr_t phys_end, + const Okl4::bi_user_data_t *data) +{ + if (verbose_boot_info) + printf("init_mem: virt=[%08lx,%08lx), phys=[%08lx,%08lx)\n", + virt_base, virt_end, phys_base, phys_end); + + Platform *p = (Platform *)data->user_data; + p->_core_mem_alloc.phys_alloc()->add_range(phys_base, phys_end - phys_base + 1); + p->_core_mem_alloc.virt_alloc()->add_range(virt_base, virt_end - virt_base + 1); + return 0; +} + + +int Platform::bi_add_virt_mem(Okl4::bi_name_t pool, Okl4::uintptr_t base, + Okl4::uintptr_t end, const Okl4::bi_user_data_t *data) +{ + if (verbose_boot_info) + printf("add_virt_mem: pool=%d region=[0x%08lx,0x%08lx], %ld pages\n", + pool, base, end, (end - base + 1)/4096); + + /* prevent first page from being added to core memory */ + if (base < get_page_size() || end < get_page_size()) + return 0; + + Platform *p = (Platform *)data->user_data; + p->_core_mem_alloc.virt_alloc()->add_range(base, end - base + 1); + return 0; +} + + +int Platform::bi_add_phys_mem(Okl4::bi_name_t pool, Okl4::uintptr_t base, + Okl4::uintptr_t end, const Okl4::bi_user_data_t *data) +{ + if (verbose_boot_info) + printf("add_phys_mem: pool=%d region=[0x%08lx,0x%08lx], %ld pages\n", + pool, base, end, (end - base + 1)/4096); + + if (pool == 2) { + Platform *p = (Platform *)data->user_data; + p->_core_mem_alloc.phys_alloc()->add_range(base, end - base + 1); + } + return 0; +} + + +int Platform::bi_export_object(Okl4::bi_name_t pd, Okl4::bi_name_t obj, + Okl4::bi_export_type_t export_type, char *key, + Okl4::size_t key_len, const Okl4::bi_user_data_t * data) +{ + if (verbose_boot_info) + printf("export_object: pd=%d obj=%d type=%d key=\"%s\"\n", + pd, obj, export_type, key); + + /* + * We walk the boot info only once and collect all memory section + * objects. Each time we detect a memory section outside of roottask + * (PD 0), we increment the boot module index. + */ + + /* reset module index (roottask objects appear before other pd's objects) */ + if (pd == 0) num_boot_module_objects = 0; + + if (export_type != Okl4::BI_EXPORT_MEMSECTION_CAP) + return 0; + + if (num_boot_module_objects >= MAX_BOOT_MODULES) { + PERR("Maximum number of boot modules exceeded"); + return -1; + } + + /* copy name from object key */ + key_len = min((int)key_len, MAX_BOOT_MODULE_NAME_LEN - 1); + for (unsigned i = 0; i < key_len; i++) { + + /* convert letter to lower-case */ + char c = key[i]; + if (c >= 'A' && c <= 'Z') + c -= 'A' - 'a'; + + boot_modules[num_boot_module_objects].name[i] = c; + } + /* null-terminate string */ + boot_modules[num_boot_module_objects].name[key_len] = 0; + + num_boot_module_objects++; + return 0; +} + + +Okl4::bi_name_t Platform::bi_new_ms(Okl4::bi_name_t owner, + Okl4::uintptr_t base, Okl4::uintptr_t size, + Okl4::uintptr_t flags, Okl4::uintptr_t attr, + Okl4::bi_name_t physpool, Okl4::bi_name_t virtpool, + Okl4::bi_name_t zone, const Okl4::bi_user_data_t *data) +{ + if (verbose_boot_info) + printf("new_ms: owner=%d region=[%lx,%lx), flags=%lx, attr=%lx, physpool=%d, virtpool=%d, zone=%d\n", + owner, base, base + size - 1, flags, attr, physpool, virtpool, zone); + + /* reset module index (see comment in 'bi_export_object') */ + if (owner == 0) num_boot_module_memsects = 0; + + /* ignore memory pools other than pool 3 (this is just a heuristic) */ + if (virtpool != 3) return 0; + + if (num_boot_module_memsects >= MAX_BOOT_MODULES) { + PERR("Maximum number of boot modules exceeded"); + return -1; + } + + boot_modules[num_boot_module_memsects].base = base; + boot_modules[num_boot_module_memsects].size = size; + + num_boot_module_memsects++; + return 0; +} + + +Platform::Platform() : + _io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()), + _irq_alloc(core_mem_alloc()) +{ + /* + * We must be single-threaded at this stage and so this is safe. + */ + static bool initialized = 0; + if (initialized) panic("Platform constructed twice!"); + initialized = true; + + /* + * Determine address of boot-info structure. On startup, the OKL4 kernel + * provides this address in roottask's UTCB message register 1. + */ + Okl4::L4_Word_t boot_info_addr; + Okl4::L4_StoreMR(1, &boot_info_addr); + + /* + * Request base address for UTCB locations + */ + _utcb_base = (addr_t)Okl4::utcb_base_get(); + + /* + * Define our own thread ID + */ + Okl4::__L4_TCR_Set_ThreadWord(UTCB_TCR_THREAD_WORD_MYSELF, Okl4::L4_rootserver.raw); + + /* + * By default, the first roottask thread is executed at maxiumum priority. + * To make preemptive scheduler work as expected, we set the priority of + * ourself to the default priority of all other threads, which is 100 on + * OKL4. + */ + L4_Set_Priority(Okl4::L4_Myself(), Platform_thread::DEFAULT_PRIORITY); + + /* + * Invoke boot-info parser for determining the memory configuration and + * the location of the boot modules. + */ + + printf("parsing boot info at 0x%p...\n", (void *)boot_info_addr); + + /* + * Initialize callback function for parsing the boot-info + * + * The supplied callback functions differ slightly from the interface + * used by the boot-info library in that they do not have a return + * type. + */ + static Okl4::bi_callbacks_t callbacks; + callbacks.init_mem = Platform::bi_init_mem; + callbacks.add_virt_mem = Platform::bi_add_virt_mem; + callbacks.add_phys_mem = Platform::bi_add_phys_mem; + callbacks.export_object = Platform::bi_export_object; + callbacks.new_ms = Platform::bi_new_ms; + + Okl4::bootinfo_parse((void *)boot_info_addr, &callbacks, this); + + /* make gathered boot-module info known to '_rom_fs' */ + int num_boot_modules = min(num_boot_module_objects, num_boot_module_memsects); + for (int i = 0; i < num_boot_modules; i++) { + Rom_module *r = new (core_mem_alloc()) + Rom_module(boot_modules[i].base, + boot_modules[i].size, + boot_modules[i].name); + _rom_fs.insert(r); + } + + /* initialize interrupt allocator */ + _irq_alloc.add_range(0, 0x10); + + /* I/O memory could be the whole user address space */ + _io_mem_alloc.add_range(0, ~0); + + /* I/O port allocator (only meaningful for x86) */ + _io_port_alloc.add_range(0, 0x10000); + + /* preserve context area in core's virtual address space */ + _core_mem_alloc.virt_alloc()->remove_range(Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + Thread_base::CONTEXT_AREA_VIRTUAL_SIZE); + + _vm_start = 0x1000; + _vm_size = 0xb0000000 - 0x1000; + + /* + * When dumping 'ram_alloc', there are several small blocks in addition + * to the available free memory visible. These small blocks are used to + * hold the meta data for the ROM modules as initialized by '_setup_rom'. + */ + if (verbose_boot_info) { + printf(":phys_alloc: "); _core_mem_alloc.phys_alloc()->raw()->dump_addr_tree(); + printf(":virt_alloc: "); _core_mem_alloc.virt_alloc()->raw()->dump_addr_tree(); + printf(":io_mem: "); _io_mem_alloc.raw()->dump_addr_tree(); + printf(":io_port: "); _io_port_alloc.raw()->dump_addr_tree(); + printf(":irq: "); _irq_alloc.raw()->dump_addr_tree(); + printf(":rom_fs: "); _rom_fs.print_fs(); + } + + /* setup task object for core task */ + _core_pd = new(core_mem_alloc()) Platform_pd(true); + + /* + * We setup the thread object for thread0 in core task using a special + * interface that allows us to specify the thread ID. For core this creates + * the situation that task_id == thread_id of first task. But since we do + * not destroy this task, it should be no problem. + */ + Platform_thread *core_thread = + new(core_mem_alloc()) Platform_thread("core.main"); + + core_thread->set_l4_thread_id(Okl4::L4_rootserver); + + _core_pd->bind_thread(core_thread); +} + + +/******************************** + ** Generic platform interface ** + ********************************/ + +void Platform::wait_for_exit() +{ + /* + * On OKL4, core never exits. So let us sleep forever. + */ + sleep_forever(); +} + + +void Core_parent::exit(int exit_value) { } diff --git a/base-okl4/src/core/platform_pd.cc b/base-okl4/src/core/platform_pd.cc new file mode 100644 index 000000000..0adcacc5e --- /dev/null +++ b/base-okl4/src/core/platform_pd.cc @@ -0,0 +1,315 @@ +/* + * \brief OKL4-specific protection-domain facility + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +} } + +using namespace Genode; + + +static const bool verbose = false; + + +/**************************** + ** Private object members ** + ****************************/ + +void Platform_pd::_create_pd(bool syscall) +{ + using namespace Okl4; + + if (!syscall) + return; + + L4_Word_t control = L4_SpaceCtrl_new; + L4_ClistId_t cap_list = L4_rootclist; + L4_Word_t utcb_area_size = L4_GetUtcbSize()*(1 << Thread_id_bits::THREAD); + L4_Word_t utcb_location = platform_specific()->utcb_base() + + _pd_id*utcb_area_size; + L4_Fpage_t utcb_area = L4_Fpage(utcb_location, utcb_area_size); + L4_Word_t resources = 0; + L4_Word_t old_resources = 0; + +#ifdef NO_UTCB_RELOCATE + utcb_area = L4_Nilpage; /* UTCB allocation is handled by the kernel */ +#endif + + /* create address space */ + int ret = L4_SpaceControl(L4_SpaceId(_pd_id), control, cap_list, utcb_area, + resources, &old_resources); + + if (ret != 1) + PERR("L4_SpaceControl(new) returned %d, error code=%d", + ret, (int)L4_ErrorCode()); +} + + +void Platform_pd::_destroy_pd() +{ + using namespace Okl4; + + L4_Word_t control = L4_SpaceCtrl_delete; + L4_ClistId_t cap_list = L4_rootclist; + L4_Word_t utcb_area_size = L4_GetUtcbSize()*(1 << Thread_id_bits::THREAD); + L4_Word_t utcb_location = platform_specific()->utcb_base() + + _pd_id*utcb_area_size; + L4_Fpage_t utcb_area = L4_Fpage(utcb_location, utcb_area_size); + L4_Word_t resources = 0; + L4_Word_t old_resources = 0; + +#ifdef NO_UTCB_RELOCATE + utcb_area = L4_Nilpage; /* UTCB allocation is handled by the kernel */ +#endif + + int ret = L4_SpaceControl(L4_SpaceId(_pd_id), control, cap_list, utcb_area, + resources, &old_resources); + + if (ret != 1) + PERR("L4_SpaceControl(delete) returned %d, error code=%d", + ret, (int)L4_ErrorCode()); +} + + +int Platform_pd::_alloc_pd(signed pd_id) +{ + if (pd_id == PD_INVALID) { + unsigned i; + + for (i = PD_FIRST; i <= PD_MAX; i++) + if (_pds()[i].free) break; + + /* no free protection domains available */ + if (i > PD_MAX) return -1; + + pd_id = i; + + } else { + if (!_pds()[pd_id].reserved || !_pds()[pd_id].free) + return -1; + } + + _pds()[pd_id].free = 0; + + _pd_id = pd_id; + + return pd_id; +} + + +void Platform_pd::_free_pd() +{ + unsigned id = _pd_id; + + if (_pds()[id].free) return; + + _pds()[id].free = 1; +} + + +void Platform_pd::_init_threads() +{ + unsigned i; + + for (i = 0; i < THREAD_MAX; ++i) + _threads[i] = 0; +} + + +Platform_thread* Platform_pd::_next_thread() +{ + unsigned i; + + /* look for bound thread */ + for (i = 0; i < THREAD_MAX; ++i) + if (_threads[i]) break; + + /* no bound threads */ + if (i == THREAD_MAX) return 0; + + return _threads[i]; +} + + +int Platform_pd::_alloc_thread(int thread_id, Platform_thread *thread) +{ + int i = thread_id; + + /* look for free thread */ + if (thread_id == Platform_thread::THREAD_INVALID) { + for (i = 0; i < THREAD_MAX; ++i) + if (!_threads[i]) break; + + /* no free threads available */ + if (i == THREAD_MAX) return -1; + } else { + if (_threads[i]) return -2; + } + + _threads[i] = thread; + + return i; +} + + +void Platform_pd::_free_thread(int thread_id) +{ + if (!_threads[thread_id]) + PWRN("double-free of thread %x.%x detected", _pd_id, thread_id); + + _threads[thread_id] = 0; +} + + +/*************************** + ** Public object members ** + ***************************/ + +int Platform_pd::bind_thread(Platform_thread *thread) +{ + using namespace Okl4; + + /* thread_id is THREAD_INVALID by default - only core is the special case */ + int thread_id = thread->thread_id(); + L4_ThreadId_t l4_thread_id; + + int t = _alloc_thread(thread_id, thread); + if (t < 0) { + PERR("thread alloc failed"); + return -1; + } + thread_id = t; + l4_thread_id = make_l4_id(_pd_id, thread_id); + + /* finally inform thread about binding */ + thread->bind(thread_id, l4_thread_id, this); + return 0; +} + + +void Platform_pd::unbind_thread(Platform_thread *thread) +{ + int thread_id = thread->thread_id(); + + /* unbind thread before proceeding */ + thread->unbind(); + + _free_thread(thread_id); + + if (verbose) _debug_log_threads(); +} + + +void Platform_pd::space_pager(Platform_thread *thread) +{ + using namespace Okl4; + + L4_Word_t control = L4_SpaceCtrl_space_pager; + L4_SpaceId_t pager_space = L4_SpaceId(thread->pd()->pd_id()); + L4_ClistId_t cap_list = L4_rootclist; + L4_Word_t utcb_area_size = L4_GetUtcbSize()*(1 << Thread_id_bits::THREAD); + L4_Word_t utcb_location = platform_specific()->utcb_base() + + _pd_id*utcb_area_size; + L4_Fpage_t utcb_area = L4_Fpage(utcb_location, utcb_area_size); + L4_Word_t resources = 0; + L4_Word_t old_resources = 0; + + /* set the space pager */ + _space_pager = thread; + L4_LoadMR(0,pager_space.raw); + int ret = L4_SpaceControl(L4_SpaceId(_pd_id), control, cap_list, utcb_area, + resources, &old_resources); + + if (ret != 1) + PERR("L4_SpaceControl(new space_pager...) returned %d, error code=%d", + ret, (int)L4_ErrorCode()); + + /* grant the pager mapping rights regarding this space */ + if(!L4_AllowUserMapping(pager_space, 0x0, 0xff000000)) + PERR("Failed to delegate pt access to %lx, error %lx", + pager_space.raw, L4_ErrorCode()); +} + + +void Platform_pd::_setup_address_space() +{ + PERR("not yet implemented"); +} + + +Platform_pd::Platform_pd(bool core) +: _space_pager(0) +{ + /* init remainder */ + Pd_alloc free(false, true); + for (unsigned i = 0 ; i <= PD_MAX; ++i) _pds()[i] = free; + + _init_threads(); + + _pd_id = _alloc_pd(PD_INVALID); + + _create_pd(false); +} + + +Platform_pd::Platform_pd(signed pd_id, bool create) +: _space_pager(0) +{ + if (!create) + panic("create must be true."); + + _init_threads(); + + _pd_id = _alloc_pd(pd_id); + + if (_pd_id > PD_MAX) + PERR("pd alloc failed"); + + _create_pd(create); +} + + +Platform_pd::~Platform_pd() +{ + /* unbind all threads */ + while (Platform_thread *t = _next_thread()) unbind_thread(t); + + _destroy_pd(); + _free_pd(); +} + + +/*********************** + ** Debugging support ** + ***********************/ + +void Platform_pd::_debug_log_threads() +{ + PWRN("_debug_log_threads disabled."); +} + + +void Platform_pd::_debug_log_pds() +{ + PWRN("_debug_log_pds disabled."); +} diff --git a/base-okl4/src/core/platform_thread.cc b/base-okl4/src/core/platform_thread.cc new file mode 100644 index 000000000..7eee4cb5c --- /dev/null +++ b/base-okl4/src/core/platform_thread.cc @@ -0,0 +1,197 @@ +/* + * \brief OKL4 thread facility + * \author Julian Stecklina + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2008-03-19 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +#include +#include +#include +} } + +using namespace Genode; +using namespace Okl4; + + +void Platform_thread::set_cpu(unsigned int cpu_no) +{ + PERR("not yet implemented"); +} + + +int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no) +{ + if (!_platform_pd) { + PWRN("thread %d is not bound to a PD", _thread_id); + return -1; + } + + /* activate local thread by assigning a UTCB address and thread ID */ + int space_no = _platform_pd->pd_id(); + L4_ThreadId_t new_thread_id = _platform_pd->make_l4_id(space_no, + _thread_id); + L4_SpaceId_t space_id = L4_SpaceId(space_no); + L4_ThreadId_t scheduler = L4_rootserver; + L4_ThreadId_t pager = _pager ? _pager->cap().tid() : L4_nilthread; + L4_ThreadId_t exception_handler = pager; + L4_Word_t resources = 0; + L4_Word_t utcb_size_per_task = L4_GetUtcbSize()*(1 << Thread_id_bits::THREAD); + L4_Word_t utcb_location = platform_specific()->utcb_base() + + _platform_pd->pd_id()*utcb_size_per_task + + _thread_id*L4_GetUtcbSize(); + /* + * On some ARM architectures, UTCBs are allocated by the kernel. + * In this case, we need to specify -1 as UTCB location to prevent + * the thread creation to fail with an 'L4_ErrUtcbArea' error. + */ +#ifdef NO_UTCB_RELOCATE + utcb_location = ~0; +#endif + + /* + * If a pager for the PD was set before, we will use it as the pager + * of this thread. + * + * Note: This is used by OKLinux only + */ + if(_platform_pd && _platform_pd->space_pager()) { + pager = _platform_pd->space_pager()->_l4_thread_id; + exception_handler = pager; + } + + int ret = L4_ThreadControl(new_thread_id, + space_id, + scheduler, pager, exception_handler, + resources, (void *)utcb_location); + if (ret != 1) { + PERR("L4_ThreadControl returned %d, error code=%d", + ret, (int)L4_ErrorCode()); + return -1; + } + + /* make the symbolic thread name known to the kernel debugger */ + L4_KDB_SetThreadName(new_thread_id, _name); + + /* let the new thread know its global thread id */ + L4_Set_UserDefinedHandleOf(new_thread_id, new_thread_id.raw); + + /* + * Don't start if ip and sp are set invalid. + * + * Note: This quirk is only used by OKLinux + */ + if((L4_Word_t)sp != 0xffffffff || (L4_Word_t)ip != 0xffffffff) + L4_Start_SpIp(new_thread_id, (L4_Word_t)sp, (L4_Word_t)ip); + + /* assign priority */ + if (!L4_Set_Priority(new_thread_id, + Cpu_session::scale_priority(DEFAULT_PRIORITY, _priority))) + PWRN("Could not set thread prioritry to default"); + + set_l4_thread_id(new_thread_id); + return 0; +} + + +void Platform_thread::pause() +{ + L4_SuspendThread(_l4_thread_id); +} + + +void Platform_thread::resume() +{ + L4_UnsuspendThread(_l4_thread_id); +} + + +void Platform_thread::bind(int thread_id, L4_ThreadId_t l4_thread_id, + Platform_pd *pd) +{ + _thread_id = thread_id; + _l4_thread_id = l4_thread_id; + _platform_pd = pd; +} + + +void Platform_thread::unbind() +{ + L4_Word_t res = L4_ThreadControl(_l4_thread_id, L4_nilspace, + L4_nilthread, L4_nilthread, L4_nilthread, ~0, 0); + + if (res != 1) + PERR("Deleting thread 0x%08lx failed. Continuing...", _l4_thread_id.raw); + + _thread_id = THREAD_INVALID; + _l4_thread_id = L4_nilthread; + _platform_pd = 0; +} + + +void Platform_thread::cancel_blocking() +{ + L4_Word_t dummy; + L4_ThreadId_t dummy_tid; + + /* + * For more details, please refer to the corresponding implementation in + * the 'base-pistachio' repository. + */ + + /* reset value for the thread's user-defined handle */ + enum { USER_DEFINED_HANDLE_ZERO = 0 }; + + L4_ExchangeRegisters(_l4_thread_id, + L4_ExReg_Resume | L4_ExReg_AbortOperation | L4_ExReg_user, + 0, 0, 0, USER_DEFINED_HANDLE_ZERO, L4_nilthread, + &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy_tid); +} + + +unsigned long Platform_thread::pager_object_badge() const +{ + return native_thread_id().raw; +} + + +Platform_thread::Platform_thread(const char *name, unsigned prio, int thread_id) +: _thread_id(thread_id), _l4_thread_id(L4_nilthread), _platform_pd(0), + _priority(prio), _pager(0) +{ + strncpy(_name, name, sizeof(_name)); +} + + +Platform_thread::~Platform_thread() +{ + /* + * We inform our protection domain about thread destruction, which will end up in + * Thread::unbind() + */ + if (_platform_pd) + _platform_pd->unbind_thread(this); +} diff --git a/base-okl4/src/core/ram_session_support.cc b/base-okl4/src/core/ram_session_support.cc new file mode 100644 index 000000000..be396e362 --- /dev/null +++ b/base-okl4/src/core/ram_session_support.cc @@ -0,0 +1,66 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Norman Feske + * \date 2006-07-03 + * + * On L4, each dataspace _is_ a shared memory object. + * Therefore, these functions are empty. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include /* needed for 'L4_ErrorCode' */ +} } + +using namespace Genode; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { } + +void Ram_session_component::_clear_ds (Dataspace_component *ds) +{ + size_t page_rounded_size = (ds->size() + get_page_size() - 1) & get_page_mask(); + + /* allocate range in core's virtual address space */ + void *virt_addr; + if (!platform()->region_alloc()->alloc(page_rounded_size, &virt_addr)) { + PERR("could not allocate virtual address range in core of size %zd\n", + page_rounded_size); + return; + } + + /* map the dataspace's physical pages to corresponding virtual addresses */ + size_t num_pages = page_rounded_size >> get_page_size_log2(); + if (!map_local(ds->phys_addr(), (addr_t)virt_addr, num_pages)) { + PERR("core-local memory mapping failed, Error Code=%d\n", (int)Okl4::L4_ErrorCode()); + return; + } + + /* clear dataspace */ + size_t num_longwords = page_rounded_size/sizeof(long); + for (long *dst = (long *)virt_addr; num_longwords--;) + *dst++ = 0; + + /* unmap dataspace from core */ + if (!unmap_local((addr_t)virt_addr, num_pages)) + PERR("could not unmap core-local address range at %p (Error Code %ld)", + virt_addr, Okl4::L4_ErrorCode()); + + /* free core's virtual address space */ + platform()->region_alloc()->free(virt_addr, page_rounded_size); +} diff --git a/base-okl4/src/core/rm_session_support.cc b/base-okl4/src/core/rm_session_support.cc new file mode 100644 index 000000000..e2c98403e --- /dev/null +++ b/base-okl4/src/core/rm_session_support.cc @@ -0,0 +1,90 @@ +/* + * \brief OKL4-specific part of RM-session implementation + * \author Norman Feske + * \date 2009-04-10 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +#include +} } + +using namespace Genode; +using namespace Okl4; + +static const bool verbose_unmap = false; + + +static void unmap_log2_range(L4_SpaceId_t space_id, L4_Word_t base, L4_Word_t size_log2) +{ + L4_Fpage_t fpage = L4_FpageLog2(base, size_log2); + L4_FpageAddRightsTo(&fpage, L4_FullyAccessible); + int ret = L4_UnmapFpage(space_id, fpage); + if (ret != 1) + PERR("could not unmap page at %p from space %lx (Error Code %ld)", + (void *)base, space_id.raw, L4_ErrorCode()); +} + + +void Rm_client::unmap(addr_t, addr_t virt_base, size_t size) +{ + L4_ThreadId_t tid = { raw : badge() }; + L4_SpaceId_t space_id = { raw: L4_ThreadNo(tid) >> Thread_id_bits::THREAD }; + L4_Word_t addr = virt_base; + L4_Word_t remaining_size = size; + L4_Word_t size_log2 = get_page_size_log2(); + + if (verbose_unmap) + printf("RM client %p (%lx) unmap [%lx,%lx)\n", + this, badge(), virt_base, virt_base + size); + + /* + * Let unmap granularity ('size_log2') grow + */ + while (remaining_size >= (1UL << size_log2)) { + + enum { SIZE_LOG2_MAX = 22 /* 4M */ }; + + /* issue 'unmap' for the current address if flexpage aligned */ + if (addr & (1 << size_log2)) { + unmap_log2_range(space_id, addr, size_log2); + + remaining_size -= 1 << size_log2; + addr += 1 << size_log2; + } + + /* increase flexpage size */ + size_log2++; + } + + /* + * Let unmap granularity ('size_log2') shrink + */ + while (remaining_size > 0) { + + if (remaining_size >= (1UL << size_log2)) { + unmap_log2_range(space_id, addr, size_log2); + + remaining_size -= 1 << size_log2; + addr += 1 << size_log2; + } + + /* decrease flexpage size */ + size_log2--; + } +} diff --git a/base-okl4/src/core/target.inc b/base-okl4/src/core/target.inc new file mode 100644 index 000000000..9bcb0bd21 --- /dev/null +++ b/base-okl4/src/core/target.inc @@ -0,0 +1,55 @@ +TARGET = core +REQUIRES = okl4 +LIBS = cxx ipc heap core_printf process pager lock \ + raw_signal raw_server bootinfo + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = main.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + pd_session_component.cc \ + okl4_pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_start.cc \ + thread_bootstrap.cc \ + platform_thread.cc \ + platform_pd.cc \ + platform.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + core_rm_session.cc \ + core_mem_alloc.cc \ + dump_alloc.cc \ + context_area.cc + +INC_DIR = $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include + +vpath main.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath signal_source_component.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath core_mem_alloc.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath context_area.cc $(GEN_CORE_DIR) +vpath %.cc $(REP_DIR)/src/core +vpath thread_bootstrap.cc $(REP_DIR)/src/base/thread +vpath thread_start.cc $(BASE_DIR)/src/base/thread +vpath thread.cc $(BASE_DIR)/src/base/thread diff --git a/base-okl4/src/core/thread_start.cc b/base-okl4/src/core/thread_start.cc new file mode 100644 index 000000000..51a3f85fb --- /dev/null +++ b/base-okl4/src/core/thread_start.cc @@ -0,0 +1,59 @@ +/* + * \brief Implementation of Thread API interface on top of Platform_thread + * \author Norman Feske + * \date 2006-05-03 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +using namespace Genode; + + +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + sleep_forever(); +} + + +void Thread_base::start() +{ + /* create and start platform thread */ + _tid.pt = new(platform()->core_mem_alloc()) Platform_thread(_context->name); + + platform_specific()->core_pd()->bind_thread(_tid.pt); + + _tid.pt->start((void *)_thread_start, _context->stack); +} + + +void Thread_base::cancel_blocking() +{ + /* + * Within core, we never need to unblock threads + */ +} + + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() +{ + /* destruct platform thread */ + destroy(platform()->core_mem_alloc(), _tid.pt); +} diff --git a/base-okl4/src/core/x86/platform_thread_x86.cc b/base-okl4/src/core/x86/platform_thread_x86.cc new file mode 100644 index 000000000..dcb111dd9 --- /dev/null +++ b/base-okl4/src/core/x86/platform_thread_x86.cc @@ -0,0 +1,57 @@ +/* + * \brief x86-specific OKL4 thread facility + * \author Christian Prochaska + * \date 2011-04-15 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +} } + +using namespace Genode; +using namespace Okl4; + + +int Platform_thread::state(Thread_state *state_dst) +{ + state_dst->tid = _l4_thread_id; + + L4_Copy_regs_to_mrs(_l4_thread_id); + + enum { + MR_EIP = 0, + MR_EFLAGS = 1, + MR_EDI = 2, + MR_ESI = 3, + MR_EBP = 4, + MR_ESP = 5, + MR_EBX = 6, + MR_EDX = 7, + MR_ECX = 8, + MR_EAX = 9, + }; + + L4_StoreMR(MR_EIP, &state_dst->ip); + L4_StoreMR(MR_EFLAGS, &state_dst->eflags); + L4_StoreMR(MR_EDI, &state_dst->edi); + L4_StoreMR(MR_ESI, &state_dst->esi); + L4_StoreMR(MR_EBP, &state_dst->ebp); + L4_StoreMR(MR_ESP, &state_dst->sp); + L4_StoreMR(MR_EBX, &state_dst->ebx); + L4_StoreMR(MR_EDX, &state_dst->edx); + L4_StoreMR(MR_ECX, &state_dst->ecx); + L4_StoreMR(MR_EAX, &state_dst->eax); + + return 0; +} diff --git a/base-okl4/src/core/x86/target.mk b/base-okl4/src/core/x86/target.mk new file mode 100644 index 000000000..4bf5abc5c --- /dev/null +++ b/base-okl4/src/core/x86/target.mk @@ -0,0 +1,8 @@ +include $(PRG_DIR)/../target.inc + +REQUIRES += x86 + +SRC_CC += platform_thread_x86.cc + +vpath io_port_session_component.cc $(GEN_CORE_DIR)/x86 +vpath platform_thread_x86.cc $(GEN_CORE_DIR)/x86 diff --git a/base-okl4/src/kernel/target.inc b/base-okl4/src/kernel/target.inc new file mode 100644 index 000000000..7696ddfaf --- /dev/null +++ b/base-okl4/src/kernel/target.inc @@ -0,0 +1,67 @@ +TARGET = kernel +OKL4_SRC_DIR = $(REP_DIR)/contrib/okl4 +OKL4_BUILD_DIR = $(BUILD_BASE_DIR)/kernel +REQUIRES += okl4 +STARTUP_LIB = +LIBS = kernel +SRC_O += $(addprefix $(OKL4_BUILD_DIR)/asm/,$(addsuffix .o,$(basename $(SRC_SPP)))) +SRC_CC += $(subst $(OKL4_SRC_DIR)/,,$(wildcard $(OKL4_SRC_DIR)/pistachio/kdb/src/*.cc)) \ + $(subst $(OKL4_SRC_DIR)/,,$(wildcard $(OKL4_SRC_DIR)/pistachio/src/*.cc)) +CONFIG += __API__=v4 \ + CONFIG_ASSERT_LEVEL=3 \ + CONFIG_DEBUG=1 \ + CONFIG_ENABLE_FASTPATHS=1 \ + CONFIG_HYBRID_MUTEXES=1 \ + CONFIG_IS_32BIT=1 \ + CONFIG_KDB=1 \ + CONFIG_KDB_BREAKIN=1 \ + CONFIG_KDB_CLI=1 \ + CONFIG_KDB_COLOR_VT=1 \ + CONFIG_KDB_CONS=1 \ + CONFIG_KMEM_TRACE=1 \ + CONFIG_LITTLEENDIAN=1 \ + CONFIG_MAX_SPACES=256U \ + CONFIG_MAX_THREAD_BITS=10 \ + CONFIG_MUTEX_NAMES=1 \ + CONFIG_REMOTE_MEMORY_COPY=1 \ + CONFIG_SCHEDULE_INHERITANCE=1 \ + CONFIG_SMP_MAX_CPUS=1 \ + CONFIG_THREAD_NAMES=1 \ + CONFIG_TRACEBUFFER=1 \ + CONFIG_TRACEBUF_PAGES=64 \ + CONFIG_TRACEPOINTS=1 \ + KENGE_PISTACHIO \ + KERNEL_GEN_DAY=$(shell date +%d) \ + KERNEL_GEN_MONTH=$(shell date +%m) \ + KERNEL_GEN_YEAR=$(shell date +%g) \ + KERNEL_SUBSUBVERSION=0 \ + KERNEL_SUBVERSION=1 \ + KERNEL_VERSION=0 \ + WORDSIZE_32 +CC_OPT += -Wno-write-strings -Wredundant-decls -Wundef \ + -Wpointer-arith -Wno-uninitialized \ + -fno-builtin -fomit-frame-pointer \ + -fno-exceptions -fno-unwind-tables \ + -fno-asynchronous-unwind-tables \ + -finline-limit=99999999 $(addprefix -D,$(CONFIG)) \ + "-D__USER__=\"Genode Labs\"" + +CC_WARN = -Wall -Wno-unused-but-set-variable -Wno-uninitialized + +# +# Enforce building the kernel with -O3. Otherwise, the kernel build would fail +# if the global 'CC_OLEVEL' is set is -O0. (OKL4 depends on some builtin +# functions that are not provided by the compiler when building with -O0) +# +override CC_OLEVEL = -O3 + +$(OKL4_BUILD_DIR)/asm/%.o: $(OKL4_SRC_DIR)/%.spp + $(MSG_COMP)$*.spp + $(VERBOSE)$(CC) $(CC_MARCH) -x assembler-with-cpp -DASSEMBLY \ + $(addprefix -D,$(CONFIG)) \ + $(addprefix -I,$(INC_DIR)) -c $< -o $@ + +clean cleanall: + $(VERBOSE)rm -rf $(OKL4_BUILD_DIR) + +vpath %.cc $(OKL4_SRC_DIR) diff --git a/base-okl4/src/kernel/x86/target.mk b/base-okl4/src/kernel/x86/target.mk new file mode 100644 index 000000000..eb597953f --- /dev/null +++ b/base-okl4/src/kernel/x86/target.mk @@ -0,0 +1,56 @@ +REQUIRES = x86 32bit +CONFIG = ARCH_IA32 \ + CONFIG_ARCH_IA32=1 \ + CONFIG_CPU_IA32_I686 \ + CONFIG_CPU_IA32_I686 \ + CONFIG_CPU_IA32_IDT=1 \ + CONFIG_KDB_CONS_COM=1 \ + CONFIG_KDB_CONS_SERIAL=1 \ + CONFIG_PLAT_PC99=1 \ + CONFIG_ROOT_CAP_BITS=12 \ + ENDIAN_LITTLE \ + L4_ARCH_IA32 \ + MACHINE_IA32_PC99 \ + __ARCH__=ia32 \ + __CPU__=idt \ + __L4_ARCH__=ia32 \ + __PLATFORM__=pc99 +SRC_CC = macro_sets.cc \ + arch/ia32/pistachio/kdb/breakpoints.cc \ + arch/ia32/pistachio/kdb/stepping.cc \ + arch/ia32/pistachio/kdb/x86.cc \ + arch/ia32/pistachio/kdb/glue/prepost.cc \ + arch/ia32/pistachio/kdb/glue/readmem.cc \ + arch/ia32/pistachio/kdb/glue/resources.cc \ + arch/ia32/pistachio/kdb/glue/space.cc \ + arch/ia32/pistachio/kdb/glue/thread.cc \ + arch/ia32/pistachio/src/debug.cc \ + arch/ia32/pistachio/src/exception.cc \ + arch/ia32/pistachio/src/init.cc \ + arch/ia32/pistachio/src/ldt.cc \ + arch/ia32/pistachio/src/platsupport.cc \ + arch/ia32/pistachio/src/resources.cc \ + arch/ia32/pistachio/src/schedule.cc \ + arch/ia32/pistachio/src/smp.cc \ + arch/ia32/pistachio/src/space.cc \ + arch/ia32/pistachio/src/thread.cc \ + arch/ia32/pistachio/src/user.cc \ + platform/pc99/pistachio/src/plat.cc \ + platform/pc99/pistachio/src/reboot.cc +SRC_CC += $(subst $(OKL4_SRC_DIR)/,,$(wildcard $(OKL4_SRC_DIR)/arch/ia32/pistachio/cpu/idt/src/*.cc)) \ + $(subst $(OKL4_SRC_DIR)/,,$(wildcard $(OKL4_SRC_DIR)/platform/pc99/pistachio/kdb/*.cc)) +SRC_SPP = arch/ia32/pistachio/src/trap.spp \ + arch/ia32/pistachio/src/gnu/bootmem-elf.spp \ + platform/pc99/pistachio/src/smp.spp \ + platform/pc99/pistachio/src/startup.spp +LD_TEXT_ADDR = 0xf0100000 + +-include $(PRG_DIR)/../target.inc + +LD_SCRIPT_STATIC = $(OKL4_SRC_DIR)/../generated/x86/linker.ld +INC_DIR = $(OKL4_BUILD_DIR)/include \ + $(OKL4_SRC_DIR)/../generated/x86 \ + $(OKL4_SRC_DIR)/pistachio/include + +vpath macro_sets.cc $(OKL4_SRC_DIR)/../generated/x86 +vpath %.spp $(OKL4_SRC_DIR)/arch/ia32/pistachio/src diff --git a/base-okl4/src/platform/_main_helper.h b/base-okl4/src/platform/_main_helper.h new file mode 100644 index 000000000..a5011484f --- /dev/null +++ b/base-okl4/src/platform/_main_helper.h @@ -0,0 +1,50 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Christian Prochaska + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM___MAIN_HELPER_H_ +#define _PLATFORM___MAIN_HELPER_H_ + + +/* OKL4-specific includes and definitions */ +namespace Okl4 { extern "C" { +#include +#include +} } + +enum { L4_PAGEMASK = ~0xFFF }; +enum { L4_PAGESIZE = 0x1000 }; + +namespace Okl4 { + + /* + * Read global thread ID from user-defined handle and store it + * into a designated UTCB entry. + */ + void copy_uregister_to_utcb() + { + using namespace Okl4; + + L4_Word_t my_global_id = L4_UserDefinedHandle(); + __L4_TCR_Set_ThreadWord(Genode::UTCB_TCR_THREAD_WORD_MYSELF, + my_global_id); + } +} + + +static void main_thread_bootstrap() +{ + /* copy thread ID to utcb */ + Okl4::copy_uregister_to_utcb(); +} + +#endif /* _PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-okl4/src/test/create_thread.h b/base-okl4/src/test/create_thread.h new file mode 100644 index 000000000..59745d0ee --- /dev/null +++ b/base-okl4/src/test/create_thread.h @@ -0,0 +1,144 @@ +/* + * \brief Thread creation on OKL4 + * \author Norman Feske + * \date 2009-03-26 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CREATE_THREAD_H_ +#define _CREATE_THREAD_H_ + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +#include +#include +#include +#include +#include +} } + +namespace Okl4 { + extern L4_Word_t copy_uregister_to_utcb(void); +} + +/* Genode includes */ +#include +#include + + +enum { DEFAULT_PRIORITY = 100 }; + +/** + * Create and start new thread + * + * \param thread_no designated thread number of new thread + * \param space_no space ID in which the new thread will be executed + * \param sp initial stack pointer + * \param ip initial instruction pointer + * \return native thread ID + */ +static inline Okl4::L4_ThreadId_t create_thread(int thread_no, int space_no, + void *sp, void (*ip)(void)) +{ + using namespace Okl4; + + /* activate local thread by assigning a UTCB address and thread ID */ + L4_ThreadId_t new_thread_id = L4_GlobalId(thread_no, 1); + L4_SpaceId_t roottask_space_id = L4_SpaceId(space_no); + L4_ThreadId_t scheduler = L4_rootserver; + L4_ThreadId_t pager = L4_rootserver; + L4_ThreadId_t exception_handler = L4_rootserver; + L4_Word_t resources = 0; + L4_Word_t utcb_location = (L4_Word_t)utcb_base_get() + + space_no*L4_GetUtcbAreaSize() + + thread_no*L4_GetUtcbSize(); +#ifdef NO_UTCB_RELOCATE + utcb_location = ~0; /* UTCB allocation is handled by the kernel */ +#endif + + int ret = L4_ThreadControl(new_thread_id, + roottask_space_id, + scheduler, pager, exception_handler, + resources, (void *)utcb_location); + if (ret != 1) { + PERR("L4_ThreadControl returned %d, error code=%d", + ret, (int)L4_ErrorCode()); + for (;;); + return L4_nilthread; + } + + /* let the new thread know its global thread id */ + L4_Set_UserDefinedHandleOf(new_thread_id, new_thread_id.raw); + + /* start thread */ + L4_Start_SpIp(new_thread_id, (L4_Word_t)sp, (L4_Word_t)ip); + + /* set default priority */ + L4_Set_Priority(new_thread_id, DEFAULT_PRIORITY); + + return new_thread_id; +} + + +/** + * Perform thread startup protocol to make global ID known to the ourself + * + * This function must be executed by a newly created thread to make + * 'thread_get_my_global_id' to work. + */ +static inline void thread_init_myself() +{ + using namespace Okl4; + + /* + * Read global thread ID from user-defined handle and store it + * into a designated UTCB entry. + */ + L4_Word_t my_global_id = L4_UserDefinedHandle(); + __L4_TCR_Set_ThreadWord(Genode::UTCB_TCR_THREAD_WORD_MYSELF, my_global_id); +} + + +/** + * Register the rootserver's thread ID at our UTCB + * + * This function must be executed at the startup of the rootserver main + * thread to make 'thread_get_my_gloal_id' to work. + */ +static inline void roottask_init_myself() +{ + using namespace Okl4; + + /* + * On Genode, the user-defined handle get initialized with the thread's + * global ID by core when creating the new thread. For the main thread, + * we do this manually. + */ + __L4_TCR_Set_UserDefinedHandle(L4_rootserver.raw); + copy_uregister_to_utcb(); +} + + +/** + * Return the global thread ID of the calling thread + * + * On OKL4 we cannot use 'L4_Myself()' to determine our own thread's + * identity. By convention, each thread stores its global ID in a + * defined entry of its UTCB. + */ +static inline Okl4::L4_ThreadId_t thread_get_my_global_id() +{ + Okl4::L4_ThreadId_t myself; + myself.raw = Okl4::__L4_TCR_ThreadWord(Genode::UTCB_TCR_THREAD_WORD_MYSELF); + return myself; +} + +#endif /* _CREATE_THREAD_H_ */ diff --git a/base-okl4/src/test/io_port.h b/base-okl4/src/test/io_port.h new file mode 100644 index 000000000..3bee2c359 --- /dev/null +++ b/base-okl4/src/test/io_port.h @@ -0,0 +1,36 @@ +/* + * \brief I/O port access function for x86 + * \author Norman Feske + * \date 2009-03-31 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _IO_PORT_H_ +#define _IO_PORT_H_ + +/** + * Read byte from I/O port + */ +inline unsigned char inb(unsigned short port) +{ + unsigned char res; + asm volatile ("inb %%dx, %0" :"=a"(res) :"Nd"(port)); + return res; +} + + +/** + * Write byte to I/O port + */ +inline void outb(unsigned short port, unsigned char val) +{ + asm volatile ("outb %b0, %w1" : : "a" (val), "Nd" (port)); +} + +#endif /* _IO_PORT_H_ */ diff --git a/base-okl4/src/test/mini_env.h b/base-okl4/src/test/mini_env.h new file mode 100644 index 000000000..ae2b350d5 --- /dev/null +++ b/base-okl4/src/test/mini_env.h @@ -0,0 +1,83 @@ +/* + * \brief Minimalistic environment used for test steps + * \author Norman Feske + * \date 2009-03-25 + * + * This file is not an interface but an implementation snippet. It should + * be included only once per program because it contains the implementation + * of the global 'env()' function. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _MINI_ENV_H_ +#define _MINI_ENV_H_ + +#include +#include + +/** + * Minimalistic environment + * + * \param HEAP_SIZE size of static heap in bytes + * + * This implementation of the Genode environment does only + * provide a heap. It suffices to successfully initialize + * the C++ exception handling. + */ +template +class Minimal_env : public Genode::Env +{ + private: + + char _heap[HEAP_SIZE]; + Genode::Allocator_avl _alloc; + + public: + + /** + * Constructor + */ + Minimal_env() : _alloc(0) { + _alloc.add_range((Genode::addr_t)_heap, HEAP_SIZE); } + + Genode::Allocator *heap() { return &_alloc; } + + + /*********************************************** + ** Dummy implementation of the Env interface ** + ***********************************************/ + + Genode::Parent *parent() { return 0; } + Genode::Ram_session *ram_session() { return 0; } + Genode::Cpu_session *cpu_session() { return 0; } + Genode::Rm_session *rm_session() { return 0; } + Genode::Pd_session *pd_session() { return 0; } + + Genode::Ram_session_capability ram_session_cap() { + return Genode::Ram_session_capability(); } +}; + + +/** + * Instance of the minimal environment + */ +namespace Genode { + + /** + * Instance of minimalistic Genode environment providing a + * static heap of 64KB + */ + Env *env() + { + static Minimal_env<64*1024> env; + return &env; + } +} + +#endif /* _MINI_ENV_H_ */ diff --git a/base-okl4/src/test/okl4_01_hello_raw/Makefile b/base-okl4/src/test/okl4_01_hello_raw/Makefile new file mode 100644 index 000000000..0adbcf0bf --- /dev/null +++ b/base-okl4/src/test/okl4_01_hello_raw/Makefile @@ -0,0 +1,27 @@ +OKL4_DIR = ../okl4_2.1.1-patch.9 +CXXFLAGS = -I$(OKL4_DIR)/build/pistachio/include -nostdlib -nostdinc +#CXXFLAGS += -DCONFIG_MAX_THREAD_BITS=10 +#CXXFLAGS += -DCONFIG_CPU_IA32_I586 +#CXXFLAGS += -DCONFIG_KDB_CONS +LD_SCRIPT = genode.ld +LDFLAGS = -Wl,-Ttext=0x01000000 -Wl,-T$(LD_SCRIPT) + +all: image + +hello: hello.cc + $(CXX) $(CXXFLAGS) $(LDFLAGS) -static crt0.s hello.cc -o $@ + +# +# Merge kernel and hello program into a single elf image +# +# We need to strip the elf image to make the image loadable by grub. +# Otherwise GRUB complaints: +# +# "Error 28: Selected item cannot fit into memory" +# +image: hello weaver.xml + $(OKL4_DIR)/tools/pyelf/elfweaver merge -o $@ weaver.xml + strip $@ + +clean: + rm -f hello image diff --git a/base-okl4/src/test/okl4_01_hello_raw/crt0.s b/base-okl4/src/test/okl4_01_hello_raw/crt0.s new file mode 100644 index 000000000..30e7e367e --- /dev/null +++ b/base-okl4/src/test/okl4_01_hello_raw/crt0.s @@ -0,0 +1,54 @@ +/** + * \brief Startup code for Genode applications + * \author Christian Helmuth + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/*--- .text (program code) -------------------------*/ + .text + + .globl _start +_start: + /* XXX Switch to our own stack. */ + mov $_stack_high,%esp + + /* Clear the base pointer so that stack backtraces will work. */ + xor %ebp,%ebp + + /* Jump into init C code */ + call _main + + /* We should never get here since _main does not return */ +1: int $3 + jmp 2f + .ascii "_main() returned." +2: jmp 1b + + +/*--------------------------------------------------*/ + .globl __dso_handle +__dso_handle: + .long 0 + +/*--- .eh_frame (exception frames) -----------------*/ +/* + .section .eh_frame,"aw" + .globl __EH_FRAME_BEGIN__ +__EH_FRAME_BEGIN__: +*/ + +/*--- .bss (non-initialized data) ------------------*/ + .bss + .p2align 4 + .globl _stack_low +_stack_low: + .space 64*1024 + .globl _stack_high +_stack_high: diff --git a/base-okl4/src/test/okl4_01_hello_raw/genode.ld b/base-okl4/src/test/okl4_01_hello_raw/genode.ld new file mode 100644 index 000000000..3d8b06a68 --- /dev/null +++ b/base-okl4/src/test/okl4_01_hello_raw/genode.ld @@ -0,0 +1,89 @@ +/* + * \brief Linker script for GENODE programs + * \author Christian Helmuth + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +ENTRY(_start) + +PHDRS +{ + ro PT_LOAD; + rw PT_LOAD; +} + +SECTIONS +{ + .text : { + /* begin of program image (link address) */ + _prog_img_beg = .; + + *(.init) + *(.text .text.* .gnu.linkonce.t.*) + *(.fini) + *(.rodata .rodata.* .gnu.linkonce.r.*) + + . = ALIGN(0x04); + + _ctors_start = .; + KEEP (*(.ctors)) + KEEP (*(SORT(.ctors.*))) + _ctors_end = .; + _dtors_start = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + _dtors_end = .; + } : ro = 0x90909090 + + /* Linux: exception section for uaccess mechanism */ + __ex_table : { *(__ex_table) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } + + . = ALIGN(0x1000); + + _prog_img_data = .; + + .data : { + /* Leave space for parent capability parameters at start of data + * section. The protection domain creator is reponsible for storing + * sane values here. + */ + _parent_cap_thread_id = .; + LONG(0xffffffff); + _parent_cap_local_name = .; + LONG(0xffffffff); + + *(.data .data.* .gnu.linkonce.d.*) + } : rw + + /* exception frames for C++ */ + .eh_frame : { + __eh_frame_start__ = .; + KEEP (*(.eh_frame)) + LONG(0) + } : rw + .gcc_except_table : { KEEP(*(.gcc_except_table)) } + .dynamic : { *(.dynamic) } + + .bss : { + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } + + /* end of program image -- must be after last section */ + _prog_img_end = .; + + /DISCARD/ : { + *(.note) + *(.note.ABI-tag) + *(.comment) + *(.eh_frame) + } +} diff --git a/base-okl4/src/test/okl4_01_hello_raw/hello.cc b/base-okl4/src/test/okl4_01_hello_raw/hello.cc new file mode 100644 index 000000000..5fd0451d5 --- /dev/null +++ b/base-okl4/src/test/okl4_01_hello_raw/hello.cc @@ -0,0 +1,76 @@ +/* + * \brief Simple roottask replacement for OKL4 that just prints some text + * \author Norman Feske + * \date 2008-09-01 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +typedef unsigned char uint8_t; + + +/** + * Read byte from I/O port + */ +inline uint8_t inb(unsigned short port) +{ + uint8_t res; + asm volatile ("inb %%dx, %0" :"=a"(res) :"Nd"(port)); + return res; +} + + +/** + * Write byte to I/O port + */ +inline void outb(unsigned short port, uint8_t val) +{ + asm volatile ("outb %b0, %w1" : : "a" (val), "Nd" (port)); +} + + +/** + * Definitions of PC serial ports + */ +enum Comport { COMPORT_0, COMPORT_1, COMPORT_2, COMPORT_3 }; + + +/** + * Output character to serial port + */ +inline void serial_out_char(Comport comport, uint8_t c) +{ + static int io_port[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + + /* wait until serial port is ready */ + while (!(inb(io_port[comport] + 5) & 0x60)); + + /* output character */ + outb(io_port[comport], c); +} + + +/** + * Print null-terminated string to serial port + */ +inline void serial_out_string(Comport comport, const char *str) +{ + while (*str) + serial_out_char(comport, *str++); +} + + +/** + * Main program, called by the startup code in crt0.s + */ +extern "C" int _main(void) +{ + serial_out_string(COMPORT_0, "Hallo, this is some code running on OKL4.\n"); + serial_out_string(COMPORT_0, "Returning from main...\n"); + return 0; +} diff --git a/base-okl4/src/test/okl4_01_hello_raw/weaver.xml b/base-okl4/src/test/okl4_01_hello_raw/weaver.xml new file mode 100644 index 000000000..9b32d53b2 --- /dev/null +++ b/base-okl4/src/test/okl4_01_hello_raw/weaver.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base-okl4/src/test/okl4_02_hello/hello.cc b/base-okl4/src/test/okl4_02_hello/hello.cc new file mode 100644 index 000000000..85775e1b9 --- /dev/null +++ b/base-okl4/src/test/okl4_02_hello/hello.cc @@ -0,0 +1,37 @@ +/* + * \brief Test for the Genode console and exception handling + * \author Norman Feske + * \date 2009-03-24 + * + * This program can be started as roottask replacement directly on + * the OKL4 kernel. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include "../mini_env.h" + +/** + * Main program + */ +int main() +{ + Genode::printf("Hello, this is Genode's printf.\n"); + + try { + throw 1; + } catch (...) { + Genode::printf("Successfully caught an exception.\n"); + } + + return 0; +} diff --git a/base-okl4/src/test/okl4_02_hello/target.mk b/base-okl4/src/test/okl4_02_hello/target.mk new file mode 100644 index 000000000..f0564a019 --- /dev/null +++ b/base-okl4/src/test/okl4_02_hello/target.mk @@ -0,0 +1,4 @@ +TARGET = hello +REQUIRES = okl4 +LIBS = cxx core_printf ipc +SRC_CC = hello.cc diff --git a/base-okl4/src/test/okl4_03_thread/main.cc b/base-okl4/src/test/okl4_03_thread/main.cc new file mode 100644 index 000000000..579f5e2b2 --- /dev/null +++ b/base-okl4/src/test/okl4_03_thread/main.cc @@ -0,0 +1,76 @@ +/* + * \brief Test for using OKL4 system-call bindings for thread creation + * \author Norman Feske + * \date 2009-03-25 + * + * This program can be started as roottask replacement directly on the + * OKL4 kernel. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* local includes */ +#include "../mini_env.h" +#include "../create_thread.h" + + +/** + * Global variable, modified by the thread, observed by the main thread + */ +static volatile int counter; + + +/** + * Thread entry function + */ +static void thread_entry(void) +{ + /* infinite busy loop incrementing a global variable */ + while (1) + counter++; +} + + +/** + * Main program + */ +int main() +{ + using namespace Genode; + using namespace Okl4; + + /* set default priority for ourself to make round-robin scheduling work */ + L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY); + + /* stack used for the new thread */ + enum { THREAD_STACK_SIZE = 4096 }; + static int thread_stack[THREAD_STACK_SIZE]; + + /* stack grows from top, hence we specify the end of the stack */ + create_thread(1, L4_rootserverno, + (void *)(&thread_stack[THREAD_STACK_SIZE]), + thread_entry); + + /* observe the work done by the new thread */ + enum { COUNT_MAX = 10*1000*1000 }; + printf("main thread: let new thread count to %d\n", (int)COUNT_MAX); + + while (counter < COUNT_MAX) { + + printf("main thread: counter=%d\n", counter); + + /* + * Yield the remaining time slice to the new thread to + * avoid printing the same counter value again and again. + */ + L4_Yield(); + } + + printf("exiting main()\n"); + return 0; +} diff --git a/base-okl4/src/test/okl4_03_thread/target.mk b/base-okl4/src/test/okl4_03_thread/target.mk new file mode 100644 index 000000000..00d935440 --- /dev/null +++ b/base-okl4/src/test/okl4_03_thread/target.mk @@ -0,0 +1,4 @@ +TARGET = test-thread +REQUIRES = okl4 +LIBS = cxx core_printf ipc +SRC_CC = main.cc diff --git a/base-okl4/src/test/okl4_04_ipc_send_wait/main.cc b/base-okl4/src/test/okl4_04_ipc_send_wait/main.cc new file mode 100644 index 000000000..9c3e2971a --- /dev/null +++ b/base-okl4/src/test/okl4_04_ipc_send_wait/main.cc @@ -0,0 +1,79 @@ +/* + * \brief Test for IPC send and wait via Genode's IPC framework + * \author Norman Feske + * \date 2009-03-26 + * + * This program can be started as roottask replacement directly on the + * OKL4 kernel. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include "../mini_env.h" +#include "../create_thread.h" + +using namespace Genode; +using namespace Okl4; + +static Untyped_capability receiver_cap; + + +/** + * Sender thread, must not be started before 'receiver_cap' is initialized + */ +static void sender_thread_entry() +{ + thread_init_myself(); + + static Msgbuf<256> sndbuf; + static Ipc_ostream os(receiver_cap, &sndbuf); + + int a = 1, b = 2, c = 3; + + printf("sending a=%d, b=%d, c=%d\n", a, b, c); + os << a << b << c << IPC_SEND; + + for (;;) L4_Yield(); +} + + +/** + * Main program + */ +int main() +{ + roottask_init_myself(); + + /* set default priority for ourself to make round-robin scheduling work */ + L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY); + + static Msgbuf<256> rcvbuf; + static Ipc_istream is(&rcvbuf); + + /* make input stream capability known */ + receiver_cap = is; + + /* create sender thread, sending to destination (us) */ + enum { THREAD_STACK_SIZE = 4096 }; + static int thread_stack[THREAD_STACK_SIZE]; + create_thread(1, L4_rootserverno, + (void *)(&thread_stack[THREAD_STACK_SIZE]), + sender_thread_entry); + + /* wait for incoming IPC */ + int a = 0, b = 0, c = 0; + is >> IPC_WAIT >> a >> b >> c; + printf("received a=%d, b=%d, c=%d\n", a, b, c); + + printf("exiting main()\n"); + return 0; +} diff --git a/base-okl4/src/test/okl4_04_ipc_send_wait/target.mk b/base-okl4/src/test/okl4_04_ipc_send_wait/target.mk new file mode 100644 index 000000000..5ee72ceae --- /dev/null +++ b/base-okl4/src/test/okl4_04_ipc_send_wait/target.mk @@ -0,0 +1,4 @@ +TARGET = test-ipc_send_wait +REQUIRES = okl4 +LIBS = cxx core_printf ipc +SRC_CC = main.cc diff --git a/base-okl4/src/test/okl4_05_ipc_call/main.cc b/base-okl4/src/test/okl4_05_ipc_call/main.cc new file mode 100644 index 000000000..70e852558 --- /dev/null +++ b/base-okl4/src/test/okl4_05_ipc_call/main.cc @@ -0,0 +1,90 @@ +/* + * \brief Test for IPC call via Genode's IPC framework + * \author Norman Feske + * \date 2009-03-26 + * + * This program can be started as roottask replacement directly on the + * OKL4 kernel. The main program plays the role of a server. It starts + * a thread that acts as a client and performs an IPC call to the server. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include "../mini_env.h" +#include "../create_thread.h" + +using namespace Genode; +using namespace Okl4; + +static Untyped_capability server_cap; + + +/** + * Client thread, must not be started before 'destination' is initialized + */ +static void client_thread_entry() +{ + thread_init_myself(); + + Msgbuf<256> client_rcvbuf, client_sndbuf; + Ipc_client client(server_cap, &client_sndbuf, &client_rcvbuf); + + printf("client sends call(11, 12, 13)\n"); + int res, d = 0, e = 0; + res = (client << 11 << 12 << 13 << IPC_CALL >> d >> e).result(); + printf("client received reply d=%d, e=%d, res=%d\n", d, e, res); + + printf("client sends call(14, 15, 16)\n"); + res = (client << 14 << 15 << 16 << IPC_CALL >> d >> e).result(); + printf("client received reply d=%d, e=%d, res=%d\n", d, e, res); + + for (;;) L4_Yield(); +} + + +/** + * Main program + */ +int main() +{ + roottask_init_myself(); + + /* set default priority for ourself to make round-robin scheduling work */ + L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY); + + Msgbuf<256> server_rcvbuf, server_sndbuf; + Ipc_server server(&server_sndbuf, &server_rcvbuf); + + /* make server capability known */ + server_cap = server; + + /* create client thread, making a call to the server (us) */ + enum { THREAD_STACK_SIZE = 4096 }; + static int thread_stack[THREAD_STACK_SIZE]; + create_thread(1, L4_rootserverno, + (void *)(&thread_stack[THREAD_STACK_SIZE]), + client_thread_entry); + + /* infinite server loop */ + int a = 0, b = 0, c = 0; + for (;;) { + printf("server: reply_wait\n"); + + server >> IPC_REPLY_WAIT >> a >> b >> c; + printf("server: received a=%d, b=%d, c=%d, send reply %d, %d, res=33\n", + a, b, c, a + b + c, a*b*c); + + server << a + b + c << a*b*c; + server.ret(33); + } + return 0; +} diff --git a/base-okl4/src/test/okl4_05_ipc_call/target.mk b/base-okl4/src/test/okl4_05_ipc_call/target.mk new file mode 100644 index 000000000..fa124f5e7 --- /dev/null +++ b/base-okl4/src/test/okl4_05_ipc_call/target.mk @@ -0,0 +1,4 @@ +TARGET = test-ipc_call +REQUIRES = okl4 +LIBS = cxx core_printf ipc +SRC_CC = main.cc diff --git a/base-okl4/src/test/okl4_06_pager/main.cc b/base-okl4/src/test/okl4_06_pager/main.cc new file mode 100644 index 000000000..60851581b --- /dev/null +++ b/base-okl4/src/test/okl4_06_pager/main.cc @@ -0,0 +1,142 @@ +/* + * \brief Test for creating and paging address spaces + * \author Norman Feske + * \date 2009-03-28 + * + * This program can be started as roottask replacement directly on the + * OKL4 kernel. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +namespace Okl4 { extern "C" { +#include +} } + +/* local includes */ +#include "../mini_env.h" +#include "../create_thread.h" + +using namespace Genode; +using namespace Okl4; + + +/** + * Entry of child address space + */ +static void subspace_thread_entry() +{ + static char read_area[4096*2]; + static char write_area[4096*2]; + thread_init_myself(); + + int a = 0; + + for (unsigned i = 0; i < sizeof(read_area); i++) + a += read_area[i]; + + for (unsigned i = 0; i < sizeof(write_area); i++) + write_area[i] = a; + + for (;;) L4_Yield(); +} + + +/** + * Print page-fault information in a human-readable form + */ +static inline void print_page_fault(L4_Word_t type, L4_Word_t addr, L4_Word_t ip) +{ + printf("page (%s%s%s) fault at pf_addr=%lx, pf_ip=%lx\n", + type & L4_Readable ? "r" : "-", + type & L4_Writable ? "w" : "-", + type & L4_eXecutable ? "x" : "-", + addr, ip); +} + + +/** + * Main program + */ +int main() +{ + roottask_init_myself(); + + /* set default priority for ourself to make round-robin scheduling work */ + L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY); + + enum { NEW_SPACE_ID = 1 }; + enum { FPAGE_LOG2_SIZE_4K = 12 }; + + /* create address space */ + L4_SpaceId_t space = L4_SpaceId(NEW_SPACE_ID); + L4_Word_t control = L4_SpaceCtrl_new; + L4_ClistId_t cap_list = L4_rootclist; + L4_Fpage_t utcb_area = L4_FpageLog2((L4_Word_t)utcb_base_get() + + NEW_SPACE_ID*L4_GetUtcbAreaSize(), + FPAGE_LOG2_SIZE_4K); +#ifdef NO_UTCB_RELOCATE + utcb_area = L4_Nilpage; /* UTCB allocation is handled by the kernel */ +#endif + + L4_Word_t resources = 0; + L4_Word_t old_resources = 0; + + int ret = L4_SpaceControl(space, control, cap_list, utcb_area, + resources, &old_resources); + + if (ret != 1) + PERR("L4_SpaceControl returned %d, error code=%d", + ret, (int)L4_ErrorCode()); + + /* create main thread for new address space */ + enum { THREAD_STACK_SIZE = 4096 }; + static int thread_stack[THREAD_STACK_SIZE]; + create_thread(1, NEW_SPACE_ID, + (void *)(&thread_stack[THREAD_STACK_SIZE]), + subspace_thread_entry); + + printf("entering pager loop\n"); + + for (;;) { + L4_ThreadId_t faulter; + + /* wait for page fault */ + L4_MsgTag_t faulter_tag = L4_Wait(&faulter); + + /* read fault information */ + L4_Word_t pf_type, pf_ip, pf_addr; + L4_StoreMR(1, &pf_addr); + L4_StoreMR(2, &pf_ip); + pf_type = L4_Label(faulter_tag) & 7; + + print_page_fault(pf_type, pf_addr, pf_ip); + + /* determine corresponding page in our own address space */ + pf_addr &= ~(4096 - 1); + L4_Fpage_t fpage = L4_FpageLog2(pf_addr, 12); + fpage.X.rwx = 7; + + /* request physical address of page */ + L4_MapItem_t map_item; + L4_PhysDesc_t phys_desc; + L4_ReadFpage(L4_SpaceId(0), fpage, &phys_desc, &map_item); + + /* map page to faulting space */ + int ret = L4_MapFpage(L4_SenderSpace(), fpage, phys_desc); + + if (ret != 1) + PERR("L4_MapFpage returned %d, error_code=%d", + ret, (int)L4_ErrorCode()); + + /* reply to page-fault message to resume the faulting thread */ + L4_LoadMR(0, 0); + L4_Send(faulter); + } + return 0; +} diff --git a/base-okl4/src/test/okl4_06_pager/target.mk b/base-okl4/src/test/okl4_06_pager/target.mk new file mode 100644 index 000000000..074c411aa --- /dev/null +++ b/base-okl4/src/test/okl4_06_pager/target.mk @@ -0,0 +1,4 @@ +TARGET = test-pager +REQUIRES = okl4 +LIBS = cxx core_printf ipc +SRC_CC = main.cc diff --git a/base-okl4/src/test/okl4_07_boot_info/main.cc b/base-okl4/src/test/okl4_07_boot_info/main.cc new file mode 100644 index 000000000..2bb20a62e --- /dev/null +++ b/base-okl4/src/test/okl4_07_boot_info/main.cc @@ -0,0 +1,103 @@ +/* + * \brief Test for parsing OKL4 boot information + * \author Norman Feske + * \date 2009-03-24 + * + * This program can be started as roottask replacement directly on + * the OKL4 kernel. It determines the available memory resources + * and boot-time data modules. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include "../mini_env.h" + +/* OKL4 includes */ +namespace Okl4 { extern "C" { +#include +#include +#include +} } + +using namespace Okl4; +using namespace Genode; + +static int init_mem(uintptr_t virt_base, uintptr_t virt_end, + uintptr_t phys_base, uintptr_t phys_end, + const bi_user_data_t * data) +{ + printf("init_mem: virt=[%lx,%lx), phys=[%lx,%lx)\n", + virt_base, virt_end, phys_base, phys_end); + return 0; +} + + +static int add_virt_mem(bi_name_t pool, uintptr_t base, uintptr_t end, + const bi_user_data_t * data) +{ + printf("add_virt_mem: pool=%d region=[%lx,%lx]\n", pool, base, end); + return 0; +} + + +static int add_phys_mem(bi_name_t pool, uintptr_t base, uintptr_t end, + const bi_user_data_t * data) +{ + printf("add_phys_mem: pool=%d region=[%lx,%lx]\n", pool, base, end); + return 0; +} + + +static int export_object(bi_name_t pd, bi_name_t obj, + bi_export_type_t export_type, char *key, + Okl4::size_t key_len, + const bi_user_data_t * data) +{ + printf("export_object: pd=%d obj=%d type=%d key=\"%s\"\n", + pd, obj, export_type, key); + return 0; +} + + +static bi_name_t new_ms(bi_name_t owner, uintptr_t base, uintptr_t size, + uintptr_t flags, uintptr_t attr, bi_name_t physpool, + bi_name_t virtpool, bi_name_t zone, + const bi_user_data_t * data) +{ + printf("new_ms: owner=%d region=[%lx,%lx), flags=%lx, attr=%lx, physpool=%d, virtpool=%d, zone=%d\n", + owner, base, base + size - 1, flags, attr, physpool, virtpool, zone); + return 0; +} + + +/** + * Main program + */ +int main() +{ + L4_Word_t boot_info_addr; + L4_StoreMR(1, &boot_info_addr); + printf("boot info at 0x%lx\n", boot_info_addr); + + printf("parsing boot info...\n"); + static bi_user_data_t user_data; + static bi_callbacks_t callbacks; + callbacks.init_mem = init_mem; + callbacks.add_virt_mem = add_virt_mem; + callbacks.add_phys_mem = add_phys_mem; + callbacks.export_object = export_object; + callbacks.new_ms = new_ms; + int ret = bootinfo_parse((void *)boot_info_addr, &callbacks, &user_data); + + printf("finished parsing of boot info with ret=%d, exiting main()\n", ret); + return 0; +} diff --git a/base-okl4/src/test/okl4_07_boot_info/stdint.h b/base-okl4/src/test/okl4_07_boot_info/stdint.h new file mode 100644 index 000000000..0421e00ca --- /dev/null +++ b/base-okl4/src/test/okl4_07_boot_info/stdint.h @@ -0,0 +1,22 @@ +/* + * \brief Integer types required for using OKL4's boot-info parser + * \author Norman Feske + * \date 2009-04-04 + * + * This file is indirectly included by OKL4's 'bootinfo.h'. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__STDINT_H_ +#define _CORE__INCLUDE__STDINT_H_ + +typedef unsigned long uintptr_t; +typedef signed long intptr_t; + +#endif /* _CORE__INCLUDE__STDINT_H_ */ diff --git a/base-okl4/src/test/okl4_07_boot_info/target.mk b/base-okl4/src/test/okl4_07_boot_info/target.mk new file mode 100644 index 000000000..ce8ba8088 --- /dev/null +++ b/base-okl4/src/test/okl4_07_boot_info/target.mk @@ -0,0 +1,5 @@ +TARGET = test-boot_info +REQUIRES = okl4 +LIBS = cxx core_printf ipc bootinfo +SRC_CC = main.cc +INC_DIR += $(PRG_DIR) diff --git a/base-okl4/src/test/okl4_08_timer_pit/main.cc b/base-okl4/src/test/okl4_08_timer_pit/main.cc new file mode 100644 index 000000000..c993b0c14 --- /dev/null +++ b/base-okl4/src/test/okl4_08_timer_pit/main.cc @@ -0,0 +1,135 @@ +/* + * \brief Test for interrupt handling and timer on OKL4 + * \author Norman Feske + * \date 2009-03-31 + * + * This program can be started as roottask replacement directly on the OKL4 + * kernel. It has two purposes, to test the interrupt handling on OKL4 and to + * provide a user-level time source. The x86 version of the OKL4 kernel uses + * the APIC timer as scheduling timer. So the PIT free to use as user-land time + * source. This is needed because the OKL4 kernel provides no means to access + * the kernel-level time source through IPC timeouts anymore. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include "../mini_env.h" +#include "../io_port.h" + +namespace Okl4 { extern "C" { +#include +#include +#include +#include +} } + +using namespace Okl4; +using namespace Genode; + +enum { IRQ_PIT = 0 }; /* timer interrupt line at the PIC */ + + +enum { + PIT_TICKS_PER_SECOND = 1193182, + PIT_MAX_COUNT = 65535, + PIT_DATA_PORT_0 = 0x40, /* data port for PIT channel 0, connected + to the PIC */ + PIT_CMD_PORT = 0x43 /* PIT command port */ +}; + + +/** + * Bit definitions for accessing the PIT command port + */ +enum { + PIT_CMD_SELECT_CHANNEL_0 = 0 << 6, + PIT_CMD_ACCESS_LO = 1 << 4, + PIT_CMD_ACCESS_LO_HI = 3 << 4, + PIT_CMD_MODE_IRQ = 0 << 1, + PIT_CMD_MODE_RATE = 2 << 1, +}; + + +/** + * Set PIT counter value + */ +static inline void pit_set_counter(uint16_t value) +{ + outb(PIT_DATA_PORT_0, value & 0xff); + outb(PIT_DATA_PORT_0, (value >> 8) & 0xff); +} + + +/** + * Main program + */ +int main() +{ + /* operate PIT in one-shot mode */ + outb(PIT_CMD_PORT, PIT_CMD_SELECT_CHANNEL_0 | + PIT_CMD_ACCESS_LO_HI | + PIT_CMD_MODE_IRQ); + + int irq = IRQ_PIT; + + /* allow roottask (ourself) to handle the interrupt */ + L4_LoadMR(0, irq); + int ret = L4_AllowInterruptControl(L4_rootspace); + if (ret != 1) + printf("L4_AllowInterruptControl returned %d, error code=%ld\n", + ret, L4_ErrorCode()); + + /* bit to use for IRQ notifications */ + enum { IRQ_NOTIFY_BIT = 13 }; + + /* + * Note: 'L4_Myself()' does not work for the thread argument of + * 'L4_RegisterInterrupt'. We have to specify our global ID. + */ + L4_LoadMR(0, irq); + ret = L4_RegisterInterrupt(L4_rootserver, IRQ_NOTIFY_BIT, 0, 0); + if (ret != 1) + printf("L4_RegisterInterrupt returned %d, error code=%ld\n", + ret, L4_ErrorCode()); + + /* prepare ourself to receive asynchronous IRQ notifications */ + L4_ThreadId_t partner = L4_nilthread; + L4_Set_NotifyMask(1 << IRQ_NOTIFY_BIT); + L4_Accept(L4_NotifyMsgAcceptor); + + int cnt = 0, seconds = 1; + for (;;) { + /* wait for asynchronous interrupt notification */ + L4_ReplyWait(partner, &partner); + + /* + * Schedule next interrupt + * + * The PIT generates the next interrupt when reaching + * PIT_MAX_COUNT. By initializing the PIT with a higher + * value than 0, we can shorten the time until the next + * interrupt occurs. + */ + pit_set_counter(0); + + /* we got an interrupt, acknowledge */ + L4_LoadMR(0, irq); + L4_AcknowledgeInterrupt(0, 0); + + /* count timer interrupts, print a message each second */ + if (cnt++ == PIT_TICKS_PER_SECOND/PIT_MAX_COUNT) { + printf("Second %d\n", seconds++); + cnt = 0; + } + } + return 0; +} diff --git a/base-okl4/src/test/okl4_08_timer_pit/target.mk b/base-okl4/src/test/okl4_08_timer_pit/target.mk new file mode 100644 index 000000000..cf27568c1 --- /dev/null +++ b/base-okl4/src/test/okl4_08_timer_pit/target.mk @@ -0,0 +1,4 @@ +TARGET = test-timer_pit +REQUIRES = okl4 x86 +LIBS = cxx core_printf ipc +SRC_CC = main.cc diff --git a/base-okl4/tool/README b/base-okl4/tool/README new file mode 100644 index 000000000..2287bccb3 --- /dev/null +++ b/base-okl4/tool/README @@ -0,0 +1,3 @@ +This directory contains the following utilities for working with Genode +on OKL4. + diff --git a/base-okl4/tool/weaver_x86.xml b/base-okl4/tool/weaver_x86.xml new file mode 100644 index 000000000..0c9f535a0 --- /dev/null +++ b/base-okl4/tool/weaver_x86.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base-pistachio/Makefile b/base-pistachio/Makefile new file mode 100644 index 000000000..5a184958d --- /dev/null +++ b/base-pistachio/Makefile @@ -0,0 +1,40 @@ +# +# \brief Checkout Pistachio and addtional needed tools (kickstart) +# \author Stefan Kalkowski +# \date 2011-07-15 +# + +VERBOSE = @ +ECHO = @echo +GIT_URI = https://github.com/l4ka/pistachio.git +GIT_REV = 5c1b29b9c77fbd4760f35507da3d2f548f4364bd +CONTRIB_DIR = contrib +PATCHES = $(shell find patches -name *.patch) + +# +# Print help information by default +# +help:: + $(ECHO) + $(ECHO) "Check out upstream source code of Pistachio" + $(ECHO) + $(ECHO) "The source code will be located at the '$(CONTRIB_DIR)/' directory." + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - checkout upstream source codes" + $(ECHO) "clean - remove upstream source codes" + $(ECHO) + +$(CONTRIB_DIR): + $(VERBOSE)git clone $(GIT_URI) contrib + +prepare: $(CONTRIB_DIR) + $(VERBOSE)cd $(CONTRIB_DIR); git fetch; git reset --hard $(GIT_REV) + @# use GCC front end for as linker for the pistachio user land + $(VERBOSE)sed -i "/LD=/s/^.*$$/LD=\$$(CC)/" $(CONTRIB_DIR)/user/config.mk.in + $(ECHO) "applying patches to '$(CONTRIB_DIR)/'" + $(VERBOSE)for i in $(PATCHES); do patch -d $(CONTRIB_DIR) -p1 < $$i; done + $(VERBOSE)cd $(CONTRIB_DIR)/user; autoheader; autoconf; + +clean:: + $(VERBOSE)rm -rf $(CONTRIB_DIR) diff --git a/base-pistachio/README b/base-pistachio/README new file mode 100644 index 000000000..8d8d81f71 --- /dev/null +++ b/base-pistachio/README @@ -0,0 +1,3 @@ +This repository contains the L4ka::Pistachio-specific implementation of Genode. +Please see the documentation at 'base-pistachio/doc/pistachio.txt' for further +instructions on building and using Genode on the L4ka::Pistachio kernel. diff --git a/base-pistachio/config/kernel b/base-pistachio/config/kernel new file mode 100644 index 000000000..2f05ae2d4 --- /dev/null +++ b/base-pistachio/config/kernel @@ -0,0 +1,153 @@ +# +# This is a Pistachio kernel configuration that is known to work with Genode. +# To use it, create a fresh Pistachio build directory and copy this file to +# '/config.out' and call 'make batchconfig' from the +# Pistachio build directory. +# + +# +# Pistachio Kernel Configuration System +# + +# +# Hardware +# + +# +# Basic Architecture +# +CONFIG_ARCH_X86=y +CONFIG_ARCH_POWERPC=n +CONFIG_ARCH_POWERPC64=n + + +# +# X86 Processor Architecture +# +CONFIG_SUBARCH_X32=y +CONFIG_SUBARCH_X64=n + + +# +# Processor Type +# +CONFIG_CPU_X86_I486=n +CONFIG_CPU_X86_I586=n +CONFIG_CPU_X86_I686=n +CONFIG_CPU_X86_P4=y +CONFIG_CPU_X86_K8=n +CONFIG_CPU_X86_C3=n +CONFIG_CPU_X86_SIMICS=n + + +# +# Platform +# +CONFIG_PLAT_PC99=y + + +# +# Miscellaneous +# +CONFIG_IOAPIC=n +CONFIG_MAX_IOAPICS=8 +CONFIG_APIC_TIMER_TICK=1000 + +CONFIG_SMP=n +CONFIG_SMP_MAX_PROCS=8 +CONFIG_SMP_IDLE_POLL=n + + +# +# Kernel +# +CONFIG_EXPERIMENTAL=y + +# +# Experimental Features +# +CONFIG_X_PAGER_EXREGS=y +CONFIG_X_CTRLXFER_MSG=n +CONFIG_X_EVT_LOGGING=n + +# +# Kernel scheduling policy +# +CONFIG_SCHED_RR=y +CONFIG_X_SCHED_HS=n + + +CONFIG_IPC_FASTPATH=n +CONFIG_DEBUG=y +CONFIG_DEBUG_SYMBOLS=n +CONFIG_K8_FLUSHFILTER=n +CONFIG_PERFMON=n +CONFIG_SPIN_WHEELS=n +CONFIG_NEW_MDB=y +CONFIG_STATIC_TCBS=n +CONFIG_X86_SMALL_SPACES=n +CONFIG_X86_IO_FLEXPAGES=n + + +# +# Debugger +# + +# +# Kernel Debugger Console +# +CONFIG_KDB_CONS_COM=y +CONFIG_KDB_COMPORT=0x0 +CONFIG_KDB_COMSPEED=115200 +CONFIG_KDB_CONS_KBD=n +CONFIG_KDB_BOOT_CONS=0 + +CONFIG_KDB_DISAS=n +CONFIG_KDB_ON_STARTUP=n +CONFIG_KDB_BREAKIN=y +CONFIG_KDB_BREAKIN_BREAK=y +CONFIG_KDB_BREAKIN_ESCAPE=y +CONFIG_KDB_INPUT_HLT=n +CONFIG_KDB_NO_ASSERTS=n + +# +# Trace Settings +# +CONFIG_VERBOSE_INIT=y +CONFIG_TRACEPOINTS=y +CONFIG_KMEM_TRACE=n +CONFIG_TRACEBUFFER=y + + + +# +# Code Generator Options +# + + +# +# Derived symbols +# +CONFIG_HAVE_MEMORY_CONTROL=n +CONFIG_X86_PSE=y +CONFIG_BIGENDIAN=n +CONFIG_PPC_MMU_TLB=n +CONFIG_X86_SYSENTER=y +CONFIG_X86_PGE=y +CONFIG_X86_FXSR=y +CONFIG_IS_32BIT=y +CONFIG_X86_HTT=y +CONFIG_X86_PAT=y +CONFIG_PPC_BOOKE=n +CONFIG_IS_64BIT=n +CONFIG_MULTI_ARCHITECTURE=n +CONFIG_X86_EM64T=n +CONFIG_PPC_CACHE_L1_WRITETHROUGH=n +CONFIG_PPC_TLB_INV_LOCAL=n +CONFIG_PPC_CACHE_ICBI_LOCAL=n +CONFIG_X86_SMALL_SPACES_GLOBAL=n +CONFIG_X86_HVM=y +CONFIG_PPC_MMU_SEGMENTS=n +CONFIG_X86_TSC=y +# +# That's all, folks! diff --git a/base-pistachio/doc/pistachio.txt b/base-pistachio/doc/pistachio.txt new file mode 100644 index 000000000..a9ea9c7b7 --- /dev/null +++ b/base-pistachio/doc/pistachio.txt @@ -0,0 +1,78 @@ + + ========================================= + Genode on the L4ka::Pistachio microkernel + ========================================= + + + Norman Feske + + +Pistachio is the reference implementation of the L4 API version x.2 (also +referred to a v4). It is developed by the System Architecture Group at the +University of Karlsruhe, Germany and the DiSy group at the University of +New South Wales, Australia. + +Because this kernel has been the experimentation platform for a lot of exciting +research experiments at the L4ka group and it is the basis for the commercial +version of L4 developed by OK-Labs, Pistachio is a very interesting base +platform for the Genode OS Framework. + +The original port of the Genode OS Framework to Pistachio is the work of Julian +Stecklina who wanted to elaborate on the portability of the framework and +explore the use of Pistachio's multi-processor capabilities with Genode. + +This document provides brief instructions about downloading, building and +booting the Pistachio version of Genode. + + +Downloading, building, and using L4ka::Pistachio +################################################ + +Please make sure that you haved downloaded and installed the tool chain, +which will be used for both, the L4ka::Pistachio kernel and Genode. + +:[http://genode.org/download/tool-chain]: + Preconfigured GNU tool chain for building Genode + +To download the kernel source codes, issue 'make prepare' from within the +'base-pistachio' repository. This command will checkout the upstream Mercurial +repository of the kernel. Please make sure to have Mercurial installed. After +having successfully prepared the 'base-pistachio' repository, you are ready to +create a Genode build directory using the 'tool/create_builddir': + +! /tool/create_builddir pistachio_x86 \ +! BUILD_DIR= + +From within this directory, you can build the kernel by using 'make kernel'. +The kernel will be built within '/kernel/pistachio' using the Genode +tool chain. + +To build and start Genode directly from within the Genode build directory, +issue + +! make run/demo + +This command will execute the steps described in the run script located at +'os/run/demo.run'. It will build all Genode components needed for the demo +scenario, create a configuration, and start the scenario using Qemu. To inspect +the individual steps more closely or learn the steps needed to manually +integrate Genode with L4ka::Pistachio, please revisit the Pistachio-specific +run environment at 'base-pistachio/run/env'. + + +Using an externally supplied kernel +################################### + +It is possible to use a L4ka::Pistachio kernel that is manually downloaded +and built. To let Genode use an external kernel, create a file called +'/etc/pistachio.conf' with the following declarations: + +! PISTACHIO_USER_BUILD_DIR = + +The location of the Pistachio user-level build directory is the place where +Genode looks for the kernel-interface header files and the system bindings. + +! PISTACHIO_KERNEL = + +The specified kernel binary will be used when executing run scripts. + diff --git a/base-pistachio/etc/specs.conf b/base-pistachio/etc/specs.conf new file mode 100644 index 000000000..f426787da --- /dev/null +++ b/base-pistachio/etc/specs.conf @@ -0,0 +1 @@ +SPECS = genode pistachio_x86 diff --git a/base-pistachio/include/base/clock.h b/base-pistachio/include/base/clock.h new file mode 100644 index 000000000..3180ae589 --- /dev/null +++ b/base-pistachio/include/base/clock.h @@ -0,0 +1,37 @@ +/* + * \brief Timer interface + * \author Julian Stecklina + * \date 2007-12-30 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CLOCK_H_ +#define _INCLUDE__BASE__CLOCK_H_ + +#include + +namespace Genode { + + typedef uint64_t cycles_t; + + /** + * Returns the clock resolution in nanoseconds + */ + uint64_t clock_resolution(); + + /** + * Return the current time as nanoseconds + * + * The base of this value is unspecified, but the value should not + * wrap in at least several years. + */ + uint64_t get_time(); +} + +#endif /* _INCLUDE__BASE__CLOCK_H_ */ diff --git a/base-pistachio/include/base/ipc_msgbuf.h b/base-pistachio/include/base/ipc_msgbuf.h new file mode 100644 index 000000000..f910dc8ea --- /dev/null +++ b/base-pistachio/include/base/ipc_msgbuf.h @@ -0,0 +1,65 @@ +/* + * \brief Pistachio-specific layout of IPC message buffer + * \author Julian Stecklina + * \date 2007-01-10 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ +#define _INCLUDE__BASE__IPC_MSGBUF_H_ + +namespace Genode { + + /** + * IPC message buffer layout + */ + class Msgbuf_base + { + protected: + + size_t _size; + char _msg_start[]; /* symbol marks start of message */ + + public: + + /* + * Begin of message buffer layout + */ + Pistachio::L4_Fpage_t rcv_fpage; + /* Send message */ + /* Recv message */ + char buf[]; + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; }; + + /** + * Return address of message buffer + */ + inline void *addr() { return &_msg_start[0]; }; + }; + + + /** + * Instance of IPC message buffer with specified buffer size + */ + template + class Msgbuf : public Msgbuf_base + { + public: + + char buf[BUF_SIZE]; + + Msgbuf() { _size = BUF_SIZE; } + }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ diff --git a/base-pistachio/include/base/ipc_pager.h b/base-pistachio/include/base/ipc_pager.h new file mode 100644 index 000000000..88af210ca --- /dev/null +++ b/base-pistachio/include/base/ipc_pager.h @@ -0,0 +1,195 @@ +/* + * \brief Pistachio pager support for Genode + * \author Christian Helmuth + * \date 2006-06-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_PAGER_H_ +#define _INCLUDE__BASE__IPC_PAGER_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +} + +namespace Genode { + + class Mapping + { + private: + + union { + Pistachio::L4_MapItem_t _map_item; + Pistachio::L4_GrantItem_t _grant_item; + }; + + /* + * On Pistachio, the write-combining attribute is not part of a mapping + * but it can be applied to a flexpage via the memory-control system + * call. Therefore, we need to keep the flag in an extra member variable. + */ + bool _write_combined; /* enable write-combined access to I/O memory */ + + public: + + /** + * Constructor + */ + Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, + unsigned l2size = Pistachio::get_page_size_log2(), + bool rw = true, bool grant = false); + + /** + * Construct invalid mapping + */ + Mapping(); + + addr_t _dst_addr() const { return Pistachio::L4_SndBase(_map_item); } + + Pistachio::L4_Fpage_t fpage() const { + return Pistachio::L4_MapItemSndFpage(_map_item); } + + Pistachio::L4_MapItem_t map_item() const { return _map_item; }; + + /** + * Prepare map operation + * + * On Pistachio, we need to map a page locally to be able to map it + * to another address space. + */ + void prepare_map_operation() + { + using namespace Pistachio; + unsigned char volatile *core_local_addr = + (unsigned char volatile *)L4_Address(_map_item.X.snd_fpage); + + if (L4_Rights(_map_item.X.snd_fpage) & L4_Writable) + touch_read_write(core_local_addr); + else + touch_read(core_local_addr); + } + }; + + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + private: + + Pistachio::L4_ThreadId_t _last; /* origin of last fault message */ + Pistachio::L4_Word_t _flags; /* page-fault attributes */ + addr_t _pf_addr; /* page-fault address */ + addr_t _pf_ip; /* instruction pointer of faulter */ + Pistachio::L4_MapItem_t _map_item; /* page-fault answer */ + + protected: + + /** + * Wait for short-message (register) IPC -- pagefault + */ + void _wait(); + + /** + * Send short flex page and + * wait for next short-message (register) IPC -- pagefault + */ + void _reply_and_wait(); + + public: + + /** + * Constructor + */ + Ipc_pager(); + + /** + * Wait for a new fault received as short message IPC + */ + void wait_for_fault(); + + /** + * Reply current fault and wait for a new one + * + * Send short flex page and wait for next short-message (register) + * IPC -- pagefault + */ + void reply_and_wait_for_fault(); + + /** + * Request instruction pointer of current fault + */ + addr_t fault_ip() { return _pf_ip; } + + /** + * Request fault address of current page fault + */ + addr_t fault_addr() { return _pf_addr & ~3; } + + /** + * Set parameters for next reply + */ + void set_reply_mapping(Mapping m) { _map_item = m.map_item(); } + + /** + * Set destination for next reply + */ + void set_reply_dst(Native_capability pager_object) { + _last.raw = pager_object.local_name(); } + + /** + * Answer call without sending a flex-page mapping + * + * This function is used to acknowledge local calls from one of + * core's region-manager sessions. + */ + void acknowledge_wakeup(); + + /** + * Return thread ID of last faulter + */ + Native_thread_id last() const { return _last; } + + /** + * Return badge for faulting thread + * + * As L4v4 has no server-defined badges for fault messages, + * we interpret the sender ID as badge. + */ + unsigned long badge() const { return _last.raw; } + + /** + * Return true if last fault was a write fault + */ + bool is_write_fault() const { return (_flags & 2); } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + /* + * Reflection of exceptions is not supported on this platform. + */ + return false; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ diff --git a/base-pistachio/include/base/native_types.h b/base-pistachio/include/base/native_types.h new file mode 100644 index 000000000..68937713d --- /dev/null +++ b/base-pistachio/include/base/native_types.h @@ -0,0 +1,111 @@ +/* + * \brief Native types on Pistachio + * \author Norman Feske + * \date 2008-07-26 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_ +#define _INCLUDE__BASE__NATIVE_TYPES_H_ + +namespace Pistachio { +#include +} + +namespace Genode { + + typedef volatile int Native_lock; + + class Platform_thread; + + typedef Pistachio::L4_ThreadId_t Native_thread_id; + + struct Native_thread + { + Native_thread_id l4id; + + /** + * Only used in core + * + * For 'Thread' objects created within core, 'pt' points to + * the physical thread object, which is going to be destroyed + * on destruction of the 'Thread'. + */ + Platform_thread *pt; + }; + + inline unsigned long convert_native_thread_id_to_badge(Native_thread_id tid) + { + /* + * Pistachio has no server-defined badges for page-fault messages. + * Therefore, we have to interpret the sender ID as badge. + */ + return tid.raw; + } + + /** + * Empty UTCB type expected by the thread library + * + * On this kernel, UTCBs are not placed within the the context area. Each + * thread can request its own UTCB pointer using the kernel interface. + */ + typedef struct { } Native_utcb; + + /* + * On Pistachio, the local_name member of a capability is global to the + * whole system. Therefore, capabilities are to be created at a central + * place that prevents id clashes. + */ + class Native_capability + { + protected: + + Pistachio::L4_ThreadId_t _tid; + long _local_name; + + public: + + /** + * Default constructor + */ + Native_capability() : _local_name (0) + { + using namespace Pistachio; + _tid = L4_nilthread; + } + + long local_name() const { return _local_name; } + Pistachio::L4_ThreadId_t dst() const { return _tid; } + + bool valid() const { return !Pistachio::L4_IsNilThread(_tid); } + + + /******************************************************** + ** Functions to be used by the Pistachio backend only ** + ********************************************************/ + + /** + * Constructor + * + * Creates a L4 capability manually. This must not be called from + * generic code. + */ + Native_capability(Pistachio::L4_ThreadId_t tid, long local_name) + : _tid(tid), _local_name(local_name) { } + + /** + * Access raw capability data + */ + Pistachio::L4_ThreadId_t tid() const { return _tid; }; + }; + + typedef Pistachio::L4_ThreadId_t Native_connection_state; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ diff --git a/base-pistachio/include/pistachio/kip.h b/base-pistachio/include/pistachio/kip.h new file mode 100644 index 000000000..3372b5f8e --- /dev/null +++ b/base-pistachio/include/pistachio/kip.h @@ -0,0 +1,56 @@ +/* + * \brief Access to kernel info page (KIP) + * \author Julian Stecklina + * \date 2008-02-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PISTACHIO__KIP_H_ +#define _INCLUDE__PISTACHIO__KIP_H_ + +namespace Pistachio { +#include +#include + + /** + * Return a pointer to the kernel info page + */ + void *get_kip(); + + unsigned int get_page_size_log2(); + + L4_Word_t get_page_mask(); + + inline L4_Word_t get_page_size() + { + return 1<ThreadInfo.X.UserBase, 1); + } + + inline unsigned int get_user_base() + { + return ((L4_KernelInterfacePage_t *)get_kip())->ThreadInfo.X.UserBase; + } + + inline unsigned int get_threadno_bits() + { +#ifdef L4_32BIT + return 18; +#else +#error "Unsupported architecture." +#endif + } +} + +#endif /* _INCLUDE__PISTACHIO__KIP_H_ */ diff --git a/base-pistachio/include/pistachio/thread_helper.h b/base-pistachio/include/pistachio/thread_helper.h new file mode 100644 index 000000000..5afe4c2f3 --- /dev/null +++ b/base-pistachio/include/pistachio/thread_helper.h @@ -0,0 +1,45 @@ +/* + * \brief Pistachio-specific thread helper functions + * \author Julian Stecklina + * \date 2008-02-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PISTACHIO__THREAD_HELPER_H_ +#define _INCLUDE__PISTACHIO__THREAD_HELPER_H_ + +#include + +namespace Pistachio +{ +#include + + inline void print_l4_threadid(L4_ThreadId_t t) + { + if (L4_IsLocalId(t)) { + Genode::printf("THREAD (local) %02lx (raw %08lx)\n", + t.local.X.local_id, t.raw); + + } else if (L4_IsGlobalId(t)) { + Genode::printf("THREAD (global) %02lx (version %lx) (raw %08lx)\n", + t.global.X.thread_no, t.global.X.version, t.raw); + + } else { + const char *name; + + if (t == L4_nilthread) name = "nilthread"; + else if (t == L4_anythread) name = "anythread"; + else name = "???"; + + Genode::printf("THREAD (%s)\n", name); + } + } +} + +#endif /* _INCLUDE__PISTACHIO__THREAD_HELPER_H_ */ diff --git a/base-pistachio/include/util/hexdump.h b/base-pistachio/include/util/hexdump.h new file mode 100644 index 000000000..dd345036b --- /dev/null +++ b/base-pistachio/include/util/hexdump.h @@ -0,0 +1,39 @@ +/* + * \brief Hexdump utility + * \author Julian Stecklina + * \date 2008-02-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__HEXDUMP_H_ +#define _INCLUDE__UTIL__HEXDUMP_H_ + +namespace Util { + + /** + * Dump a block of memory in a nice way to the terminal. + * + * \param addr the memory address to start dump + * \param length the amount of bytes to be dumped + */ + void hexdump(const unsigned char *addr, + unsigned long length); + + /** + * Exactly like hexdump, but prints real_addr instead of addr as address + * + * \param addr the memory address to start dump + * \param length the amount of bytes to be dumped + */ + void hexdump(const unsigned char *addr, + unsigned long length, + unsigned long real_addr); +} + +#endif /* _INCLUDE__UTIL__HEXDUMP_H_ */ diff --git a/base-pistachio/include/x86/cpu/rdtsc.h b/base-pistachio/include/x86/cpu/rdtsc.h new file mode 100644 index 000000000..12e9db102 --- /dev/null +++ b/base-pistachio/include/x86/cpu/rdtsc.h @@ -0,0 +1,30 @@ +/* + * \brief Read time-stamp counter + * \author Norman Feske + * \date 2008-11-29 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__X86__CPU__RDTSC_H_ +#define _INCLUDE__X86__CPU__RDTSC_H_ + +#include + +namespace Genode { + + static inline cycles_t rdtsc() + { + uint32_t lo, hi; + /* We cannot use "=A", since this would use %rax on x86_64 */ + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (uint64_t)hi << 32 | lo; + } +} + +#endif /* _INCLUDE__X86__CPU__RDTSC_H_ */ diff --git a/base-pistachio/include/x86/util/smath.h b/base-pistachio/include/x86/util/smath.h new file mode 100644 index 000000000..662611d0b --- /dev/null +++ b/base-pistachio/include/x86/util/smath.h @@ -0,0 +1,54 @@ +/* + * \brief Simple math calls + * \author Julian Stecklina + * \date 2008-02-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +namespace SMath { + + static inline float sinf(float x) + { + float res; + + asm ("fsin" + : "=t" (res) /* output */ + : "0" (x) /* input */ + : /* clobbers */ + ); + + return res; + } + + static inline float cosf(float x) + { + float res; + + asm ("fcos" + : "=t" (res) /* output */ + : "0" (x) /* input */ + : /* clobbers */ + ); + + return res; + } + + static inline float sqrtf(float x) + { + float res; + + asm ("fsqrt" + : "=t" (res) /* output */ + : "0" (x) /* input */ + : /* clobbers */ + ); + + return res; + } +} diff --git a/base-pistachio/lib/mk/core_printf.mk b/base-pistachio/lib/mk/core_printf.mk new file mode 100644 index 000000000..663cf64b9 --- /dev/null +++ b/base-pistachio/lib/mk/core_printf.mk @@ -0,0 +1,5 @@ +SRC_CC = core_printf.cc +LIBS = cxx console +INC_DIR += $(REP_DIR)/src/base/console + +vpath core_printf.cc $(BASE_DIR)/src/base/console diff --git a/base-pistachio/lib/mk/hexdump.mk b/base-pistachio/lib/mk/hexdump.mk new file mode 100644 index 000000000..c1725471e --- /dev/null +++ b/base-pistachio/lib/mk/hexdump.mk @@ -0,0 +1,3 @@ +SRC_CC = hexdump.cc + +vpath hexdump.cc $(REP_DIR)/src/util/hexdump diff --git a/base-pistachio/lib/mk/ipc.mk b/base-pistachio/lib/mk/ipc.mk new file mode 100644 index 000000000..6e6443bb9 --- /dev/null +++ b/base-pistachio/lib/mk/ipc.mk @@ -0,0 +1,3 @@ +SRC_CC = ipc.cc pager.cc + +vpath %.cc $(REP_DIR)/src/base/ipc diff --git a/base-pistachio/lib/mk/kip.mk b/base-pistachio/lib/mk/kip.mk new file mode 100644 index 000000000..53fbf85b9 --- /dev/null +++ b/base-pistachio/lib/mk/kip.mk @@ -0,0 +1,5 @@ +REQUIRES = pistachio +SRC_CC = kip.cc +LIBS = cxx + +vpath %.cc $(REP_DIR)/src/base/kip diff --git a/base-pistachio/lib/mk/l4.mk b/base-pistachio/lib/mk/l4.mk new file mode 100644 index 000000000..564a78ced --- /dev/null +++ b/base-pistachio/lib/mk/l4.mk @@ -0,0 +1,11 @@ +# +# Create symlink to Pistachio's user library +# +# +-include $(BUILD_BASE_DIR)/etc/pistachio.conf + +absdir = $(realpath $(shell find $(1) -maxdepth 0 -type d)) +PISTACHIO_USER_BUILD_ABS_DIR = $(call absdir,$(PISTACHIO_USER_BUILD_DIR)) + +$(shell mkdir -p $(LIB_CACHE_DIR)/l4) +$(shell ln -sf $(PISTACHIO_USER_BUILD_ABS_DIR)/lib/libl4.a $(LIB_CACHE_DIR)/l4/l4.lib.a) diff --git a/base-pistachio/lib/mk/lock.mk b/base-pistachio/lib/mk/lock.mk new file mode 100644 index 000000000..a79c1d9a1 --- /dev/null +++ b/base-pistachio/lib/mk/lock.mk @@ -0,0 +1,4 @@ +SRC_CC = lock.cc +INC_DIR += $(REP_DIR)/src/base/lock + +vpath lock.cc $(BASE_DIR)/src/base/lock diff --git a/base-pistachio/lib/mk/pager.mk b/base-pistachio/lib/mk/pager.mk new file mode 100644 index 000000000..1c3f6ed1a --- /dev/null +++ b/base-pistachio/lib/mk/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc + +vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-pistachio/lib/mk/platform.mk b/base-pistachio/lib/mk/platform.mk new file mode 100644 index 000000000..efb3cc3f5 --- /dev/null +++ b/base-pistachio/lib/mk/platform.mk @@ -0,0 +1,25 @@ +# +# Create prerequisites for building Genode for Pistachio +# + +# +# Execute the rules in this file only at the second build stage when we know +# about the complete build settings, e.g., the 'CROSS_DEV_PREFIX'. +# +ifeq ($(called_from_lib_mk),yes) + +all: $(filter-out $(wildcard $(PISTACHIO_USER_BUILD_DIR)), $(PISTACHIO_USER_BUILD_DIR)) + +LD_PREFIX = "-Wl," + +$(PISTACHIO_USER_BUILD_DIR): + $(VERBOSE)mkdir $@ + $(VERBOSE)cd $@; \ + LIBGCCFLAGS="$(CC_MARCH)" \ + LDFLAGS="$(addprefix $(LD_PREFIX),$(LD_MARCH)) -nostdlib" \ + CFLAGS="$(CC_MARCH)" \ + $(REP_DIR)/contrib/user/configure --build=ia32 --host i686 \ + CC=$(CROSS_DEV_PREFIX)gcc + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) $(VERBOSE_DIR) -C $@ + +endif diff --git a/base-pistachio/lib/mk/x86/startup.mk b/base-pistachio/lib/mk/x86/startup.mk new file mode 100644 index 000000000..8cf5e4f08 --- /dev/null +++ b/base-pistachio/lib/mk/x86/startup.mk @@ -0,0 +1,8 @@ +REQUIRES = pistachio x86 +LIBS = cxx lock +SRC_S = crt0.s +SRC_CC = _main.cc +INC_DIR += $(BASE_DIR)/src/platform $(REP_DIR)/src/platform + +vpath crt0.s $(dir $(call select_from_repositories,src/platform/x86_32/crt0.s)) +vpath _main.cc $(dir $(call select_from_repositories,src/platform/_main.cc)) diff --git a/base-pistachio/mk/spec-pistachio.mk b/base-pistachio/mk/spec-pistachio.mk new file mode 100644 index 000000000..6e5603ea1 --- /dev/null +++ b/base-pistachio/mk/spec-pistachio.mk @@ -0,0 +1,41 @@ +# +# Specifics for the pistachio kernel API +# + +# +# Read default and builddir-specific config files +# +# In these config files, we expect to find the definition of PISTACHIO_USER_BUILD_DIR +# +-include $(call select_from_repositories,etc/pistachio.conf) +-include $(BUILD_BASE_DIR)/etc/pistachio.conf + +PISTACHIO_USER_BUILD_DIR ?= $(BUILD_BASE_DIR)/l4 + +# +# Pistachio headers +# +INC_DIR += $(PISTACHIO_USER_BUILD_DIR)/include + +# +# Pistachio-specific Genode headers +# +REP_INC_DIR += include/pistachio + +# +# Startup code to be used when building a program +# +STARTUP_LIB ?= startup +PRG_LIBS += $(STARTUP_LIB) + +# +# Linker options +# +CXX_LINK_OPT += -L$(PISTACHIO_USER_BUILD_DIR)/lib +EXT_OBJECTS += -ll4 + +clean_contrib: + $(VERBOSE)rm -rf $(BUILD_BASE_DIR)/l4 + +cleanall: clean_contrib + diff --git a/base-pistachio/mk/spec-pistachio_x86.mk b/base-pistachio/mk/spec-pistachio_x86.mk new file mode 100644 index 000000000..1a77e27c5 --- /dev/null +++ b/base-pistachio/mk/spec-pistachio_x86.mk @@ -0,0 +1,14 @@ +# +# Specifics for Pistachio on 32-bit x86 +# + +SPECS += x86_32 pistachio +SPECS += pci ps2 vesa + +# +# Linker options that are specific for x86 +# +LD_TEXT_ADDR ?= 0x00300000 + +include $(call select_from_repositories,mk/spec-x86_32.mk) +include $(call select_from_repositories,mk/spec-pistachio.mk) diff --git a/base-pistachio/patches/README b/base-pistachio/patches/README new file mode 100644 index 000000000..953f3a8a5 --- /dev/null +++ b/base-pistachio/patches/README @@ -0,0 +1,20 @@ +This directory contains patches for the Pistachio microkernel + +:'syscalls_ia32.patch': + + GCC 4.6 switches from base-pointer-relative addressing to stack-pointer- + relative addressing for memory-input constraints of inline assembler. Therefore + the syscall bindings are adapted to these requirements. + +Applying the patches +-------------------- + +To apply a patch to the Pistachio kernel, use the 'patch' command. First check +the directory given at the header of the patch. It may contain a directory +prefix (such as 'a/'), which does not actually exist. This prefix is usually +generated by the tool used to create the patch. In this case, use the '-p' +option of the patch command. To apply the patch with the first part of the +path stripped, issue the following command (make sure that you changed to +the base directory of the Pistachio kernel): + +! patch -p1 < /path/to/utcb.patch diff --git a/base-pistachio/patches/syscalls_ia32.patch b/base-pistachio/patches/syscalls_ia32.patch new file mode 100644 index 000000000..13fae3661 --- /dev/null +++ b/base-pistachio/patches/syscalls_ia32.patch @@ -0,0 +1,280 @@ +diff --git a/user/include/l4/ia32/syscalls.h b/user/include/l4/ia32/syscalls.h +index 56820e3..2307ed9 100644 +--- a/user/include/l4/ia32/syscalls.h ++++ b/user/include/l4/ia32/syscalls.h +@@ -47,6 +47,13 @@ + # define __L4_CLOBBER_REGS "ebx", "cc" + #endif + ++/* ++ * This expects the '__L4_indirect' struct in 'edi' ++ */ ++#define __L4_INDIRECT_CALL " movl 4(%%edi), %%ebx \n" \ ++ " movl (%%edi), %%edi \n" \ ++ " call *%%ebx \n" ++ + #ifdef __cplusplus + #define _C_ "C" + #else +@@ -70,6 +77,13 @@ extern _C_ void __L4_SpaceControl(void); + extern _C_ void __L4_ProcessorControl(void); + extern _C_ void __L4_MemoryControl(void); + ++typedef struct __L4_indirect ++{ ++ L4_Word_t edi; ++ void (*sys_call)(); ++} __L4_indirect; ++ ++ + L4_INLINE void * L4_KernelInterface (L4_Word_t *ApiVersion, + L4_Word_t *ApiFlags, + L4_Word_t *KernelId) +@@ -163,12 +177,14 @@ L4_INLINE L4_Word_t L4_ThreadControl (L4_ThreadId_t dest, + L4_Word_t result; + L4_Word_t dummy; + ++ __L4_indirect in; ++ in.edi = (L4_Word_t)UtcbLocation; ++ in.sys_call = __L4_ThreadControl; ++ + __asm__ __volatile__ ( + "/* L4_ThreadControl() */ \n" + __L4_SAVE_REGS +- " movl %%edi, %%ebx \n" +- " movl %9, %%edi \n" +- " call *%%ebx \n" ++ __L4_INDIRECT_CALL + __L4_RESTORE_REGS + + : /* outputs */ +@@ -183,11 +199,10 @@ L4_INLINE L4_Word_t L4_ThreadControl (L4_ThreadId_t dest, + "1" (Pager), + "2" (Scheduler), + "3" (SpaceSpecifier), +- "m" (UtcbLocation), +- "D" (__L4_ThreadControl) ++ "4" (&in) + + : /* clobbers */ +- __L4_CLOBBER_REGS); ++ __L4_CLOBBER_REGS, "memory"); + + return result; + } +@@ -239,12 +254,14 @@ L4_INLINE L4_Word_t L4_Schedule (L4_ThreadId_t dest, + L4_Word_t result; + L4_Word_t dummy; + ++ __L4_indirect in; ++ in.edi = PreemptionControl; ++ in.sys_call = __L4_Schedule; ++ + __asm__ __volatile__ ( + "/* L4_Schedule() */ \n" + __L4_SAVE_REGS +- " movl %%edi, %%ebx \n" +- " movl %9, %%edi \n" +- " call *%%ebx \n" ++ __L4_INDIRECT_CALL + __L4_RESTORE_REGS + + : /* outputs */ +@@ -259,11 +276,11 @@ L4_INLINE L4_Word_t L4_Schedule (L4_ThreadId_t dest, + "1" (PrioControl), + "2" (TimeControl), + "3" (ProcessorControl), +- "m" (PreemptionControl), +- "D" (__L4_Schedule) ++ "4" (&in) + + : /* clobbers */ +- __L4_CLOBBER_REGS); ++ __L4_CLOBBER_REGS, "memory"); ++ + return result; + } + +@@ -278,14 +295,15 @@ L4_INLINE L4_MsgTag_t L4_Ipc (L4_ThreadId_t to, + L4_Word_t * utcb = __L4_X86_Utcb (); + + #if defined(__pic__) ++ __L4_indirect in; ++ in.edi = (L4_Word_t)utcb; ++ in.sys_call = __L4_Ipc; + + __asm__ __volatile__ ( + "/* L4_Ipc() */ \n" + __L4_SAVE_REGS +- " movl %%eax, %%ebx \n" +- " movl %5, %%eax \n" +- " call *%%ebx \n" +- " movl %%ebp, %%ecx \n" ++ __L4_INDIRECT_CALL ++ " movl %%ebp, %%ecx \n" + " movl %%ebx, %%edx \n" + __L4_RESTORE_REGS + +@@ -297,12 +315,12 @@ L4_INLINE L4_MsgTag_t L4_Ipc (L4_ThreadId_t to, + + : /* inputs */ + "S" (utcb[0]), +- "m" (to.raw), +- "D" (utcb), ++ "a" (to.raw), ++ "D" (&in), + "c" (Timeouts), +- "d" (FromSpecifier), +- "a" (__L4_Ipc) +- ); ++ "d" (FromSpecifier) ++ ++ : "memory"); + + #else + L4_Word_t dummy; +@@ -319,7 +337,7 @@ L4_INLINE L4_MsgTag_t L4_Ipc (L4_ThreadId_t to, + "=a" (result), + "=b" (mr1), + "=c" (mr2), +- "=d" (dummy) ++ "=d" (dummy) + + : /* inputs */ + "S" (utcb[0]), +@@ -351,12 +369,15 @@ L4_INLINE L4_MsgTag_t L4_Lipc (L4_ThreadId_t to, + L4_Word_t * utcb = __L4_X86_Utcb (); + + #if defined(__pic__) ++ ++ __L4_indirect in; ++ in.edi = (L4_Word_t)utcb; ++ in.sys_call = __L4_Lipc; ++ + __asm__ __volatile__ ( + "/* L4_Lipc() */ \n" + __L4_SAVE_REGS +- " movl %%eax, %%ebx \n" +- " movl %5, %%eax \n" +- " call *%%ebx \n" ++ __L4_INDIRECT_CALL + " movl %%ebp, %%ecx \n" + " movl %%ebx, %%edx \n" + __L4_RESTORE_REGS +@@ -366,15 +387,15 @@ L4_INLINE L4_MsgTag_t L4_Lipc (L4_ThreadId_t to, + "=a" (result), + "=d" (mr1), + "=c" (mr2) +- ++ + : /* inputs */ + "S" (utcb[0]), +- "m" (to.raw), +- "D" (utcb), ++ "a" (to.raw), ++ "D" (&in), + "c" (Timeouts), +- "d" (FromSpecifier), +- "a" (__L4_Lipc) +- ); ++ "d" (FromSpecifier) ++ ++ : "memory"); + #else + L4_Word_t dummy; + +@@ -382,7 +403,7 @@ L4_INLINE L4_MsgTag_t L4_Lipc (L4_ThreadId_t to, + "/* L4_Lipc() */ \n" + __L4_SAVE_REGS + " call __L4_Lipc \n" +- " movl %%ebp, %%ecx \n" ++ " movl %%ebp, %%ecx \n" + __L4_RESTORE_REGS + + : /* outputs */ +@@ -390,8 +411,8 @@ L4_INLINE L4_MsgTag_t L4_Lipc (L4_ThreadId_t to, + "=a" (result), + "=b" (mr1), + "=c" (mr2), +- "=d" (dummy) +- ++ "=d" (dummy) ++ + : /* inputs */ + "S" (utcb[0]), + "a" (to.raw), +@@ -446,12 +467,14 @@ L4_INLINE L4_Word_t L4_SpaceControl (L4_ThreadId_t SpaceSpecifier, + { + L4_Word_t result, dummy; + ++ __L4_indirect in; ++ in.edi = redirector.raw; ++ in.sys_call = __L4_SpaceControl; ++ + __asm__ __volatile__ ( + "/* L4_SpaceControl() */ \n" + __L4_SAVE_REGS +- " movl %%edi, %%ebx \n" +- " movl %9, %%edi \n" +- " call *%%ebx \n" ++ __L4_INDIRECT_CALL + __L4_RESTORE_REGS + + : /* outputs */ +@@ -466,11 +489,10 @@ L4_INLINE L4_Word_t L4_SpaceControl (L4_ThreadId_t SpaceSpecifier, + "1" (control), + "2" (KernelInterfacePageArea), + "3" (UtcbArea), +- "m" (redirector), +- "D" (__L4_SpaceControl) ++ "4" (&in) + + : /* clobbers */ +- __L4_CLOBBER_REGS); ++ __L4_CLOBBER_REGS, "memory"); + + return result; + } +@@ -516,22 +538,23 @@ L4_INLINE L4_Word_t L4_MemoryControl (L4_Word_t control, + L4_Word_t result, dummy; + L4_Word_t * utcb = __L4_X86_Utcb (); + ++ __L4_indirect in; ++ in.edi = (L4_Word_t)utcb; ++ in.sys_call = __L4_MemoryControl; ++ + __asm__ __volatile__ ( + "/* L4_MemoryControl() */ \n" + __L4_SAVE_REGS +- " pushl %%edi \n" +- " movl %8, %%edi \n" + " movl 12(%6), %%ebp \n" + " movl 8(%6), %%ebx \n" + " movl 4(%6), %%edx \n" + " movl (%6), %%ecx \n" +- " call *(%%esp) \n" +- " popl %%edi \n" ++ __L4_INDIRECT_CALL + __L4_RESTORE_REGS + + : /* outputs */ + "=a" (result), +- "=c" (dummy), ++ "=c" (dummy), + "=d" (dummy), + "=S" (dummy), + "=D" (dummy) +@@ -540,11 +563,10 @@ L4_INLINE L4_Word_t L4_MemoryControl (L4_Word_t control, + "0" (control), + "1" (attributes), + "3" (utcb[0]), +- "m" (utcb), +- "4" (__L4_MemoryControl) ++ "4" (&in) + + : /* clobbers */ +- __L4_CLOBBER_REGS); ++ __L4_CLOBBER_REGS, "memory"); + + return result; + } diff --git a/base-pistachio/run/env b/base-pistachio/run/env new file mode 100644 index 000000000..0d7862eea --- /dev/null +++ b/base-pistachio/run/env @@ -0,0 +1,108 @@ +# +# \brief Pistachio-specific test-environment supplements +# \author Norman Feske +# \date 2010-08-25 +# +# This file is meant to be used as '--include' argument for 'tool/run'. +# + + +## +# Read the location of the Pistachio user directory from 'etc/pistachio.conf' +# +proc pistachio_user_dir { } { + global _pistachio_user_dir + + if {![info exists _pistachio_user_dir]} { + if {[file exists etc/pistachio.conf]} { + set _pistachio_user_dir [exec sed -n "/^PISTACHIO_USER_BUILD_DIR/s/^.*=\\s*//p" etc/pistachio.conf] + } else { + set _pistachio_user_dir "[pwd]/l4" + } + } + return $_pistachio_user_dir +} + + +## +# Read the location of the Pistachio kernel directory from 'etc/pistachio.conf' +# or return a good heuristic +# +proc pistachio_kernel { } { + global _pistachio_kernel + + if {![info exists _pistachio_kernel]} { + if {[file exists etc/pistachio.conf]} { + set _pistachio_kernel [exec sed -n "/^PISTACHIO_KERNEL/s/^.*=\\s*//p" etc/pistachio.conf] + if {$_pistachio_kernel == ""} { + set _pistachio_kernel [file dirname [file dirname [pistachio_user_dir]]]/kernel/build/x86-kernel + } + } else { + set _pistachio_kernel "[pwd]/bin/kernel" + } + } + return $_pistachio_kernel +} + + +## +# Return whether the kernel is provided from the outside +# +proc kernel_external { } { + if {[pistachio_kernel] == "[pwd]/bin/kernel"} { return 0 } + return 1 +} + + +################################## +## Test framework API functions ## +################################## + +proc create_boot_directory { } { + exec rm -rf [run_dir] + exec mkdir -p [run_dir]/genode + exec mkdir -p [run_dir]/pistachio +} + + +proc build_boot_image {binaries} { + + # + # Collect contents of the ISO image + # + copy_and_strip_genode_binaries_to_run_dir $binaries + + if {![kernel_external] && ![file exists [pistachio_kernel]]} { build { kernel } } + + exec cp [pistachio_kernel] [run_dir]/pistachio/kernel + exec cp [pistachio_user_dir]/serv/sigma0/sigma0 [run_dir]/pistachio + exec cp [pistachio_user_dir]/util/kickstart/kickstart [run_dir]/pistachio + + install_iso_bootloader_to_run_dir + + # + # Generate grub config file + # + # The core binary is part of the 'binaries' list but it must + # appear right after 'sigma0' as boot module. Hence the special case. + # + set fh [open "[run_dir]/boot/grub/menu.lst" "WRONLY CREAT TRUNC"] + puts $fh "timeout 0" + puts $fh "default 0" + puts $fh "\ntitle Genode on L4ka::Pistachio" + puts $fh " kernel /pistachio/kickstart" + puts $fh " module /pistachio/kernel" + puts $fh " module /pistachio/sigma0" + puts $fh " module /genode/core" + puts $fh " module /genode/config" + foreach binary $binaries { + if {$binary != "core"} { + puts $fh " module /genode/$binary" } } + close $fh + + create_iso_image_from_run_dir +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + spawn_qemu $wait_for_re $timeout_value } diff --git a/base-pistachio/src/base/console/core_console.h b/base-pistachio/src/base/console/core_console.h new file mode 100644 index 000000000..74a852c40 --- /dev/null +++ b/base-pistachio/src/base/console/core_console.h @@ -0,0 +1,31 @@ +/* + * \brief Console backend for Pistachio + * \author Julian Stecklina + * \date 2008-08-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Pistachio includes */ +namespace Pistachio { +#include +} + +/* Genode includes */ +#include + +namespace Genode +{ + class Core_console : public Console + { + protected: + + void _out_char(char c) { Pistachio::L4_KDB_PrintChar(c); } + }; +} + diff --git a/base-pistachio/src/base/ipc/ipc.cc b/base-pistachio/src/base/ipc/ipc.cc new file mode 100644 index 000000000..fd3ea4963 --- /dev/null +++ b/base-pistachio/src/base/ipc/ipc.cc @@ -0,0 +1,355 @@ +/* + * \brief IPC implementation for Pistachio + * \author Julian Stecklina + * \author Norman Feske + * \date 2008-01-28 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include + +namespace Pistachio { +#include +#include +#include +} + +using namespace Genode; +using namespace Pistachio; + +#define VERBOSE_IPC 0 +#if VERBOSE_IPC + +/* Just a printf wrapper for now. */ +#define IPCDEBUG(msg, ...) { \ + if (L4_Myself().raw == 0xf4001) { \ + (void)printf("IPC (thread = 0x%x) " msg, \ + L4_ThreadNo(Pistachio::L4_Myself()) \ + , ##__VA_ARGS__); \ + } else {} +} +#else +#define IPCDEBUG(...) +#endif + +/***************** + ** Ipc_ostream ** + *****************/ + +void Ipc_ostream::_send() +{ + IPCDEBUG("_send to 0x%08lx.\n", _dst.tid().raw); + + L4_Msg_t msg; + L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); + L4_Word_t local_name = _dst.local_name(); + + L4_Clear(&msg); + + L4_Append(&msg, local_name); + L4_Append(&msg, sitem); + L4_Load(&msg); + + L4_MsgTag_t result = L4_Send(_dst.tid()); + + /* + * Error indicator + * TODO Check what happened and print a nicer error message. + */ + if (L4_IpcFailed(result)) { + PERR("ipc error in _send."); + throw Genode::Ipc_error(); + } + + IPCDEBUG("_send successful\n"); + _write_offset = sizeof(umword_t); +} + + +Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg) : + Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()), + _snd_msg(snd_msg), _dst(dst) +{ + _write_offset = sizeof(umword_t); + IPCDEBUG("Ipc_ostream constructed.\n"); +} + + +/***************** + ** Ipc_istream ** + *****************/ + +/** + * Assert that we got 1 untyped word and 2 typed words + */ +static inline void check_ipc_result(L4_MsgTag_t result, L4_Word_t error_code) +{ + /* + * Test for IPC cancellation via Core's cancel-blocking mechanism + */ + enum { ERROR_MASK = 0xe, ERROR_CANCELED = 3 << 1 }; + if (L4_IpcFailed(result) && + ((L4_ErrorCode() & ERROR_MASK) == ERROR_CANCELED)) + throw Genode::Blocking_canceled(); + + /* + * Provide diagnostic information on unexpected conditions + */ + if (L4_IpcFailed(result)) { + PERR("Error in thread %08lx. IPC failed.", L4_Myself().raw); + throw Genode::Ipc_error(); + } + + if (L4_UntypedWords(result) != 1) { + PERR("Error in thread %08lx. Expected one untyped word (local_name), but got %lu.\n", + L4_Myself().raw, L4_UntypedWords(result)); + + PERR("This should not happen. Inspect!"); + throw Genode::Ipc_error(); + } + if (L4_TypedWords(result) != 2) { + PERR("Error. Expected two typed words (a string item). but got %lu.\n", + L4_TypedWords(result)); + PERR("This should not happen. Inspect!"); + throw Genode::Ipc_error(); + } +} + + +void Ipc_istream::_wait() +{ + L4_MsgTag_t result; + L4_MsgBuffer_t msgbuf; + + IPCDEBUG("_wait.\n"); +retry: + + IPCDEBUG("_wait loop start (more than once means IpcError)\n"); + + L4_Clear (&msgbuf); + L4_Append (&msgbuf, L4_StringItem (_rcv_msg->size(), _rcv_msg->buf)); + L4_Accept(L4_UntypedWordsAcceptor); + L4_Accept(L4_StringItemsAcceptor, &msgbuf); + + // Wait for message. + result = L4_Wait(&_rcv_cs); + + if (L4_IpcFailed(result)) + goto retry; + + IPCDEBUG("Got something from 0x%x.\n", _rcv_cs); + L4_Msg_t msg; + + L4_Store(result, &msg); + + check_ipc_result(result, L4_ErrorCode()); + + /* get the local name */ + L4_Word_t local_name = L4_Get(&msg,0); + + /* + * Store local_name where badge() looks for it. + * XXX Check this... + */ + *((long *)_rcv_msg->buf) = local_name; + _read_offset = sizeof(umword_t); + + IPCDEBUG("_wait successful\n"); +} + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) : + Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), + Native_capability(Pistachio::L4_Myself(), 0), + _rcv_msg(rcv_msg) +{ + IPCDEBUG("Ipc_istream constructed.\n"); + _rcv_cs = L4_nilthread; + _read_offset = sizeof(umword_t); +} + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() +{ + IPCDEBUG("Starting to _call (with %u bytes of data).\n", _write_offset); + L4_Msg_t msg; + L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); + L4_Word_t local_name = _dst.local_name(); + + IPCDEBUG("Destination local_name = 0x%x\n", local_name); + + L4_MsgBuffer_t msgbuf; + + /* prepare message buffer */ + L4_Clear (&msgbuf); + L4_Append (&msgbuf, L4_StringItem (_rcv_msg->size(), _rcv_msg->buf)); + L4_Accept(L4_UntypedWordsAcceptor); + L4_Accept(L4_StringItemsAcceptor, &msgbuf); + + /* prepare sending parameters */ + L4_Clear(&msg); + L4_Append(&msg, local_name); + L4_Append(&msg, sitem); + L4_Load(&msg); + + L4_MsgTag_t result = L4_Call(_dst.tid()); + + _write_offset = _read_offset = sizeof(umword_t); + + check_ipc_result(result, L4_ErrorCode()); + + IPCDEBUG("Call done.\n"); +} + + +Ipc_client::Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg) : + Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) +{ + IPCDEBUG("Ipc_client constructed.\n"); +} + + +/**************** + ** Ipc_server ** + ****************/ + +void Ipc_server::_prepare_next_reply_wait() +{ + /* now we have a request to reply */ + _reply_needed = true; + + /* leave space for return value at the beginning of the msgbuf */ + _write_offset = 2*sizeof(umword_t); + + /* receive buffer offset */ + _read_offset = sizeof(umword_t); +} + + +void Ipc_server::_wait() +{ + /* wait for new server request */ + try { Ipc_istream::_wait(); } catch (Blocking_canceled) { } + + /* define destination of next reply */ + _dst = Native_capability(_rcv_cs, badge()); + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() +{ + L4_Msg_t msg; + L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); + L4_Word_t local_name = _dst.local_name(); + + L4_Clear(&msg); + L4_Append(&msg, local_name); + L4_Append(&msg, sitem); + L4_Load(&msg); + + L4_MsgTag_t result = L4_Reply(_dst.tid()); + if (L4_IpcFailed(result)) + PERR("ipc error in _reply, ignored"); + + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply_wait() +{ + IPCDEBUG("Starting to _reply_wait. (with %u bytes of data)\n", + _reply_needed ? _write_offset : 0); + + if (_reply_needed) { + + /* prepare massage */ + L4_Msg_t msg; + L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); + L4_Word_t local_name = _dst.local_name(); + + L4_Clear(&msg); + L4_Append(&msg, local_name); + L4_Append(&msg, sitem); + L4_Load(&msg); + + /* Prepare message buffer */ + L4_MsgBuffer_t msgbuf; + L4_Clear(&msgbuf); + L4_Append(&msgbuf, L4_StringItem (_rcv_msg->size(), _rcv_msg->buf)); + L4_Accept(L4_UntypedWordsAcceptor); + L4_Accept(L4_StringItemsAcceptor, &msgbuf); + + L4_MsgTag_t result = L4_Ipc(_dst.tid(), L4_anythread, L4_Timeouts(L4_ZeroTime, L4_Never), &_rcv_cs); + IPCDEBUG("Got something from 0x%x.\n", L4_ThreadNo(L4_GlobalId(_rcv_cs))); + + /* error handling - check whether send or receive failed */ + if (L4_IpcFailed(result)) { + L4_Word_t errcode = L4_ErrorCode(); + L4_Word_t phase = errcode & 1; + L4_Word_t error = (errcode & 0xF) >> 1; + + PERR("IPC %s error %02lx, offset %08lx -> _wait() instead.", + phase ? "receive" : "send", error, errcode >> 4); + _wait(); + return; + } + + L4_Clear(&msg); + L4_Store(result, &msg); + + try { + check_ipc_result(result, L4_ErrorCode()); + } catch (...) { + /* + * If something went wrong, just call _wait instead of relaying + * the error to the user. + */ + IPCDEBUG("Bad IPC content -> _wait() instead.\n"); + _wait(); + return; + } + + /* get the local name */ + local_name = L4_Get(&msg, 0); + + /* + * Store local_name where badge() looks for it. + * XXX Check this... + */ + *((long *)_rcv_msg->buf) = local_name; + IPCDEBUG("local_name = 0x%lx\n", badge()); + + /* define destination of next reply */ + _dst = Native_capability(_rcv_cs, badge()); + + _prepare_next_reply_wait(); + + } else + _wait(); +} + + +Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg) : + Ipc_istream(rcv_msg), + Ipc_ostream(Native_capability(), snd_msg), + _reply_needed(false) +{ } diff --git a/base-pistachio/src/base/ipc/pager.cc b/base-pistachio/src/base/ipc/pager.cc new file mode 100644 index 000000000..15064525e --- /dev/null +++ b/base-pistachio/src/base/ipc/pager.cc @@ -0,0 +1,139 @@ +/* + * \brief Pager support for Pistachio + * \author Christian Helmuth + * \date 2006-06-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +namespace Pistachio +{ +#include +#include +#include +#include +} + +using namespace Genode; +using namespace Pistachio; + + +/************* + ** Mapping ** + *************/ + +Mapping::Mapping(addr_t dst_addr, addr_t src_addr, + bool write_combined, unsigned l2size, bool rw, bool grant) +: + _write_combined(write_combined) +{ + L4_Fpage_t fpage = L4_FpageLog2(src_addr, l2size); + + fpage += rw ? L4_FullyAccessible : L4_Readable; + + if (grant) + _grant_item = L4_GrantItem(fpage, dst_addr); + else + _map_item = L4_MapItem(fpage, dst_addr); +} + + +Mapping::Mapping() { _map_item = L4_MapItem(L4_Nilpage, 0); } + + +/*************** + ** IPC pager ** + ***************/ + +void Ipc_pager::wait_for_fault() +{ + L4_MsgTag_t result; + L4_ThreadId_t sender = L4_nilthread; + bool failed; + + do { + L4_Accept(L4_UntypedWordsAcceptor); + result = L4_Wait(&sender); + failed = L4_IpcFailed(result); + if (failed) + PERR("Page fault IPC error. (continuable)"); + + if (L4_UntypedWords(result) != 2) { + PERR("Malformed page-fault ipc. (sender = 0x%08lx)", + sender.raw); + failed = true; + } + + } while (failed); + + L4_Msg_t msg; + // TODO Error checking. Did we really receive 2 words? + L4_Store(result, &msg); + + _pf_addr = L4_Get(&msg, 0); + _pf_ip = L4_Get(&msg, 1); + _flags = L4_Label(result); + + _last = sender; +} + + +void Ipc_pager::reply_and_wait_for_fault() +{ + /* + * XXX call memory-control if mapping has enabled write-combining + */ + + L4_Msg_t msg; + L4_Accept(L4_UntypedWordsAcceptor); + L4_Clear(&msg); + + /* this should work even if _map_item is a grant item */ + L4_Append(&msg, _map_item); + L4_Load(&msg); + L4_MsgTag_t result = L4_ReplyWait(_last, &_last); + + if (L4_IpcFailed(result)) { + PERR("Page fault IPC error. (continuable)"); + wait_for_fault(); + return; + } + + if (L4_UntypedWords(result) != 2) { + PERR("Malformed page-fault ipc. (sender = 0x%08lx)", _last.raw); + wait_for_fault(); + return; + } + + L4_Clear(&msg); + // TODO Error checking. Did we really receive 2 words? + L4_Store(result, &msg); + + _pf_addr = L4_Get(&msg, 0); + _pf_ip = L4_Get(&msg, 1); + _flags = L4_Label(result); +} + + +void Ipc_pager::acknowledge_wakeup() +{ + PERR("acknowledge_wakeup called, not yet implemented"); +// /* answer wakeup call from one of core's region-manager sessions */ +// l4_msgdope_t result; +// l4_ipc_send(_last, L4_IPC_SHORT_MSG, 0, 0, L4_IPC_SEND_TIMEOUT_0, &result); +} + + +Ipc_pager::Ipc_pager() +: Native_capability(L4_Myself(), 0) +{ } + diff --git a/base-pistachio/src/base/kip/kip.cc b/base-pistachio/src/base/kip/kip.cc new file mode 100644 index 000000000..e6c8dc035 --- /dev/null +++ b/base-pistachio/src/base/kip/kip.cc @@ -0,0 +1,57 @@ +/* + * \brief Access to the kernel info page + * \author Julian Stecklina + * \date 2008-02-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Pistachio; + +#include + + +void *Pistachio::get_kip() +{ + static void *kip = 0; + + if (kip == 0) + kip = L4_KernelInterface(); + + return kip; +} + + +unsigned int Pistachio::get_page_size_log2() +{ + static unsigned int ps = 0; + + if (ps == 0) { + L4_Word_t ps_mask = L4_PageSizeMask(get_kip()); + + while ((ps_mask&1) == 0) { + ps += 1; + ps_mask >>= 1; + } + } + return ps; +} + + +L4_Word_t Pistachio::get_page_mask() +{ + static L4_Word_t page_mask = 0; + + if (page_mask == 0) { + unsigned int ps = get_page_size_log2(); + page_mask = (((L4_Word_t)~0)>>ps)< + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +} + + +bool operator == (Genode::Native_thread_id t1, Genode::Native_thread_id t2) { return t1.raw == t2.raw; } +bool operator != (Genode::Native_thread_id t1, Genode::Native_thread_id t2) { return t1.raw != t2.raw; } + + +/** + * Yield CPU time + */ +static inline void thread_yield() { Pistachio::L4_Yield(); } + + +/** + * Custom ExchangeRegisters wrapper for waking up a thread + * + * When waking up an lock applicant, we need to make sure that the thread was + * stopped beforehand. Therefore, we evaluate the previous thread state as + * returned by the 'L4_ExchangeRegisters' call. + * + * \return true if the thread was in blocking state + */ +static bool thread_check_stopped_and_restart(Genode::Native_thread_id tid) +{ + using namespace Pistachio; + + L4_Word_t dummy; + L4_ThreadId_t dummy_id; + L4_ThreadState_t state; + + enum { RESUME = 1 << 8, CANCEL_IPC = 3 << 1 }; + L4_ExchangeRegisters(tid, RESUME | CANCEL_IPC, 0, 0, 0, + 0, L4_nilthread, &state.raw, &dummy, &dummy, &dummy, + &dummy, &dummy_id); + + return L4_ThreadWasHalted(state); +} + + +static inline Genode::Native_thread_id thread_get_my_native_id() +{ + return Pistachio::L4_Myself(); +} + + +static inline Genode::Native_thread_id thread_invalid_id() +{ + using namespace Pistachio; + return L4_nilthread; +} + + +/** + * Check if a native thread ID is initialized + * + * \return true if ID is initialized + */ +static inline bool thread_id_valid(Genode::Native_thread_id tid) +{ + return (tid.raw != 0); +} + + +/** + * Yield CPU time to the specified thread + */ +static inline void thread_switch_to(Genode::Native_thread_id tid) +{ + Pistachio::L4_ThreadSwitch(tid); +} + + +/** + * Unconditionally block the calling thread + */ +static inline void thread_stop_myself() +{ + Pistachio::L4_Stop(thread_get_my_native_id()); +} diff --git a/base-pistachio/src/base/pager/pager.cc b/base-pistachio/src/base/pager/pager.cc new file mode 100644 index 000000000..0d51c201f --- /dev/null +++ b/base-pistachio/src/base/pager/pager.cc @@ -0,0 +1,118 @@ +/* + * \brief Pistachio pager framework + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-14 + * + * FIXME Isn't this file generic? + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + + +namespace Pistachio { +#include +} + +/********************** + ** Pager activation ** + **********************/ + +void Pager_activation_base::entry() +{ + Ipc_pager pager; + _cap = pager; + _cap_valid.unlock(); + + pager.wait_for_fault(); + while (1) { + + /* lookup referenced object */ + Pager_object *obj = _ep ? _ep->obj_by_id(pager.badge()) : 0; + + /* handle request */ + if (obj) { + if (obj->pager(pager)) + /* something strange occured - leave thread in pagefault */ + pager.wait_for_fault(); + else + pager.reply_and_wait_for_fault(); + } else { + + /* prevent threads outside of core to mess with our wake-up interface */ +// enum { CORE_TASK_ID = 4 }; +// if (pager.last().id.task != CORE_TASK_ID) { + +#warning Check for messages from outside of core + if (0) { + PWRN("page fault to 0x%08lx from unknown partner %lx.", + Pistachio::L4_Myself().raw, + pager.last().raw); + + } else { + + /* + * We got a request from one of cores region-manager sessions + * to answer the pending page fault of a resolved region-manager + * client. Hence, we have to send the page-fault reply to the + * specified thread and answer the call of the region-manager + * session. + * + * When called from a region-manager session, we receive the + * core-local address of the targeted pager object via the + * first message word, which corresponds to the 'fault_ip' + * argument of normal page-fault messages. + */ + obj = reinterpret_cast(pager.fault_ip()); + + /* send reply to the calling region-manager session */ + pager.acknowledge_wakeup(); + + /* answer page fault of resolved pager object */ + pager.set_reply_dst(obj->cap()); + pager.acknowledge_wakeup(); + } + pager.wait_for_fault(); + } + } +} + + +/********************** + ** Pager entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *, Pager_activation_base *a) +: _activation(a) +{ _activation->ep(this); } + + +void Pager_entrypoint::dissolve(Pager_object *obj) +{ + remove(obj); +} + + +Pager_capability Pager_entrypoint::manage(Pager_object *obj) +{ + /* return invalid capability if no activation is present */ + if (!_activation) return Pager_capability(); + + Native_capability cap = Native_capability(_activation->cap().tid(), obj->badge()); + + /* add server object to object pool */ + obj->cap(cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return reinterpret_cap_cast(cap); +} diff --git a/base-pistachio/src/core/cpu_session_platform.cc b/base-pistachio/src/core/cpu_session_platform.cc new file mode 100644 index 000000000..439ae32c2 --- /dev/null +++ b/base-pistachio/src/core/cpu_session_platform.cc @@ -0,0 +1,15 @@ + +#include +#include + +using namespace Genode; +using namespace Pistachio; + +// unsigned int Cpu_session_component::available_cpus() +// { +// if (_pinned_cpu == -1) +// return L4_NumProcessors(get_kip()); +// else +// return 1; +// } + diff --git a/base-pistachio/src/core/include/map_local.h b/base-pistachio/src/core/include/map_local.h new file mode 100644 index 000000000..190017d3a --- /dev/null +++ b/base-pistachio/src/core/include/map_local.h @@ -0,0 +1,82 @@ +/* + * \brief Core-local mapping + * \author Norman Feske + * \date 2010-02-15 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__MAP_LOCAL_H_ +#define _CORE__INCLUDE__MAP_LOCAL_H_ + +/* core includes */ +#include +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +#include +} + +namespace Genode { + + /** + * Map page locally within core + * + * On Pistachio, all mapping originate from virtual addresses. At startup, + * core obtains the whole memory sigma0 in a one-to-one fashion. Hence, + * core-local addresses normally correspond to physical addresses. + * + * \param from_addr core-virtual source address + * \param to_addr core-virtual destination address + * \param num_pages number of pages to remap + */ + inline static bool map_local(addr_t from_addr, addr_t to_addr, size_t num_pages) + { + + Native_thread_id core_pager = platform_specific()->core_pager()->native_thread_id(); + + addr_t offset = 0; + size_t page_size = get_page_size(); + for (unsigned i = 0; i < num_pages; i++, offset += page_size) { + + using namespace Pistachio; + + L4_Fpage_t fpage = L4_Fpage(from_addr + offset, page_size); + fpage += L4_FullyAccessible; + L4_MapItem_t map_item = L4_MapItem(fpage, 0); + + /* assemble local echo mapping request */ + L4_Msg_t msg; + L4_Word_t echo_request = 0, item_addr = (addr_t)&map_item; + L4_Clear(&msg); + L4_Append(&msg, item_addr); + L4_Append(&msg, echo_request); + msg.tag.X.u = 2; + + /* setup receive window */ + L4_Fpage_t rcv_fpage = L4_Fpage(to_addr + offset, page_size); + L4_Accept(L4_MapGrantItems(rcv_fpage)); + + L4_Load(&msg); + + L4_MsgTag_t result = L4_Call(core_pager); + if (L4_IpcFailed(result)) { + PWRN("could not locally remap 0x%lx to 0x%lx, error code is %ld", + from_addr, to_addr, L4_ErrorCode()); + return false; + } + } + return true; + } +} + +#endif /* _CORE__INCLUDE__MAP_LOCAL_H_ */ + diff --git a/base-pistachio/src/core/include/platform.h b/base-pistachio/src/core/include/platform.h new file mode 100644 index 000000000..9476d457b --- /dev/null +++ b/base-pistachio/src/core/include/platform.h @@ -0,0 +1,156 @@ +/* + * \brief Pistachio platform + * \author Christian Helmuth + * \author Norman Feske + * \date 2007-09-10 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_H_ +#define _CORE__INCLUDE__PLATFORM_H_ + +#include +#include + +#include "platform_generic.h" +#include "platform_thread.h" +#include "platform_pd.h" +#include "multiboot.h" + + +namespace Genode { + + class Platform : public Platform_generic + { + private: + + /* + * Shortcut for the type of allocator instances for physical resources + */ + typedef Synchronized_range_allocator Phys_allocator; + + Phys_allocator _ram_alloc; /* RAM allocator */ + Phys_allocator _io_mem_alloc; /* MMIO allocator */ + Phys_allocator _io_port_alloc; /* I/O port allocator */ + Phys_allocator _irq_alloc; /* IRQ allocator */ + Phys_allocator _region_alloc; /* virtual memory allocator for core */ + Multiboot_info _mb_info; /* multiboot information */ + Rom_fs _rom_fs; /* ROM file system */ + Rom_module _kip_rom; /* ROM module for Fiasco KIP */ + + addr_t _vm_start; /* begin of virtual memory */ + size_t _vm_size; /* size of virtual memory */ + + /* + * We do not export any boot module loaded before FIRST_ROM. + */ + enum { FIRST_ROM = 3 }; + + /** + * Setup base resources + * + * - Map and provide KIP as ROM module + * - Initializes region allocator + * - Initializes multiboot info structure + */ + void _setup_basics(); + + /** + * Setup preemption flags + */ + void _setup_preemption(); + + /** + * Setup RAM, IO_MEM, and region allocators + */ + void _setup_mem_alloc(); + + /** + * Setup I/O port space allocator + */ + void _setup_io_port_alloc(); + + /** + * Setup IRQ allocator + */ + void _setup_irq_alloc(); + + /** + * Parse multi-boot information and update ROM database + */ + void _setup_rom(); + + public: + + /** + * Pager object representing the pager of core namely sigma0 + */ + struct Sigma0 : public Pager_object + { + /** + * Constructor + */ + Sigma0(); + + int pager(Ipc_pager &ps) { /* never called */ return -1; } + }; + + /** + * Return singleton instance of Sigma0 pager object + */ + static Sigma0 *sigma0(); + + /** + * Core pager thread that handles core-internal page-faults + */ + struct Core_pager : public Platform_thread, public Pager_object + { + /** + * Constructor + */ + Core_pager(Platform_pd *core_pd); + + int pager(Ipc_pager &ps) { /* never called */ return -1; } + }; + + /** + * Return singleton instance of core pager object + */ + Core_pager *core_pager(); + + /** + * Constructor + */ + Platform(); + + /** + * Return singleton instance of core PD object + */ + Platform_pd *core_pd(); + + + /******************************** + ** Generic platform interface ** + ********************************/ + + Allocator *core_mem_alloc() { return &_ram_alloc; } + Range_allocator *ram_alloc() { return &_ram_alloc; } + Range_allocator *io_mem_alloc() { return &_io_mem_alloc; } + Range_allocator *io_port_alloc() { return &_io_port_alloc; } + Range_allocator *irq_alloc() { return &_irq_alloc; } + Range_allocator *region_alloc() { return &_region_alloc; } + addr_t vm_start() const { return _vm_start; } + size_t vm_size() const { return _vm_size; } + Rom_fs *rom_fs() { return &_rom_fs; } + + void wait_for_exit(); + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ diff --git a/base-pistachio/src/core/include/platform_pd.h b/base-pistachio/src/core/include/platform_pd.h new file mode 100644 index 000000000..4af93ecdb --- /dev/null +++ b/base-pistachio/src/core/include/platform_pd.h @@ -0,0 +1,214 @@ +/* + * \brief Pistachio protection-domain facility + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_PD_H_ +#define _CORE__INCLUDE__PLATFORM_PD_H_ + +#include + +namespace Pistachio { +#include +} + +namespace Genode { + + class Platform_thread; + class Platform_pd + { + private: + + friend class Platform_thread; + + /* + * L4 thread ID has 18 bits for thread number and 14 bits for + * version info. + */ + enum { + PD_BITS = 9, + THREAD_BITS = 9, + VERSION_BITS = 14, + PD_FIRST = 0, + PD_MAX = (1 << PD_BITS) - 1, + THREAD_MAX = (1 << THREAD_BITS) - 1, + VERSION_MAX = (1 << VERSION_BITS) - 1, + PD_INVALID = -1, + }; + + unsigned _pd_id; /* plain pd number */ + unsigned _version; /* version number */ + + Pistachio::L4_ThreadId_t _l4_task_id; /* L4 task ID */ + + /** + * Manually construct L4 thread ID from its components + */ + Pistachio::L4_ThreadId_t make_l4_id(unsigned pd_no, + unsigned thread_no, + unsigned version) + { + return Pistachio::L4_GlobalId((pd_no << PD_BITS) | thread_no, version); + } + + + /********************************************** + ** Threads of this protection domain object ** + **********************************************/ + + Platform_thread *_threads[THREAD_MAX]; + + /** + * Initialize thread allocator + */ + void _init_threads(); + + /** + * Thread iteration for one PD + */ + Platform_thread *_next_thread(); + + /** + * Thread allocation + * + * Again a special case for Core thread0. + */ + int _alloc_thread(int thread_id, Platform_thread *thread); + + /** + * Thread deallocation + * + * No special case for Core thread0 here - we just never call it. + */ + void _free_thread(int thread_id); + + + /****************** + ** PD allocator ** + ******************/ + + struct Pd_alloc + { + unsigned reserved : 1; + unsigned free : 1; + unsigned version : VERSION_BITS; + + Pd_alloc(bool r, bool f, unsigned v) + : reserved(r), free(f), version(v) { } + + /* + * Start with version 2 to avoid being mistaken as local or + * interrupt thread ID. + */ + Pd_alloc() : reserved(0), free(0), version(2) { } + }; + + static Pd_alloc *_pds() + { + static Pd_alloc static_pds[PD_MAX]; + return static_pds; + } + + Pistachio::L4_Word_t _kip_ptr; + Pistachio::L4_Word_t _utcb_ptr; + + /** + * Protection-domain creation + * + * The syscall parameter propagates if any L4 kernel function + * should be used. We need the special case for the Core startup. + */ + void _create_pd(bool syscall); + + /** + * Protection domain destruction + * + * No special case for Core here - we just never call it. + */ + void _destroy_pd(); + + /** + * Protection domain allocation + * + * Find free PD and use it. We need the special case for core + * startup. + */ + int _alloc_pd(signed pd_id); + + /** + * Protection domain deallocation + * + * No special case for Core here - we just never call it. + */ + void _free_pd(); + + /** + * Setup KIP and UTCB area + */ + void _setup_address_space(); + + /** + * Return the location of the UTCB for the specified thread + */ + Pistachio::L4_Word_t _utcb_location(unsigned int thread_id); + + + /*************** + ** Debugging ** + ***************/ + + void _debug_log_pds(void); + void _debug_log_threads(void); + + public: + + /** + * Constructors + */ + Platform_pd(bool core); + Platform_pd(signed pd_id = PD_INVALID, bool create = true); + + /** + * Destructor + */ + ~Platform_pd(); + + static Pistachio::L4_Word_t _core_utcb_ptr; + static void touch_utcb_space(); + + /** + * Bind thread to protection domain + * + * \return 0 on success or + * -1 if thread ID allocation failed. + * + * This function allocates the physical L4 thread ID. + */ + int bind_thread(Platform_thread *thread); + int bind_initial_thread(Platform_thread *thread); + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread *thread); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent) { return 0; } + + int pd_id() const { return _pd_id; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ diff --git a/base-pistachio/src/core/include/platform_thread.h b/base-pistachio/src/core/include/platform_thread.h new file mode 100644 index 000000000..9c8c9fc97 --- /dev/null +++ b/base-pistachio/src/core/include/platform_thread.h @@ -0,0 +1,151 @@ +/* + * \brief Pistachio thread facility + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_THREAD_H_ +#define _CORE__INCLUDE__PLATFORM_THREAD_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +} + +namespace Genode { + + class Platform_pd; + class Platform_thread + { + private: + + int _thread_id; /* plain thread number */ + Native_thread_id _l4_thread_id; /* L4 thread ID */ + char _name[32]; /* thread name that will be + registered at the kernel + debugger */ + Platform_pd *_platform_pd; /* protection domain thread + is bound to */ + unsigned _priority; /* thread priority */ + Pager_object *_pager; + + public: + + enum { THREAD_INVALID = -1 }; /* invalid thread number */ + enum { DEFAULT_PRIORITY = 128 }; + + /** + * Constructor + */ + Platform_thread(const char *name = 0, unsigned priority = 0, + int thread_id = THREAD_INVALID); + + /** + * Destructor + */ + ~Platform_thread(); + + /** + * Start thread + * + * \param ip instruction pointer to start at + * \param sp stack pointer to use + * \param cpu_no target cpu + * + * \retval 0 successful + * \retval -1 thread could not be started + */ + int start(void *ip, void *sp, unsigned int cpu_no = 0); + + /** + * Pause this thread + */ + void pause(); + + /** + * Resume this thread + */ + void resume(); + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * This thread is about to be bound + * + * \param thread_id local thread ID + * \param l4_thread_id final L4 thread ID + * \param pd platform pd, thread is bound to + */ + void bind(int thread_id, Native_thread_id l4_thread_id, + Platform_pd *pd); + + /** + * Unbind this thread + */ + void unbind(); + + /** + * Request thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state *state_dst); + + + /************************ + ** Accessor functions ** + ************************/ + + /** + * Return/set pager + */ + Pager_object *pager() const { return _pager; } + void pager(Pager_object *pager) { _pager = pager; } + + /** + * Return identification of thread when faulting + */ + unsigned long pager_object_badge() const { + return convert_native_thread_id_to_badge(_l4_thread_id); } + + /** + * Set the executing CPU for this thread. + */ + void set_cpu(unsigned int cpu_no); + + + /********************************** + ** Pistachio-specific Accessors ** + **********************************/ + + int thread_id() const { return _thread_id; } + Native_thread_id native_thread_id() const { return _l4_thread_id; } + const char *name() const { return _name; } + + /* use only for core... */ + void set_l4_thread_id(Native_thread_id id) { _l4_thread_id = id; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ diff --git a/base-pistachio/src/core/include/util.h b/base-pistachio/src/core/include/util.h new file mode 100644 index 000000000..0e9c44236 --- /dev/null +++ b/base-pistachio/src/core/include/util.h @@ -0,0 +1,126 @@ +/* + * \brief Pistachio utilities + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__UTIL_H_ +#define _CORE__INCLUDE__UTIL_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +#include +} + +namespace Genode { + + inline void log_event(const char *s) { } + inline void log_event(const char *s, unsigned v1, unsigned v2, unsigned v3) { } + + inline void panic(const char *s) + { + using namespace Pistachio; + PDBG("Panic: %s", s); + L4_KDB_Enter("> panic <"); + } + + inline void assert(const char *s, bool val) + { + using namespace Pistachio; + if (!val) { + PERR("Assertion failed: %s", s); + L4_KDB_Enter("Assertion failed."); + } + } + + inline void touch_ro(const void *addr, unsigned size) + { + using namespace Pistachio; + unsigned char const volatile *bptr; + unsigned char const *eptr; + L4_Word_t mask = get_page_mask(); + L4_Word_t psize = get_page_size(); + + bptr = (unsigned char const volatile *)(((unsigned)addr) & mask); + eptr = (unsigned char const *)(((unsigned)addr + size - 1) & mask); + for ( ; bptr <= eptr; bptr += psize) + touch_read(bptr); + } + + inline void touch_rw(const void *addr, unsigned size) + { + using namespace Pistachio; + unsigned char volatile *bptr; + unsigned char const *eptr; + L4_Word_t mask = get_page_mask(); + L4_Word_t psize = get_page_size(); + + bptr = (unsigned char volatile *)(((unsigned)addr) & mask); + eptr = (unsigned char const *)(((unsigned)addr + size - 1) & mask); + for(; bptr <= eptr; bptr += psize) + touch_read_write(bptr); + } + + inline size_t get_page_size() { return Pistachio::get_page_size(); } + inline size_t get_page_size_log2() { return Pistachio::get_page_size_log2(); } + inline addr_t get_page_mask() { return Pistachio::get_page_mask(); } + + inline size_t get_super_page_size_log2() + { + enum { SUPER_PAGE_SIZE_LOG2 = 22 }; + if (get_page_mask() & (1 << SUPER_PAGE_SIZE_LOG2)) + return SUPER_PAGE_SIZE_LOG2; + + /* if super pages are not supported, return default page size */ + return get_page_size(); + } + + inline size_t get_super_page_size() { return 1 << get_super_page_size_log2(); } + + inline addr_t trunc_page(addr_t addr) + { + return addr & get_page_mask(); + } + + inline addr_t round_page(addr_t addr) + { + return trunc_page(addr + get_page_size() - 1); + } + + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, + Rm_session::Fault_type pf_type, + unsigned long badge) + { + Native_thread_id tid; + tid.raw = badge; + printf("%s (%s pf_addr=%p pf_ip=%p from %02lx (raw %08lx))\n", msg, + pf_type == Rm_session::WRITE_FAULT ? "WRITE" : "READ", + (void *)pf_addr, (void *)pf_ip, + Pistachio::L4_GlobalId(tid).global.X.thread_no, tid.raw); + } + + inline addr_t map_src_addr(addr_t core_local_addr, addr_t phys_addr) { + return core_local_addr; } + + inline size_t constrain_map_size_log2(size_t size_log2) { + return size_log2; } +} + +#endif /* _CORE__INCLUDE__UTIL_H_ */ diff --git a/base-pistachio/src/core/io_mem_session_support.cc b/base-pistachio/src/core/io_mem_session_support.cc new file mode 100644 index 000000000..d5bdde2ab --- /dev/null +++ b/base-pistachio/src/core/io_mem_session_support.cc @@ -0,0 +1,123 @@ +/* + * \brief Pistachio-specific implementation of the IO_MEM session interface + * \author Julian Stecklina + * \date 2008-04-09 + * + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include +#include +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +#include +} + +using namespace Genode; + +static const bool verbose = false; + + +/* + * TODO This should take a size parameter and check if the whole + * region is "normal" memory. + */ +bool is_conventional_memory(addr_t base) +{ + using namespace Pistachio; + void *kip = get_kip(); + + /* I miss useful programming languages... */ + for (L4_Word_t i = 0; i < L4_NumMemoryDescriptors(kip); i++) { + L4_MemoryDesc_t *d = L4_MemoryDesc(kip, i); + + if (!L4_IsVirtual(d) && (L4_Type(d) == 1)) + if ((L4_Low(d) <= base) && (base <= L4_High(d))) + return true; + } + + return false; +} + + +void Io_mem_session_component::_unmap_local(addr_t base, size_t size) +{ + /* TODO .... */ + if (verbose) + PDBG("not yet implemented!"); +} + + +static inline bool can_use_super_page(addr_t base, size_t size) { + return (base & (get_super_page_size() - 1)) == 0 + && (size >= get_super_page_size()); } + + +addr_t Io_mem_session_component::_map_local(addr_t base, size_t size) +{ + using namespace Pistachio; + + addr_t local_base; + + /* align large I/O dataspaces on a super-page boundary within core */ + size_t alignment = (size >= get_super_page_size()) ? get_super_page_size_log2() + : get_page_size_log2(); + + /* special case for the null page */ + if (is_conventional_memory(base)) + local_base = base; + + else { + + /* find appropriate region for mapping */ + void *result = 0; + platform()->region_alloc()->alloc_aligned(size, &result, alignment); + local_base = (addr_t)result; + + if (!local_base) + PERR("alloc_aligned failed!"); + } + + if (verbose) + PDBG("base = 0x%08lx, size = 0x%08zx -> local = 0x%lx", base, size, local_base); + + unsigned offset = 0; + while (size) { + + size_t page_size = get_page_size(); + if (can_use_super_page(base + offset, size)) + page_size = get_super_page_size(); + + L4_Fpage_t ret = + L4_Sigma0_GetPage_RcvWindow(get_sigma0(), + L4_Fpage(base + offset, page_size), + L4_Fpage(local_base + offset, page_size)); + + if (_write_combined) { + int res = L4_Set_PageAttribute(L4_Fpage(local_base + offset, page_size), + L4_WriteCombiningMemory); + if (res != 1) + PERR("L4_Set_PageAttributes virt returned %d", res); + } + + if (L4_IsNilFpage(ret) && verbose) + PDBG("Got nil fpage for 0x%08lx from sigma0 (ignoring)", base + offset); + + offset += page_size; + size -= page_size; + } + + return local_base; +} diff --git a/base-pistachio/src/core/irq_session_component.cc b/base-pistachio/src/core/irq_session_component.cc new file mode 100644 index 000000000..f44e7ae27 --- /dev/null +++ b/base-pistachio/src/core/irq_session_component.cc @@ -0,0 +1,134 @@ +/* + * \brief Pistachio-specific implementation of IRQ sessions + * \author Julian Stecklina + * \date 2008-02-21 + * + * FIXME ram quota missing + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +} + +using namespace Genode; +using namespace Pistachio; + + +static inline L4_ThreadId_t irqno_to_threadid(unsigned int irqno) +{ + /* + * Interrupt threads have their number as thread_no and a version of 1. + */ + return L4_GlobalId(irqno, 1); +} + + +bool Irq_session_component::Irq_control_component::associate_to_irq(unsigned) +{ + /* + * We defer the association with the IRQ to the first call of the + * 'wait_for_irq' function. + */ + return true; +} + + +void Irq_session_component::wait_for_irq() +{ + L4_ThreadId_t irq_thread = irqno_to_threadid(_irq_number); + + /* attach to IRQ when called for the first time */ + L4_MsgTag_t res; + if (!_irq_attached) { + + if (L4_AssociateInterrupt(irq_thread, L4_Myself()) != true) { + PERR("L4_AssociateInterrupt failed"); + return; + } + + /* + * Right after associating with an interrupt, the interrupt is + * unmasked. Hence, we do not need to send an unmask message + * to the IRQ thread but just wait for the IRQ. + */ + L4_Set_MsgTag(L4_Niltag); + res = L4_Receive(irq_thread); + + /* + * Now, the IRQ is masked. To receive the next IRQ we have to send + * an unmask message to the IRQ thread first. + */ + _irq_attached = true; + + /* receive subsequent interrupt */ + } else { + + /* send unmask message and wait for new IRQ */ + L4_Set_MsgTag(L4_Niltag); + res = L4_Call(irq_thread); + } + + if (L4_IpcFailed(res)) { + PERR("ipc error while waiting for interrupt."); + return; + } +} + + +Irq_session_component::Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args) +: + _irq_alloc(irq_alloc), + _ep(cap_session, STACK_SIZE, "irqctrl"), + _irq_attached(false), + _control_client(Capability()) +{ + bool shared = Arg_string::find_arg(args, "irq_shared").bool_value(false); + if (shared) { + PWRN("IRQ sharing not supported"); + + /* FIXME error condition -> exception */ + return; + } + + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + if (irq_number == -1 || !irq_alloc || + irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK) { + PERR("unavailable IRQ %lx requested", irq_number); + + /* FIXME error condition -> exception */ + return; + } + _irq_number = irq_number; + + /* initialize capability */ + _irq_cap = _ep.manage(this); +} + + +Irq_session_component::~Irq_session_component() +{ + L4_Word_t res = L4_DeassociateInterrupt(irqno_to_threadid(_irq_number)); + + if (res != 1) { + PERR("L4_DeassociateInterrupt failed"); + } +} + diff --git a/base-pistachio/src/core/multiboot_info.cc b/base-pistachio/src/core/multiboot_info.cc new file mode 100644 index 000000000..a0c24d611 --- /dev/null +++ b/base-pistachio/src/core/multiboot_info.cc @@ -0,0 +1,162 @@ +/* + * \brief GRUB multi-boot information handling + * \author Christian Helmuth + * \date 2006-05-10 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* core includes */ +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +} + +using namespace Genode; + + +static const bool verbose = false; + + +#define VPRINTF(fmt...) if (verbose) printf(fmt); else {} + + +void Multiboot_info::print_debug() +{ + printf("TODO Multiboot_info does not support print_debug."); +} + + +unsigned Multiboot_info::num_modules() +{ + using namespace Pistachio; + + unsigned int i = 0; + L4_Word_t entries; + L4_BootRec_t *rec; + for (entries = L4_BootInfo_Entries(_mb_info), + rec = L4_BootInfo_FirstEntry(_mb_info); + entries > 0; + entries--, rec = L4_Next(rec)) + { + if (L4_Type(rec) == L4_BootInfo_Module) + i++; + } + + /* return count of modules */ + return i; +} + + +Rom_module Multiboot_info::get_module(unsigned num) +{ + using namespace Pistachio; + + /* find the right record */ + bool found = false; + unsigned int i = 0; + L4_Word_t entries; + L4_BootRec_t *rec; + for (entries = L4_BootInfo_Entries(_mb_info), + rec = L4_BootInfo_FirstEntry(_mb_info); + entries > 0; + entries--, rec = L4_Next(rec)) + { + if ((L4_Type(rec) == L4_BootInfo_Module) && + (i++ == num)) { + found = true; + break; + } + } + + if (!found) + panic("No such rom module"); + + /* strip path info and command line */ + char *name = L4_Module_Cmdline(rec); + for (char *c = name; *c != 0; c++) { + if (*c == '/') name = c + 1; + if (*c == ' ') { + *c = 0; + break; + } + } + + /* request the memory from sigma0 and create the rom_module object */ + L4_Word_t start = L4_Module_Start(rec); + L4_Word_t size = L4_Module_Size(rec); + + if (start != trunc_page(start)) + panic("Module is not aligned to page boundary."); + + L4_ThreadId_t s0 = get_sigma0(); + addr_t ps = get_page_size(); + for (addr_t cur = start; cur < start + size; cur += ps) { + L4_Fpage_t fp = L4_Sigma0_GetPage(s0, L4_Fpage(cur, ps)); + + if (L4_IsNilFpage(fp) || + L4_Address(fp) != cur) + panic("Unable to map module data."); + } + + Rom_module ret = Rom_module(start, size, name); + return ret; +} + + +bool Multiboot_info::check_module(unsigned num, addr_t *start, addr_t *end) +{ + panic("TODO Who calls check_module?"); + return false; +} + + +Multiboot_info::Multiboot_info(void *mb_info) +: _mb_info(mb_info) +{ + using namespace Pistachio; + + if (!L4_BootInfo_Valid(mb_info)) + panic("Invalid BootInfo."); + + /* some debug info, can probably be removed */ + unsigned int i; + L4_Word_t entries; + L4_BootRec_t *rec; + for (entries = L4_BootInfo_Entries(mb_info), + rec = L4_BootInfo_FirstEntry(mb_info), + i = 0; + entries > 0; + entries--, i++, rec = L4_Next(rec)) { + + VPRINTF("Entry[%d]\n", i); + switch (L4_Type(rec)) { + case L4_BootInfo_Module: + VPRINTF(" Type: Module\n"); + VPRINTF(" Cmd : %s\n", L4_Module_Cmdline(rec)); + break; + case L4_BootInfo_SimpleExec: + VPRINTF(" Type: SimpleExec (ignored)\n"); + VPRINTF(" Cmd : %s\n", L4_SimpleExec_Cmdline(rec)); + break; + case L4_BootInfo_EFITables: + VPRINTF(" Type: EFITables (ignored)\n"); break; + case L4_BootInfo_Multiboot: + VPRINTF(" Type: Multiboot (ignored)\n"); break; + } + } +} diff --git a/base-pistachio/src/core/platform.cc b/base-pistachio/src/core/platform.cc new file mode 100644 index 000000000..039cdf07d --- /dev/null +++ b/base-pistachio/src/core/platform.cc @@ -0,0 +1,648 @@ +/* + * \brief Pistachio platform interface implementation + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include +#include +#include +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +#include +#include +#include +} + +using namespace Genode; + + +static const bool verbose = false; +static const bool verbose_core_pf = false; +static const bool verbose_region_alloc = false; + + +/*********************************** + ** Core address space management ** + ***********************************/ + +static Synchronized_range_allocator &_core_address_ranges() +{ + static Synchronized_range_allocator _core_address_ranges(0); + return _core_address_ranges; +} + +enum { PAGER_STACK_ELEMENTS = 512 }; +static unsigned long _core_pager_stack[PAGER_STACK_ELEMENTS]; + + +static inline bool is_write_fault(Pistachio::L4_Word_t flags) { + return (flags & 2) == 1; } + + +static bool wait_for_page_fault(Pistachio::L4_ThreadId_t &from, + Pistachio::L4_Word_t &pf_addr, + Pistachio::L4_Word_t &pf_ip, + Pistachio::L4_Word_t &flags) +{ + using namespace Pistachio; + + L4_Accept(L4_UntypedWordsAcceptor); + L4_MsgTag_t res = L4_Wait(&from); + L4_Msg_t msg; + + enum { EXPECT = 2 }; + if (L4_IpcFailed(res) || (L4_UntypedWords(res)) != EXPECT) { + PERR("got %ld words, expected %d", L4_UntypedWords(res), EXPECT); + return false; + } + L4_Store(res, &msg); + + pf_addr = L4_Get(&msg, 0); + pf_ip = L4_Get(&msg, 1); + flags = res.X.flags; + return true; +} + + +static bool reply_and_wait_for_page_fault(Pistachio::L4_ThreadId_t to, + Pistachio::L4_MapItem_t item, + Pistachio::L4_ThreadId_t &from, + Pistachio::L4_Word_t &pf_addr, + Pistachio::L4_Word_t &pf_ip, + Pistachio::L4_Word_t &flags) +{ + using namespace Pistachio; + + L4_Msg_t msg; + L4_Clear(&msg); + L4_Append(&msg, item); + L4_Accept(L4_UntypedWordsAcceptor); + L4_MsgLoad(&msg); + + L4_MsgTag_t res = L4_ReplyWait(to, &from); + + enum { EXPECT = 2 }; + if (L4_IpcFailed(res) || (L4_UntypedWords(res)) != EXPECT) { + PERR("got %ld words, expected %d", L4_UntypedWords(res), EXPECT); + return wait_for_page_fault(from, pf_addr, pf_ip, flags); + } + L4_Store(res, &msg); + + pf_addr = L4_Get(&msg, 0); + pf_ip = L4_Get(&msg, 1); + flags = res.X.flags; + return true; +} + + +/**************** + ** Core pager ** + ****************/ + +static void _core_pager_loop() +{ + if (verbose) PDBG("Core pager running."); + + using namespace Pistachio; + + L4_ThreadId_t t; + L4_Word_t pf_addr, pf_ip; + L4_Word_t page_size = Genode::get_page_size(); + L4_Word_t flags; + L4_MapItem_t item; + + bool send_reply = false; + while (1) { + + if (send_reply) + reply_and_wait_for_page_fault(t, item, t, pf_addr, pf_ip, flags); + else + wait_for_page_fault(t, pf_addr, pf_ip, flags); + +#warning "TODO Ignore fault messages from non-core tasks" + + /* + * Check for local echo mapping request. To request a local + * mappings, a core thread may send an IPC to the core pager with + * the message word 1 (which normally carries the pf_ip) set to 0. + * The message word 0 contains a pointer to a map item to be used + * for the echo reply. + */ + if (pf_ip == 0) { + item = *(L4_MapItem_t *)pf_addr; + send_reply = true; + continue; + } + + PDBG("Got page fault (pf_addr = %08lx, pf_ip = %08lx, flags = %08lx).", + pf_addr, pf_ip, flags); + print_l4_threadid(L4_GlobalId(t)); + + /* check for NULL pointer */ + if (pf_addr < page_size) { + PERR("possible null pointer %s at address %lx at EIP %lx in", + is_write_fault(flags) ? "WRITE" : "READ/EXEC", pf_addr, pf_ip); + print_l4_threadid(t); + /* do not unblock faulter */ + break; + } else if (!_core_address_ranges().valid_addr(pf_addr)) { + /* page-fault address is not in RAM */ + + PERR("%s access outside of RAM at %lx IP %lx", + is_write_fault(flags) ? "WRITE" : "READ", pf_addr, pf_ip); + print_l4_threadid(t); + /* do not unblock faulter */ + break; + } else if (verbose_core_pf) { + PDBG("pfa=%lx ip=%lx in", pf_addr, pf_ip); + print_l4_threadid(t); + } + + /* my pf handler is sigma0 - just touch the appropriate page */ + L4_Fpage_t res = L4_Sigma0_GetPage(get_sigma0(), + L4_Fpage(trunc_page(pf_addr), page_size)); + if (L4_IsNilFpage(res)) { + panic("Unhandled page fault"); + } + + /* answer pagefault */ + L4_Fpage_t fpage = L4_Fpage(pf_addr, page_size); + fpage += L4_FullyAccessible; + item = L4_MapItem(fpage, pf_addr); + send_reply = true; + } +} + + +Platform::Sigma0::Sigma0() : Pager_object(0) +{ + cap(Native_capability(Pistachio::get_sigma0(), 0)); +} + + +Platform::Sigma0 *Platform::sigma0() +{ + static Sigma0 _sigma0; + return &_sigma0; +} + + +Platform::Core_pager::Core_pager(Platform_pd *core_pd) +: + Platform_thread("core.pager"), Pager_object(0) +{ + Platform_thread::pager(sigma0()); + + core_pd->bind_thread(this); + cap(Native_capability(native_thread_id(), 0)); + + /* stack begins at the top end of the '_core_pager_stack' array */ + void *sp = (void *)&_core_pager_stack[PAGER_STACK_ELEMENTS - 1]; + start((void *)_core_pager_loop, sp); + + /* pager0 receives pagefaults from me - for NULL pointer detection */ + L4_Set_Pager(native_thread_id()); +} + + +Platform::Core_pager *Platform::core_pager() +{ + static Core_pager _core_pager(core_pd()); + return &_core_pager; +} + + +/*********************************** + ** Helper for L4 region handling ** + ***********************************/ + +struct Region +{ + addr_t start; + addr_t end; + + Region() : start(0), end(0) { } + Region(addr_t s, addr_t e) : start(s), end(e) { } +}; + + +/** + * Log region + */ +static inline void print_region(Region r) +{ + printf("[%08lx,%08lx) %08lx", r.start, r.end, r.end - r.start); +} + + +/** + * Add region to allocator + */ +static inline void add_region(Region r, Range_allocator &alloc) +{ + if (r.start >= r.end) { + PERR("(start = 0x%08lx, end = 0x%08lx)\n", r.start, r.end); + panic("add_region called with bogus parameters."); + } + + if (verbose_region_alloc) { + printf("%p add: ", &alloc); print_region(r); printf("\n"); + } + + /* adjust region */ + addr_t start = trunc_page(r.start); + addr_t end = round_page(r.end); + + alloc.add_range(start, end - start); +} + + +/** + * Remove region from allocator + */ +static inline void remove_region(Region r, Range_allocator &alloc) +{ + if (r.start >= r.end) + panic("remove_region called with bogus parameters."); + + if (verbose_region_alloc) { + printf("%p remove: ", &alloc); print_region(r); printf("\n"); + } + + /* adjust region */ + addr_t start = trunc_page(r.start); + addr_t end = round_page(r.end); + + alloc.remove_range(start, end - start); +} + + +static void dump_kip_memdesc(Pistachio::L4_KernelInterfacePage_t *kip) +{ + using namespace Pistachio; + + L4_Word_t num_desc = L4_NumMemoryDescriptors(kip); + static const char *types[16] = + { + "Undefined", "Conventional", "Reserved by kernel", + "Dedicated", "Shared", "?", "?", "?", "?", "?", + "?", "?", "?", "?", "Boot loader", + "Architecture-dependent" + }; + + for (L4_Word_t i = 0; i < num_desc; i++) { + L4_MemoryDesc_t *d = L4_MemoryDesc(kip, i); + + printf("mem %ld: [0x%08lx, 0x%08lx) type=0x%lx (%s) %s\n", + i, + L4_Low(d), + L4_High(d)+1, + L4_Type(d), types[L4_Type(d) & 0xF], + L4_IsVirtual(d) ? "Virtual" : "Non-Virtual"); + } +} + + +/** + * Request any RAM page from Sigma0 + */ +bool sigma0_req_region(addr_t *addr, unsigned log2size) +{ + using namespace Pistachio; + + L4_Fpage_t fpage = L4_Sigma0_GetAny(get_sigma0(), log2size, + L4_CompleteAddressSpace); + + if (L4_IsNilFpage(fpage)) + return false; + + *addr = L4_Address(fpage); + return true; +} + + +void Platform::_setup_mem_alloc() +{ + /* + * Completely map program image by touching all pages read-only to + * prevent sigma0 from handing out those page as anonymous memory. + */ + volatile const char *beg, *end; + beg = (const char *)(((unsigned)&_prog_img_beg) & get_page_mask()); + end = (const char *)&_prog_img_end; + for ( ; beg < end; beg += get_page_size()) (void)(*beg); + + Pistachio::L4_Word_t page_size_mask = Pistachio::L4_PageSizeMask(Pistachio::get_kip()); + unsigned int size_log2; + + /* + * Allocate all memory from sigma0 in descending page sizes. Only + * try page sizes that are hardware supported. + */ + for ( size_log2 = 31; page_size_mask != 0; size_log2-- ) { + + unsigned int size = 1 << size_log2; + + /* if this page size is not supported try next */ + if ((page_size_mask & size) == 0) + continue; + + /* mask out that size bit */ + page_size_mask &= ~size; + + printf("Trying to allocate %uK pages from sigma0.\n", size >> 10); + + /* + * Suck out sigma0. The spec says that we get only "conventional + * memory". Let's hope this is true. + */ + bool succ; + unsigned int bytes_got = 0; + do { + addr_t addr; + Region region; + + succ = sigma0_req_region(&addr, size_log2); + if (succ) { + /* XXX do not allocate page0 */ + if (addr == 0) { +// L4_Fpage_t f = L4_FpageLog2(0, pslog2); +// f += L4_FullyAccessible; +// L4_Flush(f); + + } else { + region.start = addr; region.end = addr + size; + add_region(region, _ram_alloc); + add_region(region, _core_address_ranges()); + remove_region(region, _io_mem_alloc); + remove_region(region, _region_alloc); + } + + bytes_got += size; + } + } while (succ); + + printf("Got %uK in %uK pieces.\n", bytes_got >> 10, size >> 10); + } +} + + +void Platform::_setup_irq_alloc() { _irq_alloc.add_range(0, 0x10); } + + +void Platform::_setup_preemption() +{ + /* + * The roottask has the maximum priority + */ + L4_Set_Priority(Pistachio::L4_Myself(), + Platform_thread::DEFAULT_PRIORITY); +} + + +void Platform::_setup_basics() +{ + using namespace Pistachio; + + /* completely map program image */ + addr_t beg = trunc_page((addr_t)&_prog_img_beg); + addr_t end = round_page((addr_t)&_prog_img_end); + for ( ; beg < end; beg += get_page_size()) + L4_Sigma0_GetPage(get_sigma0(), L4_Fpage(beg, get_page_size())); + + /* store mapping base from received mapping */ + L4_KernelInterfacePage_t *kip = (L4_KernelInterfacePage_t *)get_kip(); + + if (kip->magic != L4_MAGIC) + panic("we got something but not the KIP"); + + if (verbose) { + printf("\n"); + printf("KIP @ %p\n", kip); + printf(" magic: %08lx\n", kip->magic); + printf(" version: %08lx\n", kip->ApiVersion.raw); + printf(" BootInfo: %08lx\n", kip->BootInfo); + } + + dump_kip_memdesc(kip); + + /* add KIP as ROM module */ + _kip_rom = Rom_module((addr_t)kip, sizeof(L4_KernelInterfacePage_t), "pistachio_kip"); + _rom_fs.insert(&_kip_rom); + + /* update multi-boot info pointer from KIP */ + void *mb_info_ptr = (void *)kip->BootInfo; + + // Get virtual bootinfo address. + + L4_Fpage_t bipage = L4_Sigma0_GetPage(get_sigma0(), + L4_Fpage(kip->BootInfo, + get_page_size())); + if (L4_IsNilFpage(bipage)) + panic("Could not map BootInfo."); + + if (!L4_BootInfo_Valid(mb_info_ptr)) + panic("No valid boot info."); + + if (L4_BootInfo_Size(mb_info_ptr) > get_page_size()) + panic("TODO Our multiboot info is bigger than a page..."); + + /* done magic */ + + _mb_info = Multiboot_info(mb_info_ptr); + if (verbose) printf("MBI @ %p\n", mb_info_ptr); + + /* get UTCB memory */ + Platform_pd::touch_utcb_space(); + + /* I/O memory could be the whole user address space */ + _io_mem_alloc.add_range(0, ~0); + + unsigned int kip_size = sizeof(L4_KernelInterfacePage_t); + + _vm_start = 0x0; + _vm_size = 0x0; + + /* + * Determine the valid virtual address range by iterating through the + * memory descriptors provided by the KIP. We expect only one + * virtual-memory descriptor. + */ + for (unsigned i = 0; i < L4_NumMemoryDescriptors(kip); i++) { + L4_MemoryDesc_t *md = L4_MemoryDesc(kip, i); + if (!L4_IsVirtual(md)) continue; + + if (_vm_start != 0x0 || _vm_size != 0x0) { + PWRN("KIP has multiple virtual-memory descriptors. Taking only the first."); + break; + } + + /* + * Exclude the zero page so that we are able to see null-pointer + * dereference bugs. + */ + _vm_start = max((L4_Word_t)0x1000, L4_MemoryDescLow(md)); + _vm_size = L4_MemoryDescHigh(md) - _vm_start + 1; + + printf("KIP reports virtual memory region at [%lx,%lx)\n", + L4_MemoryDescLow(md), L4_MemoryDescHigh(md)); + } + + /* configure core's virtual memory, exclude KIP, context area */ + _region_alloc.add_range(_vm_start, _vm_size); + _region_alloc.remove_range((addr_t)kip, kip_size); + _region_alloc.remove_range(Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + Thread_base::CONTEXT_AREA_VIRTUAL_SIZE); + + /* remove KIP and MBI area from region and IO_MEM allocator */ + remove_region(Region((addr_t)kip, (addr_t)kip + kip_size), _region_alloc); + remove_region(Region((addr_t)kip, (addr_t)kip + kip_size), _io_mem_alloc); + remove_region(Region((addr_t)mb_info_ptr, (addr_t)mb_info_ptr + _mb_info.size()), _region_alloc); + remove_region(Region((addr_t)mb_info_ptr, (addr_t)mb_info_ptr + _mb_info.size()), _io_mem_alloc); + + /* remove utcb area */ + addr_t utcb_ptr = (addr_t)Platform_pd::_core_utcb_ptr; + + remove_region(Region(utcb_ptr, utcb_ptr + L4_UtcbAreaSize (kip)), _region_alloc); + remove_region(Region(utcb_ptr, utcb_ptr + L4_UtcbAreaSize (kip)), _io_mem_alloc); + + /* remove core program image memory from region allocator */ + addr_t img_start = (addr_t) &_prog_img_beg; + addr_t img_end = (addr_t) &_prog_img_end; + remove_region(Region(img_start, img_end), _region_alloc); + remove_region(Region(img_start, img_end), _io_mem_alloc); + + /* image is accessible by core */ + add_region(Region(img_start, img_end), _core_address_ranges()); +} + + +void Platform::_setup_rom() +{ + Rom_module rom; + + Pistachio::L4_Word_t page_size = Pistachio::get_page_size(); + + for (unsigned i = 0; i < _mb_info.num_modules(); i++) { + if (!(rom = _mb_info.get_module(i)).valid()) continue; + + Rom_module *new_rom = new(core_mem_alloc()) Rom_module(rom); + + _rom_fs.insert(new_rom); + + if (verbose) + printf(" mod[%d] [%p,%p) %s\n", i, + (void *)new_rom->addr(), ((char *)new_rom->addr()) + new_rom->size(), + new_rom->name()); + + /* zero remainder of last ROM page */ + size_t count = page_size - rom.size() % page_size; + if (count != page_size) + memset(reinterpret_cast(rom.addr() + rom.size()), 0, count); + + /* remove ROM area from region and IO_MEM allocator */ + remove_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _region_alloc); + remove_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _io_mem_alloc); + + /* add area to core-accessible ranges */ + add_region(Region(new_rom->addr(), new_rom->addr() + new_rom->size()), _core_address_ranges()); + } +} + + +Platform_pd *Platform::core_pd() +{ + /* on first call, setup task object for core task */ + static Platform_pd _core_pd(true); + return &_core_pd; +} + + +Platform::Platform() : + _ram_alloc(0), _io_mem_alloc(core_mem_alloc()), + _io_port_alloc(core_mem_alloc()), _irq_alloc(core_mem_alloc()), + _region_alloc(core_mem_alloc()) +{ + /* + * We must be single-threaded at this stage and so this is safe. + */ + static bool initialized = 0; + if (initialized) panic("Platform constructed twice!"); + initialized = true; + + _setup_basics(); + _setup_preemption(); + _setup_mem_alloc(); + _setup_io_port_alloc(); + _setup_irq_alloc(); + _setup_rom(); + + /* + * When dumping 'ram_alloc', there are several small blocks in addition + * to the available free memory visible. These small blocks are used to + * hold the meta data for the ROM modules as initialized by '_setup_rom'. + */ + if (verbose) { + printf(":ram_alloc: "); _ram_alloc.raw()->dump_addr_tree(); + printf(":region_alloc: "); _region_alloc.raw()->dump_addr_tree(); + printf(":io_mem: "); _io_mem_alloc.raw()->dump_addr_tree(); + printf(":io_port: "); _io_port_alloc.raw()->dump_addr_tree(); + printf(":irq: "); _irq_alloc.raw()->dump_addr_tree(); + printf(":rom_fs: "); _rom_fs.print_fs(); + printf(":core ranges: "); _core_address_ranges().raw()->dump_addr_tree(); + } + + /* + * We setup the thread object for thread0 in core task using a + * special interface that allows us to specify the thread + * ID. For core, this creates the situation that task_id == + * thread_id of first task. But since we do not destroy this + * task, it should be no problem. + */ + static Platform_thread core_thread("core.main"); + + core_thread.set_l4_thread_id(Pistachio::L4_MyGlobalId()); + core_thread.pager(sigma0()); + + core_pd()->bind_thread(&core_thread); +} + + +/******************************** + ** Generic platform interface ** + ********************************/ + +void Platform::wait_for_exit() +{ + /* + * On Pistachio, core never exits. So let us sleep forever. + */ + sleep_forever(); +} + + +void Core_parent::exit(int exit_value) { } diff --git a/base-pistachio/src/core/platform_pd.cc b/base-pistachio/src/core/platform_pd.cc new file mode 100644 index 000000000..eda4f4640 --- /dev/null +++ b/base-pistachio/src/core/platform_pd.cc @@ -0,0 +1,387 @@ +/* + * \brief Pistachio protection domain facility + * \author Christian Helmuth + * \author Julian Stecklina + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +#include +} + +using namespace Pistachio; +using namespace Genode; + + +static const bool verbose = false; + +#define PT_DBG(args...) if (verbose) PDBG(args); else {} + + +/************************** + ** Static class members ** + **************************/ + +L4_Word_t Platform_pd::_core_utcb_ptr = 0; + + +/**************************** + ** Private object members ** + ****************************/ + +void Platform_pd::_create_pd(bool syscall) +{ + if (syscall) { + PT_DBG("_create_pd (_l4_task_id = 0x%08lx)", + _l4_task_id.raw); + + /* create place-holder thread representing the PD */ + L4_ThreadId_t l4t = make_l4_id(_pd_id, 0, _version); + L4_Word_t res = L4_ThreadControl(l4t, l4t, L4_Myself(), L4_nilthread, (void *)-1); + L4_Set_Priority(l4t, 0); + + if (res == 0) + panic("Task creation failed"); + + _l4_task_id = l4t; + + } else { + + /* core case */ + if (!L4_SameThreads(L4_Myself(), _l4_task_id)) + panic("Core creation is b0rken"); + } + + _setup_address_space(); +} + + +void Platform_pd::_destroy_pd() +{ + using namespace Pistachio; + + PT_DBG("_destroy_pd (_l4_task_id = 0x%08lx)", + _l4_task_id.raw); + + // Space Specifier == nilthread -> destroy + L4_Word_t res = L4_ThreadControl(_l4_task_id, L4_nilthread, + L4_nilthread, L4_nilthread, + (void *)-1); + + if (res != 1) { + panic("Destroying protection domain failed."); + } + + _l4_task_id = L4_nilthread; +} + + +int Platform_pd::_alloc_pd(signed pd_id) +{ + if (pd_id == PD_INVALID) { + unsigned i; + + for (i = PD_FIRST; i < PD_MAX; i++) + if (_pds()[i].free) break; + + /* no free protection domains available */ + if (i == PD_MAX) return -1; + + pd_id = i; + + } else { + if (!_pds()[pd_id].reserved || !_pds()[pd_id].free) + return -1; + } + + _pds()[pd_id].free = 0; + + _pd_id = pd_id; + _version = _pds()[pd_id].version; + + return pd_id; +} + + +void Platform_pd::_free_pd() +{ + unsigned id = _pd_id; + + if (_pds()[id].free) return; + + /* maximum reuse count reached leave non-free */ + if (_pds()[id].version++ == VERSION_MAX) return; + + _pds()[id].free = 1; + ++_pds()[id].version; +} + + +void Platform_pd::_init_threads() +{ + unsigned i; + + for (i = 0; i < THREAD_MAX; ++i) + _threads[i] = 0; +} + + +Platform_thread* Platform_pd::_next_thread() +{ + unsigned i; + + /* look for bound thread */ + for (i = 0; i < THREAD_MAX; ++i) + if (_threads[i]) break; + + /* no bound threads */ + if (i == THREAD_MAX) return 0; + + return _threads[i]; +} + + +int Platform_pd::_alloc_thread(int thread_id, Platform_thread *thread) +{ + int i = thread_id; + + /* look for free thread */ + if (thread_id == Platform_thread::THREAD_INVALID) { + + /* start from 1 here, because thread 0 is our placeholder thread */ + for (i = 1; i < THREAD_MAX; ++i) + if (!_threads[i]) break; + + /* no free threads available */ + if (i == THREAD_MAX) return -1; + } else { + if (_threads[i]) return -2; + } + + _threads[i] = thread; + + return i; +} + + +void Platform_pd::_free_thread(int thread_id) +{ + if (!_threads[thread_id]) + PWRN("double-free of thread %x.%x detected", _pd_id, thread_id); + + _threads[thread_id] = 0; +} + + +/*************************** + ** Public object members ** + ***************************/ + +int Platform_pd::bind_thread(Platform_thread *thread) +{ + using namespace Pistachio; + + /* thread_id is THREAD_INVALID by default - only core is the special case */ + int thread_id = thread->thread_id(); + L4_ThreadId_t l4_thread_id; + + int t = _alloc_thread(thread_id, thread); + if (t < 0) { + PERR("thread alloc failed"); + return -1; + } + thread_id = t; + l4_thread_id = make_l4_id(_pd_id, thread_id, _version); + + /* finally inform thread about binding */ + thread->bind(thread_id, l4_thread_id, this); + + if (verbose) _debug_log_threads(); + return 0; +} + + +void Platform_pd::unbind_thread(Platform_thread *thread) +{ + int thread_id = thread->thread_id(); + + /* unbind thread before proceeding */ + thread->unbind(); + + _free_thread(thread_id); + + if (verbose) _debug_log_threads(); +} + + +void Platform_pd::touch_utcb_space() +{ + L4_Word_t utcb_ptr; + + void *kip = get_kip(); + L4_ThreadId_t mylocalid = L4_MyLocalId(); + utcb_ptr = *(L4_Word_t *) &mylocalid; + utcb_ptr &= ~(L4_UtcbAreaSize (get_kip()) - 1); + + /* store a pointer to core's utcb area */ + _core_utcb_ptr = utcb_ptr; + + PT_DBG("Core's UTCB area is at 0x%08lx (0x%08lx)", + utcb_ptr, L4_UtcbAreaSize(kip)); + PWRN("Core can have %lu threads.", + L4_UtcbAreaSize(kip) / L4_UtcbSize(kip)); + + /* + * We used to touch the UTCB space here, but that was probably not + * neccessary. + */ +} + + +/* defined in genode.ld linker script */ +extern "C" L4_Word_t _kip_utcb_area; + + +void Platform_pd::_setup_address_space() +{ + L4_ThreadId_t ss = _l4_task_id; + + /* + * Check whether the address space we are about to change is Core's. + * If it is, we need to do little more than filling in some values. + */ + if (L4_SameThreads(ss, L4_Myself())) { + _kip_ptr = (L4_Word_t)get_kip(); + _utcb_ptr = _core_utcb_ptr; + return; + } + + /* setup a brand new address space */ + + L4_KernelInterfacePage_t *kip = (L4_KernelInterfacePage_t *)get_kip(); + L4_Fpage_t kip_space = L4_FpageLog2((L4_Word_t)kip, L4_KipAreaSizeLog2(kip)); + PT_DBG("kip_start = %08lx", L4_Address(kip_space)); + + /* utcb space follows the kip, but must be aligned */ + L4_Word_t kip_end = L4_Address(kip_space) + L4_KipAreaSize(kip); + PT_DBG("kip_end = %08lx", kip_end); + + L4_Word_t utcb_start = _core_utcb_ptr; +// L4_Word_t utcb_start = (L4_Word_t)(&_kip_utcb_area); + PT_DBG("utcb_start = %08lx", utcb_start); + L4_Word_t utcb_size = L4_UtcbSize(kip) * THREAD_MAX; + PT_DBG("utcb_size = %08lx", utcb_size); + + L4_Fpage_t utcb_space = L4_Fpage(utcb_start, + // L4_Fpage truncates this. + utcb_size + get_page_size() - 1 ); + + PT_DBG("Creating address space for %08lx.", ss.raw); + + L4_Word_t old_control; + int res; + + res = L4_SpaceControl(ss, 0, kip_space, utcb_space, L4_anythread, &old_control); + + if (res != 1 ) { + PERR("Error while setting up address space: %lu", L4_ErrorCode()); + panic("L4_SpaceControl"); + } + + PT_DBG("Address space for %08lx created!", ss.raw); + + _kip_ptr = L4_Address(kip_space); + _utcb_ptr = L4_Address(utcb_space); +} + + +L4_Word_t Platform_pd::_utcb_location(unsigned int thread_id) +{ + return _utcb_ptr + thread_id*L4_UtcbSize(get_kip()); +} + + +Platform_pd::Platform_pd(bool core) : + _l4_task_id(L4_MyGlobalId()) +{ + /* + * Start with version 2 to avoid being mistaken as local or + * interrupt ID. + */ + Pd_alloc free(false, true, 2); + + _init_threads(); + + /* init remainder */ + for (unsigned i = 0 ; i < PD_MAX; ++i) _pds()[i] = free; + + /* mark system threads as reserved */ + _pds()[0].reserved = 1; + _pds()[0].free = 0; + + _pd_id = _alloc_pd(PD_INVALID); + + _create_pd(false); +} + + +Platform_pd::Platform_pd(signed pd_id, bool create) +{ + if (!create) + panic("create must be true."); + + _init_threads(); + + _pd_id = _alloc_pd(pd_id); + + if (_pd_id < 0) { + PERR("pd alloc failed"); + } + + _create_pd(create); +} + + +Platform_pd::~Platform_pd() +{ + PT_DBG("Destroying all threads of pd %p", this); + + /* unbind all threads */ + while (Platform_thread *t = _next_thread()) unbind_thread(t); + + PT_DBG("Destroying pd %p", this); + + _destroy_pd(); + _free_pd(); +} + + +/*********************** + ** Debugging support ** + ***********************/ + +void Platform_pd::_debug_log_threads() +{ + PWRN("_debug_log_threads disabled."); +} + + +void Platform_pd::_debug_log_pds() +{ + PWRN("_debug_log_pds disabled."); +} diff --git a/base-pistachio/src/core/platform_thread.cc b/base-pistachio/src/core/platform_thread.cc new file mode 100644 index 000000000..ad866c08a --- /dev/null +++ b/base-pistachio/src/core/platform_thread.cc @@ -0,0 +1,235 @@ +/* + * \brief Pistachio thread facility + * \author Julian Stecklina + * \date 2008-03-19 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* core includes */ +#include +#include + +/* Pistachio includes */ +namespace Pistachio +{ +#include +#include +#include +#include +}; + +using namespace Genode; +using namespace Pistachio; + +static const bool verbose = false; +static const bool verbose2 = true; + +#define PT_DBG(args...) if (verbose) PDBG(args); else {} + + +void Platform_thread::set_cpu(unsigned int cpu_no) +{ + if (cpu_no >= L4_NumProcessors(get_kip())) { + PERR("Invalid processor number."); + return; + } + + if (L4_Set_ProcessorNo(_l4_thread_id, cpu_no) == 0) + PERR("Error setting processor number."); +} + + +int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no) +{ + L4_ThreadId_t thread = _l4_thread_id; + L4_ThreadId_t pager = _pager ? _pager->cap().tid() : L4_nilthread; + + /* XXX should always be the root task */ + L4_ThreadId_t preempter = L4_Myself(); + + PT_DBG("Trying to Platform_thread::start the thread '%s'.", _name); + + if (verbose2) + printf("thread '%s' has id 0x%08lx (task = 0x%x, thread = 0x%x)\n", + _name, thread.raw, _platform_pd->pd_id(), _thread_id); + + if (_thread_id == THREAD_INVALID) { + PERR("Trying to start a thread with invalid ID."); + return -1; + } + + L4_Word_t utcb_location = _platform_pd->_utcb_location(_thread_id); + + PT_DBG("New thread's utcb at %08lx.", utcb_location); + PT_DBG("Attaching thread to address space 0x%08lx.", + _platform_pd->_l4_task_id.raw); + + PT_DBG("sp = %p, ip = %p", sp, ip); + int ret = L4_ThreadControl(thread, _platform_pd->_l4_task_id, + preempter, L4_Myself(), (void *)utcb_location); + + PT_DBG("L4_ThreadControl() = %d", ret); + if (ret != 1) { + PERR("Error code = 0x%08lx", L4_ErrorCode()); + PERR("L4_ThreadControl failed."); + return -2; + } + + /* set real pager */ + ret = L4_ThreadControl(thread, _platform_pd->_l4_task_id, + L4_nilthread, pager, (void *)-1); + + if (ret != 1) { + PERR("Error code = 0x%08lx", L4_ErrorCode()); + PERR("Setting pager failed."); + return -3; + } + + /* get the thread running on the right cpu */ + set_cpu(cpu_no); + + /* assign priority */ + if (!L4_Set_Priority(thread, + Cpu_session::scale_priority(DEFAULT_PRIORITY, _priority))) + PWRN("Could not set thread prioritry to default"); + + /* send start message */ + L4_Msg_t msg; + L4_Clear(&msg); + L4_Append(&msg, (L4_Word_t)ip); + L4_Append(&msg, (L4_Word_t)sp); + L4_Load(&msg); + + L4_MsgTag_t tag = L4_Send(thread); + + if (L4_IpcFailed(tag)) { + PERR("Starting thread failed. (IPC error)"); + return -4; + } + + PT_DBG("Done starting thread."); + + return 0; +} + + +void Platform_thread::pause() +{ + PDBG("not implemented"); +} + + +void Platform_thread::resume() +{ + PDBG("not implemented"); +} + + +void Platform_thread::bind(int thread_id, L4_ThreadId_t l4_thread_id, + Platform_pd *pd) +{ + _thread_id = thread_id; + _l4_thread_id = l4_thread_id; + _platform_pd = pd; +} + + +void Platform_thread::unbind() +{ + PT_DBG("Killing thread 0x%08lx.", _l4_thread_id.raw); + + L4_Word_t res = L4_ThreadControl(_l4_thread_id, L4_nilthread, + L4_nilthread, L4_nilthread, (void *)-1); + + if (res != 1) + PERR("Deleting thread 0x%08lx failed. Continuing...", _l4_thread_id.raw); + + _thread_id = THREAD_INVALID; + _l4_thread_id = L4_nilthread; + _platform_pd = 0; +} + + +int Platform_thread::state(Thread_state *state_dst) +{ + L4_Word_t dummy; + L4_ThreadId_t dummy_tid; + L4_Word_t ip, sp; + + enum { + DELIVER = 1 << 9, + }; + + L4_ExchangeRegisters(_l4_thread_id, + DELIVER, + 0, 0, 0, 0, L4_nilthread, + &dummy, &sp, &ip, &dummy, &dummy, + &dummy_tid); + state_dst->ip = ip; + state_dst->sp = sp; + return 0; +} + + +void Platform_thread::cancel_blocking() +{ + L4_Word_t dummy; + L4_ThreadId_t dummy_tid; + + /* + * XXX: This implementation is not safe because it only cancels + * a currently executed blocking operation but it has no + * effect when the thread is executing user code and going + * to block soon. To solve this issue, we would need signalling + * semantics, which means that we flag the thread to being + * canceled the next time it enters the kernel. + */ + + /* control flags for 'L4_ExchangeRegisters' */ + enum { + CANCEL_SEND = 1 << 2, + CANCEL_RECV = 1 << 1, + CANCEL_IPC = CANCEL_SEND | CANCEL_RECV, + USER_DEFINED_HANDLE = 1 << 6, + RESUME = 1 << 8, + }; + + /* reset value for the thread's user-defined handle */ + enum { USER_DEFINED_HANDLE_ZERO = 0 }; + + L4_ExchangeRegisters(_l4_thread_id, + CANCEL_IPC | RESUME | USER_DEFINED_HANDLE, + 0, 0, 0, USER_DEFINED_HANDLE_ZERO, L4_nilthread, + &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy_tid); +} + + +Platform_thread::Platform_thread(const char *name, unsigned prio, int id) +: _thread_id(id), _l4_thread_id(L4_nilthread), _priority(prio), _pager(0) +{ + strncpy(_name, name, sizeof(_name)); +} + + +Platform_thread::~Platform_thread() +{ + /* + * We inform our protection domain about thread destruction, which will end up in + * Thread::unbind() + */ + if (_platform_pd) + _platform_pd->unbind_thread(this); +} diff --git a/base-pistachio/src/core/ram_session_support.cc b/base-pistachio/src/core/ram_session_support.cc new file mode 100644 index 000000000..9fe8d2c55 --- /dev/null +++ b/base-pistachio/src/core/ram_session_support.cc @@ -0,0 +1,27 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Norman Feske + * \date 2006-07-03 + * + * On L4, each dataspace _is_ a shared memory object. + * Therefore, these functions are empty. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "ram_session_component.h" + +using namespace Genode; + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { } + +void Ram_session_component::_clear_ds(Dataspace_component *ds) +{ + memset((void *)ds->phys_addr(), 0, ds->size()); +} diff --git a/base-pistachio/src/core/rm_session_support.cc b/base-pistachio/src/core/rm_session_support.cc new file mode 100644 index 000000000..a78197e58 --- /dev/null +++ b/base-pistachio/src/core/rm_session_support.cc @@ -0,0 +1,50 @@ +/* + * \brief Pistachio-specific part of RM-session implementation + * \author Norman Feske + * \date 2009-04-10 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include + +/* Pistachio includes */ +namespace Pistachio { +#include +#include +} + +using namespace Genode; + +static const bool verbose_unmap = false; + + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + /* + * Pistachio's 'unmap' syscall unmaps the specified flexpage from all + * address spaces to which we mapped the pages. We cannot target this + * operation to a specific L4 task. Hence, we unmap the dataspace from + * all tasks, not only for this RM client. + */ + + using namespace Pistachio; + + L4_Word_t page_size = get_page_size(); + + if (verbose_unmap) + printf("RM client %p (%lx) unmap core-local [%lx,%lx)\n", + this, badge(), core_local_base, core_local_base + size); + + addr_t addr = core_local_base; + for (; addr < core_local_base + size; addr += page_size) { + L4_Fpage_t fp = L4_Fpage(addr, page_size); + L4_Unmap(L4_FpageAddRightsTo(&fp, L4_FullyAccessible)); + } +} diff --git a/base-pistachio/src/core/target.inc b/base-pistachio/src/core/target.inc new file mode 100644 index 000000000..eed6e1e14 --- /dev/null +++ b/base-pistachio/src/core/target.inc @@ -0,0 +1,52 @@ +TARGET = core +REQUIRES = pistachio +LIBS = cxx ipc heap core_printf process pager lock \ + raw_signal raw_server kip hexdump + +GEN_CORE_DIR = $(BASE_DIR)/src/core + +SRC_CC = main.cc \ + multiboot_info.cc \ + ram_session_component.cc \ + ram_session_support.cc \ + rom_session_component.cc \ + cpu_session_component.cc \ + cpu_session_platform.cc \ + pd_session_component.cc \ + io_mem_session_component.cc \ + io_mem_session_support.cc \ + thread.cc \ + thread_start.cc \ + thread_bootstrap.cc \ + platform_thread.cc \ + platform_pd.cc \ + platform.cc \ + dataspace_component.cc \ + rm_session_component.cc \ + rm_session_support.cc \ + io_port_session_component.cc \ + irq_session_component.cc \ + signal_session_component.cc \ + signal_source_component.cc \ + dump_alloc.cc \ + context_area.cc + +INC_DIR += $(REP_DIR)/src/core/include \ + $(GEN_CORE_DIR)/include + +vpath main.cc $(GEN_CORE_DIR) +vpath ram_session_component.cc $(GEN_CORE_DIR) +vpath rom_session_component.cc $(GEN_CORE_DIR) +vpath cpu_session_component.cc $(GEN_CORE_DIR) +vpath pd_session_component.cc $(GEN_CORE_DIR) +vpath rm_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_component.cc $(GEN_CORE_DIR) +vpath io_mem_session_support.cc $(GEN_CORE_DIR) +vpath signal_session_component.cc $(GEN_CORE_DIR) +vpath signal_source_component.cc $(GEN_CORE_DIR) +vpath dataspace_component.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath context_area.cc $(GEN_CORE_DIR) +vpath thread_bootstrap.cc $(BASE_DIR)/src/base/thread +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath %.cc $(REP_DIR)/src/core diff --git a/base-pistachio/src/core/thread_start.cc b/base-pistachio/src/core/thread_start.cc new file mode 100644 index 000000000..1041e5ecc --- /dev/null +++ b/base-pistachio/src/core/thread_start.cc @@ -0,0 +1,62 @@ +/* + * \brief Implementation of Thread API interface on top of Platform_thread + * \author Norman Feske + * \date 2006-05-03 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include +#include + +using namespace Genode; + + +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + sleep_forever(); +} + + +void Thread_base::start() +{ + /* create and start platform thread */ + _tid.pt = new(platform()->core_mem_alloc()) Platform_thread(_context->name); + + platform_specific()->core_pd()->bind_thread(_tid.pt); + + _tid.pt->pager(platform_specific()->core_pager()); + _tid.l4id = _tid.pt->native_thread_id(); + + _tid.pt->start((void *)_thread_start, _context->stack); +} + + +void Thread_base::cancel_blocking() +{ + /* + * Within core, we never need to unblock threads + */ +} + + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() +{ + /* destruct platform thread */ + destroy(platform()->core_mem_alloc(), _tid.pt); +} diff --git a/base-pistachio/src/core/x86/platform_x86.cc b/base-pistachio/src/core/x86/platform_x86.cc new file mode 100644 index 000000000..115963040 --- /dev/null +++ b/base-pistachio/src/core/x86/platform_x86.cc @@ -0,0 +1,32 @@ +/* + * \brief Platform support specific to x86 + * \author Christian Helmuth + * \date 2006-04-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +#include "platform.h" +#include "util.h" + +namespace Pistachio { +#include +#include +} + +using namespace Genode; + +void Platform::_setup_io_port_alloc() +{ + /* setup allocator */ + enum { IO_PORT_RANGE_SIZE = 0x10000 }; + _io_port_alloc.add_range(0, IO_PORT_RANGE_SIZE); +} diff --git a/base-pistachio/src/core/x86/target.mk b/base-pistachio/src/core/x86/target.mk new file mode 100644 index 000000000..719b8306e --- /dev/null +++ b/base-pistachio/src/core/x86/target.mk @@ -0,0 +1,7 @@ +include $(PRG_DIR)/../target.inc + +REQUIRES += x86 +SRC_CC += platform_x86.cc + +vpath io_port_session_component.cc $(GEN_CORE_DIR)/x86 + diff --git a/base-pistachio/src/kernel/target.mk b/base-pistachio/src/kernel/target.mk new file mode 100644 index 000000000..29f5234f5 --- /dev/null +++ b/base-pistachio/src/kernel/target.mk @@ -0,0 +1,61 @@ +TARGET = kernel +REQUIRES += pistachio +KERNEL_BUILD_DIR = $(BUILD_BASE_DIR)/kernel/pistachio +KERNEL = $(KERNEL_BUILD_DIR)/x86-kernel +KERNEL_SRC = $(REP_DIR)/contrib/kernel +STARTUP_LIB = + +LIBGCC_DIR = $(dir $(shell $(CC) $(CC_MARCH) -print-libgcc-file-name)) +GCCINC_DIR = $(dir $(shell $(CC) -print-libgcc-file-name))include + +$(TARGET): $(KERNEL) + +.PHONY: $(KERNEL) + +$(KERNEL_BUILD_DIR)/Makefile: + $(VERBOSE_MK) MAKEFLAGS= $(MAKE) $(VERBOSE_DIR) -C $(KERNEL_SRC) BUILDDIR=$(dir $@) + $(VERBOSE)cp $(REP_DIR)/config/kernel $(KERNEL_BUILD_DIR)/config/config.out + + +# +# How to pass custom compiler flags to the Pistachio build system +# +# CFLAGS do not work because the variable gets assigned within the build +# system: +# +# Mk/Makeconf: 'CFLAGS = -ffreestanding $(CCFLAGS)' +# +# However, all flags specified in 'CCFLAGS' will end up in 'CFLAGS' and +# 'CCFLAGS' is never assigned - only extended via '+='. Because of this +# extension mechanism, we have to pass custom flags as environment variables +# rather than make arguments. If we specified 'CCFLAGS' as make argument, the +# '+=' mechanism would not work, leaving out some important flags assigned by +# the Pistachio build system. +# +# Because the Pistachio build system invokes the assembler via the GCC +# front end, we can pass 'CC_MARCH' as 'ASMFLAGS'. The linker, however, is +# invoked directly (using 'ld' rather than 'gcc'). Hence, we need to pass +# real linker flags (e.g., '-melf_i386' rather than -m32) to 'LDFLAGS'. +# +# The Pistachio build system has some built-in heuristics about where to find +# libgcc. Unfortunately, this heuristics break when using a multilib tool chain +# that uses -m64 by default. Hence, we need to supply the build system with the +# correct location via the 'GCCINSTALLDIR' and 'LIBGCCINC' variables. +# + +$(KERNEL_BUILD_DIR)/config/.config: $(KERNEL_BUILD_DIR)/Makefile + $(VERBOSE_MK)CCFLAGS="$(CC_MARCH)" MAKEFLAGS= \ + $(MAKE) $(VERBOSE_DIR) -C $(KERNEL_BUILD_DIR) batchconfig \ + GCCINSTALLDIR=$(LIBGCC_DIR) + +$(KERNEL): $(KERNEL_BUILD_DIR)/config/.config + $(VERBOSE_MK)CCFLAGS="$(CC_MARCH)" LDFLAGS="$(LD_MARCH)" ASMFLAGS="$(CC_MARCH)" MAKEFLAGS= \ + $(MAKE) $(VERBOSE_DIR) -C $(KERNEL_BUILD_DIR) \ + TOOLPREFIX=$(CROSS_DEV_PREFIX) \ + GCCINSTALLDIR=$(LIBGCC_DIR) \ + LIBGCCINC=$(GCCINC_DIR) + $(VERBOSE)ln -sf $@ $(BUILD_BASE_DIR)/bin/$(TARGET) + $(VERBOSE)touch $@ + +clean cleanall: + $(VERBOSE)rm -rf $(KERNEL_BUILD_DIR) diff --git a/base-pistachio/src/platform/_main_helper.h b/base-pistachio/src/platform/_main_helper.h new file mode 100644 index 000000000..b45b96b9d --- /dev/null +++ b/base-pistachio/src/platform/_main_helper.h @@ -0,0 +1,26 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Christian Prochaska + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM___MAIN_HELPER_H_ +#define _PLATFORM___MAIN_HELPER_H_ + + +/* Pistachio-specific definitions */ +enum { L4_PAGEMASK = ~0xFFF }; +enum { L4_PAGESIZE = 0x1000 }; + + +static void main_thread_bootstrap() { } + + +#endif /* _PLATFORM___MAIN_HELPER_H_ */ diff --git a/base-pistachio/src/util/hexdump/hexdump.cc b/base-pistachio/src/util/hexdump/hexdump.cc new file mode 100644 index 000000000..f91d21ff0 --- /dev/null +++ b/base-pistachio/src/util/hexdump/hexdump.cc @@ -0,0 +1,55 @@ +#include +#include + +using Genode::printf; + +void +Util::hexdump(const unsigned char *addr, unsigned long length) +{ + hexdump(addr, length, (unsigned long)addr); +} + +void +Util::hexdump(const unsigned char *addr, unsigned long length, unsigned long real_addr) +{ + unsigned long addr_int = (unsigned int)addr; + const unsigned long step = 16; + + real_addr = real_addr&(~(step-1)); + + for (unsigned long pos = addr_int&(~(step-1)); pos < (addr_int + length); + pos += step, real_addr += step) { + + printf(" 0x%08lx:", real_addr); + for (unsigned int lpos = pos; lpos < (pos + step); lpos ++) { + + if ((lpos & 3) == 0) printf(" "); + + if ((lpos < addr_int) || (lpos > (addr_int + length))) + printf(" "); + else + printf(" %02x", addr[lpos - addr_int]); + } + + printf(" | "); + + for (unsigned int lpos = pos; lpos < (pos + step); lpos ++) { + + if ((lpos & 3) == 0) printf(" "); + + unsigned char ch; + + if ((lpos < addr_int) || (lpos > (addr_int + length))) + ch = ' '; + else + ch = addr[lpos - addr_int]; + + if ((ch < 32) || (ch >= 127)) + ch = '.'; + + printf("%c", ch); + } + + printf("\n"); + } +} diff --git a/base/README b/base/README new file mode 100644 index 000000000..c5f5e82cb --- /dev/null +++ b/base/README @@ -0,0 +1,12 @@ +This is generic part of the Genode implementation. It consists of two parts: + +:_Core_: is the ultimate root of the Genode application tree + and provides abstractions for the lowest-level hardware resources + such as RAM, ROM, CPU, and generic device access. All generic parts of Core + can be found here - for system-specific implementations refer to the + appropriate 'base-' directory. + +:_Base libraries and protocols_: that are used by each Genode component + to interact with other components. This is the glue that holds everything + together. + diff --git a/base/etc/README b/base/etc/README new file mode 100644 index 000000000..662464378 --- /dev/null +++ b/base/etc/README @@ -0,0 +1,16 @@ +This directory contains default configuration files that are used +by the '.mk' files in the 'mk/' directory. By +convention, configuration files are first read from here +followed by a corresponding config file in '/etc/'. + + +Convention +~~~~~~~~~~ + +We include config files directly into makefiles. So the basic +makefile syntax applies here, too. + +Config files should +* Have '.conf' as filename extension. +* Use only assignments but provide no rules or other 'make'-magic. +* Not include other files! diff --git a/base/etc/tools.conf b/base/etc/tools.conf new file mode 100644 index 000000000..181b2c161 --- /dev/null +++ b/base/etc/tools.conf @@ -0,0 +1,57 @@ +# +# The following options let you define non-default tools to use +# +# CUSTOM_LD is only used for the progressive linking of libraries. +# It is not used for linking the final target. +# +#CUSTOM_CC = gcc +#CUSTOM_CXX = g++ +#CUSTOM_AS = as +#CUSTOM_LD = ld + +# +# For using a cross-compile tool chain, the names of all +# binutils and compilers are typically prefixed by the +# target platform. Instead of defining CUSTOM_* variables +# individually for each tool, the prefix can be defined +# via the following variable. +# +ifeq ($(filter-out $(SPECS),x86),) +CROSS_DEV_PREFIX ?= /usr/local/genode-gcc/bin/genode-x86- +endif +ifeq ($(filter-out $(SPECS),arm),) +CROSS_DEV_PREFIX ?= /usr/local/genode-gcc/bin/genode-arm- +endif + +# +# We use libsupc++ from g++ version 3 because +# this version does not use thread-local storage +# via the gs register. This is an interim solution. +# +#CUSTOM_CXX_LIB = g++-3.4 + +# +# The default optimization level used for compiling is -O2. +# By defining the variable CC_OLEVEL, you can override this +# default value, for example to optimize your binaries for size. +# +#CC_OLEVEL = -Os + +# +# If CC_OPT should be extended please use concatenation syntax like: +# +#CC_OPT += -ffunction-sections -fdata-sections + +# +# If CXX_LINK_OPT (linker options given to CXX) should be extended please use +# concatenation syntax like: +# +#CXX_LINK_OPT += -Wl,-gc-sections + +# +# On non-GNU systems, you may direct the build system to use GNU- +# specific tools. +# +#TAC ?= /opt/gnu/bin/tac +#GNU_FIND ?= /opt/gnu/bin/find +#GNU_XARGS ?= /opt/gnu/bin/xargs diff --git a/base/include/32bit/base/fixed_stdint.h b/base/include/32bit/base/fixed_stdint.h new file mode 100644 index 000000000..19cbbbc7b --- /dev/null +++ b/base/include/32bit/base/fixed_stdint.h @@ -0,0 +1,60 @@ +/* + * \brief Standard fixed-width integer types + * \author Christian Helmuth + * \author Norman Feske + * \date 2006-05-10 + * + * In contrast to most Genode header files, which are only usable for C++, + * this file is a valid C header file because a lot of existing C-based + * software relies on fixed-size integer types. These types, however, are + * platform specific but cannot be derived from the compiler's built-in + * types. Normally, the platform-dependent part of a C library provides + * these type definitions. This header file provides a single header for + * C and C++ programs that are not using a C library but need fixed-width + * integer types. + * + * All type definition are prefixed with 'genode_'. If included as a C++ + * header, the types are also defined within the 'Genode' namespace. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__32BIT__BASE__FIXED_STDINT_H_ +#define _INCLUDE__32BIT__BASE__FIXED_STDINT_H_ + + +/* + * Fixed-size types usable from both C and C++ programs + */ +typedef signed char genode_int8_t; +typedef unsigned char genode_uint8_t; +typedef signed short int genode_int16_t; +typedef unsigned short int genode_uint16_t; +typedef signed int genode_int32_t; +typedef unsigned int genode_uint32_t; +typedef signed long long int genode_int64_t; +typedef unsigned long long int genode_uint64_t; + + +/* + * Types residing within Genode's C++ namespace + */ +#ifdef __cplusplus +namespace Genode { + typedef genode_int8_t int8_t; + typedef genode_uint8_t uint8_t; + typedef genode_int16_t int16_t; + typedef genode_uint16_t uint16_t; + typedef genode_int32_t int32_t; + typedef genode_uint32_t uint32_t; + typedef genode_int64_t int64_t; + typedef genode_uint64_t uint64_t; +} +#endif + +#endif /* _INCLUDE__32BIT__BASE__FIXED_STDINT_H_ */ diff --git a/base/include/64bit/base/fixed_stdint.h b/base/include/64bit/base/fixed_stdint.h new file mode 100644 index 000000000..a5d3616aa --- /dev/null +++ b/base/include/64bit/base/fixed_stdint.h @@ -0,0 +1,50 @@ +/* + * \brief Standard fixed-width integer types + * \author Christian Helmuth + * \author Norman Feske + * \date 2006-05-10 + * + * For additional information, please revisit the 32bit version of this file. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__64BIT__BASE__FIXED_STDINT_H_ +#define _INCLUDE__64BIT__BASE__FIXED_STDINT_H_ + + +/* + * Fixed-size types usable from both C and C++ programs + */ +typedef signed char genode_int8_t; +typedef unsigned char genode_uint8_t; +typedef signed short int genode_int16_t; +typedef unsigned short int genode_uint16_t; +typedef signed int genode_int32_t; +typedef unsigned int genode_uint32_t; +typedef signed long int genode_int64_t; +typedef unsigned long int genode_uint64_t; + + +/* + * Types residing within Genode's C++ namespace + */ +#ifdef __cplusplus +namespace Genode { + typedef genode_int8_t int8_t; + typedef genode_uint8_t uint8_t; + typedef genode_int16_t int16_t; + typedef genode_uint16_t uint16_t; + typedef genode_int32_t int32_t; + typedef genode_uint32_t uint32_t; + typedef genode_int64_t int64_t; + typedef genode_uint64_t uint64_t; +} +#endif + +#endif /* _INCLUDE__64BIT__BASE__FIXED_STDINT_H_ */ diff --git a/base/include/README b/base/include/README new file mode 100644 index 000000000..b17d4a69b --- /dev/null +++ b/base/include/README @@ -0,0 +1,3 @@ +This directory contains include files of interfaces that are exported +by components to be used by other components. Each subdirectory corresponds +to the component exporting the interface. diff --git a/base/include/arm/cpu/cpu_state.h b/base/include/arm/cpu/cpu_state.h new file mode 100644 index 000000000..26af486ac --- /dev/null +++ b/base/include/arm/cpu/cpu_state.h @@ -0,0 +1,32 @@ +/* + * \brief CPU state + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2011-05-06 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ARM__CPU__CPU_STATE_H_ +#define _INCLUDE__ARM__CPU__CPU_STATE_H_ + +#include + +namespace Genode { + + struct Cpu_state + { + addr_t ip; + addr_t sp; + addr_t r[13]; + addr_t lr; + addr_t cpsr; + }; +} + +#endif /* _INCLUDE__ARM__CPU__CPU_STATE_H_ */ diff --git a/base/include/base/allocator.h b/base/include/base/allocator.h new file mode 100644 index 000000000..cf7304580 --- /dev/null +++ b/base/include/base/allocator.h @@ -0,0 +1,205 @@ +/* + * \brief Generic allocator interface + * \author Norman Feske + * \date 2006-04-16 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__ALLOCATOR_H_ +#define _INCLUDE__BASE__ALLOCATOR_H_ + +#include +#include + +namespace Genode { + + class Allocator + { + public: + + /********************* + ** Exception types ** + *********************/ + + class Out_of_memory : public Exception { }; + + /** + * Destructor + */ + virtual ~Allocator() { } + + /** + * Allocate block + * + * \param size block size to allocate + * \param out_addr resulting pointer to the new block, + * undefined in the error case + * \return true on success + */ + virtual bool alloc(size_t size, void **out_addr) = 0; + + /** + * Allocate typed block + * + * This template allocates a typed block returned as a pointer to + * a non-void type. By providing this function, we prevent the + * compiler from warning us about "dereferencing type-punned + * pointer will break strict-aliasing rules". + */ + template bool alloc(size_t size, T **out_addr) + { + void *addr = 0; + bool ret = alloc(size, &addr); + *out_addr = (T *)addr; + return ret; + } + + /** + * Free block a previously allocated block + */ + virtual void free(void *addr, size_t size) = 0; + + /** + * Return total amount of backing store consumed by the allocator + */ + virtual size_t consumed() { return 0; } + + /** + * Return meta-data overhead per block + */ + virtual size_t overhead(size_t size) = 0; + + + /*************************** + ** Convenience functions ** + ***************************/ + + /** + * Allocate block and signal error as an exception + * + * \param size block size to allocate + * \return pointer to the new block + * \throw Out_of_memory + */ + void *alloc(size_t size) + { + void *result = 0; + if (!alloc(size, &result)) + throw Out_of_memory(); + + return result; + } + }; + + + class Range_allocator : public Allocator + { + public: + + /** + * Destructor + */ + virtual ~Range_allocator() { } + + /** + * Add free address range to allocator + */ + virtual int add_range(addr_t base, size_t size) = 0; + + /** + * Remove address range from allocator + */ + virtual int remove_range(addr_t base, size_t size) = 0; + + /** + * Allocate block + * + * \param size size of new block + * \param out_addr start address of new block, + * undefined in the error case + * \param align alignment of new block specified + * as the power of two + * \return true on success + */ + virtual bool alloc_aligned(size_t size, void **out_addr, int align = 0) = 0; + + enum Alloc_return { ALLOC_OK = 0, OUT_OF_METADATA = -1, RANGE_CONFLICT = -2 }; + + /** + * Allocate block at address + * + * \param size size of new block + * \param addr desired address of block + * + * \return 'ALLOC_OK' on success, or + * 'OUT_OF_METADATA' if meta-data allocation failed, or + * 'RANGE_CONFLICT' if specified range is occupied + */ + virtual Alloc_return alloc_addr(size_t size, addr_t addr) = 0; + + /** + * Free a previously allocated block + * + * NOTE: We have to declare the 'Allocator::free' function here + * as well to make gcc happy. + */ + virtual void free(void *addr) = 0; + virtual void free(void *addr, size_t size) = 0; + + /** + * Return the sum of available memory + * + * Note that the returned value is not neccessarily allocatable + * because the memory may be fragmented. + */ + virtual size_t avail() = 0; + + /** + * Check if address is inside an allocated block + * + * \param addr address to check + * + * \return true if address is inside an allocated block, false + * otherwise + */ + virtual bool valid_addr(addr_t addr) = 0; + }; + + + /** + * Destroy object + * + * For destroying an object, we need to specify the allocator + * that was used by the object. Because we cannot pass the + * allocator directly to the delete operator, we mimic the + * delete operator using this template function. + * + * \param T implicit object type + * + * \param alloc allocator from which the object was allocated + * \param obj object to destroy + */ + template + void destroy(Allocator *alloc, T *obj) + { + if (!obj) + return; + + /* call destructors */ + obj->~T(); + + /* free memory at the allocator */ + alloc->free(obj, sizeof(T)); + } +} + +void *operator new (Genode::size_t size, Genode::Allocator *allocator); +void *operator new [] (Genode::size_t size, Genode::Allocator *allocator); + +#endif /* _INCLUDE__BASE__ALLOCATOR_H_ */ diff --git a/base/include/base/allocator_avl.h b/base/include/base/allocator_avl.h new file mode 100644 index 000000000..50c8a5cbe --- /dev/null +++ b/base/include/base/allocator_avl.h @@ -0,0 +1,325 @@ +/* + * \brief Interface of AVL-tree-based allocator + * \author Norman Feske + * \date 2006-04-16 + * + * Each block of the managed address space is present in two AVL trees, + * one tree ordered by the base addresses of the blocks and one tree ordered + * by the available capacity within the block. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__ALLOCATOR_AVL_H_ +#define _INCLUDE__BASE__ALLOCATOR_AVL_H_ + +#include +#include +#include +#include + +namespace Genode { + + class Allocator_avl_base : public Range_allocator + { + private: + + static bool _sum_in_range(addr_t addr, addr_t offset) { + return (~0UL - addr > offset); } + + protected: + + class Block : public Avl_node + { + private: + + addr_t _addr; /* base address */ + size_t _size; /* size of block */ + bool _used; /* block is in use */ + short _id; /* for debugging */ + size_t _max_avail; /* biggest free block size of subtree */ + + /** + * Request max_avail value of subtree + */ + inline size_t _child_max_avail(bool side) { + return child(side) ? child(side)->max_avail() : 0; } + + /** + * Query if block can hold a specified subblock + * + * \param n number of bytes + * \param align alignment (power of two) + * \return true if block fits + */ + inline bool _fits(size_t n, unsigned align = 1) { + return ((align_addr(addr(), align) >= addr()) && + _sum_in_range(align_addr(addr(), align), n) && + (align_addr(addr(), align) - addr() + n <= avail())); } + + public: + + /** + * Avl_node interface: compare two nodes + */ + bool higher(Block *a) { + return a->_addr >= _addr; } + + /** + * Avl_node interface: update meta data on node rearrangement + */ + void recompute(); + + /** + * Accessor functions + */ + inline int id() { return _id; } + inline addr_t addr() { return _addr; } + inline size_t avail() { return _used ? 0 : _size; } + inline size_t size() { return _size; } + inline bool used() { return _used; } + inline size_t max_avail() { return _max_avail; } + inline void used(bool used) { _used = used; } + + enum { FREE = false, USED = true }; + + /** + * Constructor + * + * This constructor is called from meta-data allocator during + * initialization of new meta-data blocks. + */ + Block() : _addr(0), _size(0), _used(0), _max_avail(0) { } + + /** + * Constructor + */ + Block(addr_t addr, size_t size, bool used) + : _addr(addr), _size(size), _used(used), + _max_avail(used ? 0 : size) + { + static int num_blocks; + _id = ++num_blocks; + } + + /** + * Find best-fitting block + */ + Block *find_best_fit(size_t size, unsigned align = 1); + + /** + * Find block that contains the specified address range + */ + Block *find_by_address(addr_t addr, size_t size = 0, + bool check_overlap = 0); + + /** + * Return sum of available memory in subtree + */ + size_t avail_in_subtree(void); + + /** + * Debug hooks + */ + void dump(); + void dump_dot(int indent = 0); + }; + + private: + + Avl_tree _addr_tree; /* blocks sorted by base address */ + Allocator *_md_alloc; /* meta-data allocator */ + size_t _md_entry_size; /* size of block meta-data entry */ + + /** + * Alloc meta-data block + */ + Block *_alloc_block_metadata(); + + /** + * Alloc two meta-data blocks in a transactional way + */ + bool _alloc_two_blocks_metadata(Block **dst1, Block **dst2); + + /** + * Create new block + */ + int _add_block(Block *block_metadata, + addr_t base, size_t size, bool used); + + /** + * Destroy block + */ + void _destroy_block(Block *b); + + /** + * Cut specified area from block + * + * The original block gets replaced by (up to) two smaller blocks + * with remaining space. + */ + void _cut_from_block(Block *b, addr_t cut_addr, size_t cut_size, + Block *dst1, Block *dst2); + + protected: + + /** + * Find block by specified address + */ + Block *_find_by_address(addr_t addr, size_t size = 0, + bool check_overlap = 0) const + { + Block *b = static_cast(_addr_tree.first()); + + /* if the tree has one or more nodes, start search */ + return b ? b->find_by_address(addr, size, check_overlap) : 0; + } + + /** + * Constructor + * + * This constructor can only be called from a derived class that + * provides an allocator for block meta-data entries. This way, + * we can attach custom information to block meta data. + */ + Allocator_avl_base(Allocator *md_alloc, size_t md_entry_size) : + _md_alloc(md_alloc), _md_entry_size(md_entry_size) { } + + public: + + /** + * Return address of any block of the allocator + * + * \param out_addr result that contains address of block + * \return true if block was found or + * false if there is no block available + * + * If no block was found, out_addr is set to zero. + */ + bool any_block_addr(addr_t *out_addr); + + /** + * Debug hook + */ + void dump_addr_tree(Block *addr_node = 0); + + + /******************************* + ** Range allocator interface ** + *******************************/ + + int add_range(addr_t base, size_t size); + int remove_range(addr_t base, size_t size); + bool alloc_aligned(size_t size, void **out_addr, int align = 0); + Alloc_return alloc_addr(size_t size, addr_t addr); + void free(void *addr); + size_t avail(); + bool valid_addr(addr_t addr); + + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t size, void **out_addr) { + return Allocator_avl_base::alloc_aligned(size, out_addr); } + + void free(void *addr, size_t) { free(addr); } + + /** + * Return the memory overhead per Block + * + * The overhead is a rough estimation. If a block is somewhere + * in the middle of a free area, we could consider the meta data + * for the two free subareas when calculating the overhead. + * + * The 'sizeof(umword_t)' represents the overhead of the meta-data + * slab allocator. + */ + size_t overhead(size_t size) { return sizeof(Block) + sizeof(umword_t); } + }; + + + /** + * AVL-based allocator with custom meta data attached to each block. + * + * \param BMDT block meta-data type + */ + template + class Allocator_avl_tpl : public Allocator_avl_base + { + private: + + /* + * Pump up the Block class with custom meta-data type + */ + class Block : public Allocator_avl_base::Block, public BMDT { }; + + Tslab _metadata; /* meta-data allocator */ + char _initial_md_block[1024]; /* first (static) meta-data block */ + + public: + + /** + * Constructor + * + * \param metadata_chunk_alloc pointer to allocator used to allocate + * meta-data blocks. If set to 0, + * use ourself for allocating our + * meta-data blocks. This works only + * if the managed memory is completely + * accessible by the allocator. + */ + explicit Allocator_avl_tpl(Allocator *metadata_chunk_alloc) : + Allocator_avl_base(&_metadata, sizeof(Block)), + _metadata((metadata_chunk_alloc) ? metadata_chunk_alloc : this, + (Slab_block *)&_initial_md_block) { } + + /** + * Assign custom meta data to block at specified address + */ + void metadata(void *addr, BMDT bmd) const + { + Block *b = static_cast(_find_by_address((addr_t)addr)); + if (b) *static_cast(b) = bmd; + } + + /** + * Return meta data that was attached to block at specified address + */ + BMDT* metadata(void *addr) const + { + Block *b = static_cast(_find_by_address((addr_t)addr)); + return b && b->used() ? b : 0; + } + + int add_range(addr_t base, size_t size) + { + /* + * We disable the slab block allocation while + * processing add_range to prevent avalanche + * effects when (slab trying to make an allocation + * at Allocator_avl that is empty). + */ + Allocator *md_bs = _metadata.backing_store(); + _metadata.backing_store(0); + int ret = Allocator_avl_base::add_range(base, size); + _metadata.backing_store(md_bs); + return ret; + } + }; + + + /** + * Define AVL-based allocator without any meta data attached to each block + */ + class Empty { }; + typedef Allocator_avl_tpl Allocator_avl; +} + +#endif /* _INCLUDE__BASE__ALLOCATOR_AVL_H_ */ diff --git a/base/include/base/allocator_guard.h b/base/include/base/allocator_guard.h new file mode 100644 index 000000000..14df9d886 --- /dev/null +++ b/base/include/base/allocator_guard.h @@ -0,0 +1,93 @@ +/* + * \brief A guard for arbitrary allocators to limit memory exhaustion + * \author Stefan Kalkowski + * \date 2010-08-20 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _ALLOCATOR_GUARD_H_ +#define _ALLOCATOR_GUARD_H_ + +#include +#include +#include + +namespace Genode { + + /** + * This class acts as guard for arbitrary allocators to limit + * memory exhaustion + */ + class Allocator_guard : public Allocator + { + private: + + Allocator *_allocator; /* allocator to guard */ + size_t _amount; /* total amount */ + size_t _consumed; /* already consumed bytes */ + + public: + + Allocator_guard(Allocator *allocator, size_t amount) + : _allocator(allocator), _amount(amount), _consumed(0) { } + + /** + * Extend allocation limit + */ + void upgrade(size_t additional_amount) { + _amount += additional_amount; } + + + /************************* + ** Allocator interface ** + *************************/ + + /** + * Allocate block + * + * \param size block size to allocate + * \param out_addr resulting pointer to the new block, + * undefined in the error case + * \return true on success + */ + bool alloc(size_t size, void **out_addr) + { + if ((_amount - _consumed) < (size + _allocator->overhead(size))) { + PWRN("Quota exceeded! amount=%zd, size=%zd, consumed=%zd", + _amount, (size + _allocator->overhead(size)), _consumed); + return false; + } + bool b = _allocator->alloc(size, out_addr); + if (b) + _consumed += size + _allocator->overhead(size); + return b; + } + + /** + * Free block a previously allocated block + */ + void free(void *addr, size_t size) + { + _allocator->free(addr, size); + _consumed -= size - _allocator->overhead(size); + } + + /** + * Return total amount of backing store consumed by the allocator + */ + size_t consumed() { return _consumed; } + + /** + * Return meta-data overhead per block + */ + size_t overhead(size_t size) { return _allocator->overhead(size); } + }; +} + +#endif /* _ALLOCATOR_GUARD_H_ */ diff --git a/base/include/base/blocking.h b/base/include/base/blocking.h new file mode 100644 index 000000000..cb07d68fe --- /dev/null +++ b/base/include/base/blocking.h @@ -0,0 +1,28 @@ +/* + * \brief Support for blocking operations + * \author Norman Feske + * \date 2007-09-06 + * + * In Genode, two operations may block a thread, + * waiting at a lock or performing an IPC call. + * Both operations may be canceled when killing + * the thread. In this case, the thread unblocks + * and throws an exception, and therefore, is able + * to clean up the thread state before exiting. + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__BLOCKING_H_ +#define _INCLUDE__BASE__BLOCKING_H_ + +#include + +namespace Genode { class Blocking_canceled : public Exception { }; } + +#endif /* _INCLUDE__BASE__BLOCKING_H_ */ diff --git a/base/include/base/cancelable_lock.h b/base/include/base/cancelable_lock.h new file mode 100644 index 000000000..6253a3b74 --- /dev/null +++ b/base/include/base/cancelable_lock.h @@ -0,0 +1,94 @@ +/* + * \brief Basic locking primitive + * \author Norman Feske + * \date 2006-07-26 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CANCELABLE_LOCK_H_ +#define _INCLUDE__BASE__CANCELABLE_LOCK_H_ + +#include +#include +#include + +namespace Genode { + + class Cancelable_lock + { + private: + + class Applicant + { + private: + + Native_thread_id _tid; + Applicant *_to_wake_up; + + public: + + explicit Applicant(Native_thread_id tid) + : _tid(tid), _to_wake_up(0) { } + + void applicant_to_wake_up(Applicant *to_wake_up) { + _to_wake_up = to_wake_up; } + + Applicant *applicant_to_wake_up() { return _to_wake_up; } + + Native_thread_id tid() { return _tid; } + + /** + * Called from previous lock owner + */ + void wake_up(); + + bool operator == (Applicant &a) { return _tid == a.tid(); } + bool operator != (Applicant &a) { return _tid != a.tid(); } + }; + + /* + * Note that modifications of the applicants queue must be performed + * atomically. Hence, we use the additional spinlock here. + */ + + volatile int _spinlock_state; + volatile int _state; + + Applicant* volatile _last_applicant; + Applicant _owner; + + public: + + enum State { LOCKED, UNLOCKED }; + + /** + * Constructor + */ + explicit Cancelable_lock(State initial = UNLOCKED); + + /** + * Try to aquire lock an block while lock is not free + * + * This function may throw a Genode::Blocking_canceled exception. + */ + void lock(); + + /** + * Release lock + */ + void unlock(); + + /** + * Lock guard + */ + typedef Genode::Lock_guard Guard; + }; +} + +#endif /* _INCLUDE__BASE__CANCELABLE_LOCK_H_ */ diff --git a/base/include/base/capability.h b/base/include/base/capability.h new file mode 100644 index 000000000..c8f360d2a --- /dev/null +++ b/base/include/base/capability.h @@ -0,0 +1,291 @@ +/* + * \brief Capability + * \author Norman Feske + * \date 2011-05-22 + * + * A typed capability is a capability tied to one specifiec RPC interface + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CAPABILITY_H_ +#define _INCLUDE__BASE__CAPABILITY_H_ + +#include +#include +#include + +namespace Genode { + + /** + * Forward declaration needed for internal interfaces of 'Capability' + */ + class Ipc_client; + + + /** + * Capability that is not associated with a specific RPC interface + */ + typedef Native_capability Untyped_capability; + + + /** + * Capability referring to a specific RPC interface + * + * \param RPC_INTERFACE class containing the RPC interface declaration + */ + template + class Capability : public Untyped_capability + { + private: + + /** + * Insert RPC arguments into the message buffer + */ + template + void _marshal_args(Ipc_client &ipc_client, ATL &args); + + void _marshal_args(Ipc_client &, Meta::Empty &) { } + + /** + * Unmarshal single RPC argument from the message buffer + */ + template + void _unmarshal_result(Ipc_client &ipc_client, T &arg, + Meta::Overload_selector); + + template + void _unmarshal_result(Ipc_client &ipc_client, T &arg, + Meta::Overload_selector); + + template + void _unmarshal_result(Ipc_client &, T &arg, + Meta::Overload_selector) { } + + /** + * Read RPC results from the message buffer + */ + template + void _unmarshal_results(Ipc_client &ipc_client, ATL &args); + + void _unmarshal_results(Ipc_client &, Meta::Empty &) { } + + /** + * Check RPC return code for the occurrence of exceptions + * + * A server-side exception is indicated by a non-zero exception + * code. Each exception code corresponds to an entry in the + * exception type list specified in the RPC function declaration. + * The '_check_for_exception' function template throws the + * exception type belonging to the received exception code. + */ + template + void _check_for_exceptions(Rpc_exception_code const exc_code, + Meta::Overload_selector) + { + enum { EXCEPTION_CODE = RPC_EXCEPTION_BASE - Meta::Length::Value }; + + if (exc_code == EXCEPTION_CODE) + throw typename EXC_TL::Head(); + + _check_for_exceptions(exc_code, Meta::Overload_selector()); + } + + void _check_for_exceptions(Rpc_exception_code const, + Meta::Overload_selector) { } + + /** + * Perform RPC call, arguments passed a as nested 'Ref_tuple' object + */ + template + void _call(typename IF::Client_args &args, typename IF::Ret_type &ret); + + /** + * Shortcut for querying argument types used in 'call' functions + */ + template + struct Arg + { + typedef typename Meta::Type_at::Type Type; + }; + + template + Untyped_capability + _check_compatibility(Capability const &cap) + { + FROM_RPC_INTERFACE *from = 0; + RPC_INTERFACE *to = from; + (void)to; + return cap; + } + + public: + + typedef RPC_INTERFACE Rpc_interface; + + /** + * Constructor + * + * This implicit constructor checks at compile time for the + * compatibility of the source and target capability types. The + * construction is performed only if the target capability type is + * identical to or a base type of the source capability type. + */ + template + Capability(Capability const &cap) + : Untyped_capability(_check_compatibility(cap)) + { } + + /** + * Default constructor creates invalid capability + */ + Capability() { } + + /* + * Suppress warning about uninitialized 'ret' variable in 'call' + * functions on compilers that support the #praga. If this is + * not the case, the pragma can be masked by supplying the + * 'SUPPRESS_GCC_PRAGMA_WUNINITIALIZED' define to the compiler. + */ + #ifndef SUPPRESS_GCC_PRAGMA_WUNINITIALIZED + #pragma GCC diagnostic ignored "-Wuninitialized" call(); + #endif + + template + typename Trait::Call_return::Type + call() + { + Meta::Empty e; + typename Trait::Call_return::Type ret; + _call(e, ret); + return ret; + } + + template + typename Trait::Call_return::Type + call(typename Arg::Type v1) + { + Meta::Empty e; + typename IF::Client_args args(v1, e); + typename Trait::Call_return::Type ret; + _call(args, ret); + return ret; + } + + template + typename Trait::Call_return::Type + call(typename Arg::Type v1, typename Arg::Type v2) + { + Meta::Empty e; + typename IF::Client_args args(v1, v2, e); + typename Trait::Call_return::Type ret; + _call(args, ret); + return ret; + } + + template + typename Trait::Call_return::Type + call(typename Arg::Type v1, typename Arg::Type v2, + typename Arg::Type v3) + { + Meta::Empty e; + typename IF::Client_args args(v1, v2, v3, e); + typename Trait::Call_return::Type ret; + _call(args, ret); + return ret; + } + + template + typename Trait::Call_return::Type + call(typename Arg::Type v1, typename Arg::Type v2, + typename Arg::Type v3, typename Arg::Type v4) + { + Meta::Empty e; + typename IF::Client_args args(v1, v2, v3, v4, e); + typename Trait::Call_return::Type ret; + _call(args, ret); + return ret; + } + + template + typename Trait::Call_return::Type + call(typename Arg::Type v1, typename Arg::Type v2, + typename Arg::Type v3, typename Arg::Type v4, + typename Arg::Type v5) + { + Meta::Empty e; + typename IF::Client_args args(v1, v2, v3, v4, v5, e); + typename Trait::Call_return::Type ret; + _call(args, ret); + return ret; + } + + template + typename Trait::Call_return::Type + call(typename Arg::Type v1, typename Arg::Type v2, + typename Arg::Type v3, typename Arg::Type v4, + typename Arg::Type v5, typename Arg::Type v6) + { + Meta::Empty e; + typename IF::Client_args args(v1, v2, v3, v4, v5, v6, e); + typename Trait::Call_return::Type ret; + _call(args, ret); + return ret; + } + + template + typename Trait::Call_return::Type + call(typename Arg::Type v1, typename Arg::Type v2, + typename Arg::Type v3, typename Arg::Type v4, + typename Arg::Type v5, typename Arg::Type v6, + typename Arg::Type v7) + { + Meta::Empty e; + typename IF::Client_args args(v1, v2, v3, v4, v5, v6, v7, e); + typename Trait::Call_return::Type ret; + _call(args, ret); + return ret; + } + }; + + + /** + * Convert an untyped capability to a typed capability + */ + template + Capability + reinterpret_cap_cast(Untyped_capability const &untyped_cap) + { + Capability typed_cap; + + /* + * The object layout of untyped and typed capabilities is identical. + * Hence we can use memcpy to load the values of the supplied untyped + * capability into a typed capability. + */ + ::Genode::memcpy(&typed_cap, &untyped_cap, sizeof(untyped_cap)); + return typed_cap; + } + + + /** + * Convert capability type from an interface base type to an inherited + * interface type + */ + template + Capability + static_cap_cast(Capability cap) + { + /* check interface compatibility */ + (void)static_cast((FROM_RPC_INTERFACE *)0); + + return reinterpret_cap_cast(cap); + } +} + +#endif /* _INCLUDE__BASE__CAPABILITY_H_ */ diff --git a/base/include/base/child.h b/base/include/base/child.h new file mode 100644 index 000000000..fe832ef0c --- /dev/null +++ b/base/include/base/child.h @@ -0,0 +1,583 @@ +/* + * \brief Child creation framework + * \author Norman Feske + * \date 2006-07-22 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CHILD_H_ +#define _INCLUDE__BASE__CHILD_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + /** + * Child policy interface + * + * A child-policy object is an argument to a 'Child'. It is responsible for + * taking policy decisions regarding the parent interface. Most importantly, + * it defines how session requests are resolved and how session arguments + * are passed to servers when creating sessions. + */ + struct Child_policy + { + virtual ~Child_policy() { } + + /** + * Return process name of the child + */ + virtual const char *name() const = 0; + + /** + * Determine service to provide a session request + * + * \return Service to be contacted for the new session, or + * 0 if session request could not be resolved + */ + virtual Service *resolve_session_request(const char *service_name, + const char *args) + { return 0; } + + /** + * Apply transformations to session arguments + */ + virtual void filter_session_args(const char *service, + char *args, size_t args_len) { } + + /** + * Register a service provided by the child + * + * \param name service name + * \param root interface for creating sessions for the service + * \param alloc allocator to be used for child-specific + * meta-data allocations + * \return true if announcement succeeded, or false if + * child is not permitted to announce service + */ + virtual bool announce_service(const char *name, + Root_capability root, + Allocator *alloc) + { return false; } + + /** + * Unregister services that had been provided by the child + */ + virtual void unregister_services() { } + + /** + * Exit child + */ + virtual void exit(int exit_value) + { + PDBG("child exited with exit value %d", exit_value); + } + }; + + + /** + * Implementation of the parent interface that supports resource trading + * + * There are three possible cases of how a session can be provided to + * a child: + * + * # The service is implemented locally + * # The session was obtained by asking our parent + * # The session is provided by one of our children + * + * These types must be differentiated for the quota management when a child + * issues the closing of a session or a transfers quota via our parent + * interface. + * + * If we close a session to a local service, we transfer the session quota + * from our own account to the client. + * + * If we close a parent session, we receive the session quota on our own + * account and must transfer this amount to the session-closing child. + * + * If we close a session provided by a server child, we close the session + * at the server, transfer the session quota from the server's ram session + * to our account, and subsequently transfer the same amount from our + * account to the client. + */ + class Child : protected Rpc_object + { + private: + + /** + * Representation of an open session + */ + class Session : public Object_pool::Entry, + public List::Element + { + private: + + enum { IDENT_LEN = 16 }; + + /** + * Session capability at the server + */ + Session_capability _cap; + + /** + * Service interface that was used to create the session + */ + Service *_service; + + /** + * Server implementing the session + * + * Even though we can normally determine the server of the + * session via '_service->server()', this does not apply + * when destructing a server. During destruction, we use + * the 'Server' pointer as opaque key for revoking active + * sessions of the server. So we keep a copy independent of + * the 'Service' object. + */ + Server *_server; + + /** + * Total of quota associated with this session + */ + size_t _donated_ram_quota; + + /** + * Name of session, used for debugging + */ + char _ident[IDENT_LEN]; + + public: + + /** + * Constructor + * + * \param session session capability + * \param service service that implements the session + * \param ram_quota initial quota donation associated with + * the session + * \param ident optional session identifier, used for + * debugging + */ + Session(Session_capability session, Service *service, + size_t ram_quota, const char *ident = "") + : + Object_pool::Entry(session), _cap(session), + _service(service), _server(service->server()), + _donated_ram_quota(ram_quota) { + strncpy(_ident, ident, sizeof(_ident)); } + + /** + * Default constructor creates invalid session + */ + Session() : _service(0), _donated_ram_quota(0) { } + + /** + * Extend amount of ram attached to the session + */ + void upgrade_ram_quota(size_t ram_quota) { + _donated_ram_quota += ram_quota; } + + /** + * Accessors + */ + Session_capability cap() const { return _cap; } + size_t donated_ram_quota() const { return _donated_ram_quota; } + bool valid() const { return _service != 0; } + Service *service() const { return _service; } + Server *server() const { return _server; } + const char *ident() const { return _ident; } + }; + + + /** + * Guard for transferring quota donation + * + * This class is used to provide transactional semantics of quota + * transfers. Establishing a new session involves several steps, in + * particular subsequent quota transfers. If one intermediate step + * fails, we need to revert all quota transfers that already took + * place. When instantated at a local scope, a 'Transfer' object + * guards a quota transfer. If the scope is left without prior an + * explicit acknowledgement of the transfer (for example via an + * exception), the destructor the 'Transfer' object reverts the + * transfer in flight. + */ + class Transfer { + + bool _ack; + size_t _quantum; + Ram_session_capability _from; + Ram_session_capability _to; + + public: + + /** + * Constructor + * + * \param quantim number of bytes to transfer + * \param from donator RAM session + * \param to receiver RAM session + */ + Transfer(size_t quantum, + Ram_session_capability from, + Ram_session_capability to) + : _ack(false), _quantum(quantum), _from(from), _to(to) + { + if (_from.valid() && _to.valid() && + Ram_session_client(_from).transfer_quota(_to, quantum)) { + PWRN("not enough quota for a donation of %zd bytes", quantum); + throw Quota_exceeded(); + } + } + + /** + * Destructor + * + * The destructor will be called when leaving the scope of + * the 'session' function. If the scope is left because of + * an error (e.g., an exception), the donation will be + * reverted. + */ + ~Transfer() + { + if (!_ack && _from.valid() && _to.valid()) + Ram_session_client(_to).transfer_quota(_from, _quantum); + } + + /** + * Acknowledge quota donation + */ + void acknowledge() { _ack = true; } + }; + + /* RAM session that contains the quota of the child */ + Ram_session_capability _ram; + Ram_session_client _ram_session_client; + + /* CPU session that contains the quota of the child */ + Cpu_session_capability _cpu; + + /* RM session representing the address space of the child */ + Rm_session_capability _rm; + + /* heap for child-specific allocations using the child's quota */ + Heap _heap; + + Rpc_entrypoint *_entrypoint; + Parent_capability _parent_cap; + + Process _process; + + /* sessions opened by the child */ + Lock _lock; /* protect list manipulation */ + Object_pool _session_pool; + List _session_list; + + /* child policy */ + Child_policy *_policy; + + /** + * Session-argument buffer + */ + char _args[Parent::Session_args::MAX_SIZE]; + + /** + * Attach session information to a child + * + * \throw Ram_session::Quota_exceeded the child's heap partition cannot + * hold the session meta data + */ + void _add_session(const Session &s) + { + Lock::Guard lock_guard(_lock); + + /* + * Store session information in a new child's meta data + * structure. The allocation from 'heap()' may throw a + * 'Ram_session::Quota_exceeded' exception. + */ + Session *session = 0; + try { + session = new (heap()) + Session(s.cap(), s.service(), + s.donated_ram_quota(), s.ident()); } + catch (Allocator::Out_of_memory) { + throw Parent::Quota_exceeded(); } + + /* these functions may also throw 'Ram_session::Quota_exceeded' */ + _session_pool.insert(session); + _session_list.insert(session); + } + + /** + * Close session and revert quota donation associated with it + */ + void _remove_session(Session *s) + { + Lock::Guard lock_guard(_lock); + + /* forget about this session */ + _session_pool.remove(s); + _session_list.remove(s); + + /* return session quota to the ram session of the child */ + if (env()->ram_session()->transfer_quota(_ram, s->donated_ram_quota())) + PERR("We ran out of our own quota"); + + destroy(heap(), s); + } + + public: + + /** + * Constructor + * + * \param elf_ds dataspace containing the binary + * \param ram RAM session with the child's quota + * \param cpu CPU session with the child's quota + * \param entrypoint server entrypoint to serve the parent interface + * \param policy child policy + * + * If assigning a separate entry point to each child, the host of + * multiple children is able to handle a blocking invocation of + * the parent interface of one child while still maintaining the + * service to other children, each having an independent entry + * point. + */ + Child(Dataspace_capability elf_ds, + Ram_session_capability ram, + Cpu_session_capability cpu, + Rm_session_capability rm, + Rpc_entrypoint *entrypoint, + Child_policy *policy) + : + _ram(ram), _ram_session_client(ram), _cpu(cpu), _rm(rm), + _heap(&_ram_session_client, env()->rm_session()), + _entrypoint(entrypoint), + _parent_cap(_entrypoint->manage(this)), + _process(elf_ds, ram, cpu, rm, _parent_cap, policy->name(), 0), + _policy(policy) + { } + + /** + * Destructor + * + * On destruction of a child, we close all sessions of the child to + * other services. + */ + virtual ~Child() + { + _entrypoint->dissolve(this); + _policy->unregister_services(); + + for (Session *s; (s = _session_pool.first()); ) + close(s->cap()); + } + + /** + * Return heap that uses the child's quota + */ + Allocator *heap() { return &_heap; } + + Ram_session_capability ram_session_cap() const { return _ram; } + Cpu_session_capability cpu_session_cap() const { return _cpu; } + Rm_session_capability rm_session_cap() const { return _rm; } + + /** + * Discard all sessions to specified service + * + * When this function is called, we assume the server protection + * domain to be dead and all that all server quota was already + * transferred back to our own 'env()->ram_session()' account. Note + * that the specified server object may not exist anymore. We do + * not de-reference the server argument in here! + */ + void revoke_server(const Server *server) + { + while (1) { + + /* search session belonging to the specified server */ + Session *s = _session_list.first(); + for ( ; s && (s->server() != server); s = s->next()); + + /* if no matching session exists, we are done */ + if (!s) return; + + _remove_session(s); + } + } + + + /********************** + ** Parent interface ** + **********************/ + + void announce(Service_name const &name, Root_capability root) + { + if (!name.is_valid_string()) return; + + _policy->announce_service(name.string(), root, heap()); + } + + Session_capability session(Service_name const &name, Session_args const &args) + { + if (!name.is_valid_string() || !args.is_valid_string()) throw Unavailable(); + + /* return sessions that we created for the child */ + if (!strcmp("Env::ram_session", name.string())) return _ram; + if (!strcmp("Env::cpu_session", name.string())) return _cpu; + if (!strcmp("Env::rm_session", name.string())) return _rm; + if (!strcmp("Env::pd_session", name.string())) return _process.pd_session_cap(); + + /* filter session arguments according to the child policy */ + strncpy(_args, args.string(), sizeof(_args)); + _policy->filter_session_args(name.string(), _args, sizeof(_args)); + + /* transfer the quota donation from the child's account to ourself */ + size_t ram_quota = Arg_string::find_arg(_args, "ram_quota").long_value(0); + + Transfer donation_from_child(ram_quota, _ram, env()->ram_session_cap()); + + Service *service = _policy->resolve_session_request(name.string(), _args); + + /* raise an error if no matching service provider could be found */ + if (!service) + throw Service_denied(); + + /* transfer session quota from ourself to the service provider */ + Transfer donation_to_service(ram_quota, env()->ram_session_cap(), + service->ram_session_cap()); + + /* create session */ + Session_capability cap; + try { cap = service->session(_args); } + catch (Service::Invalid_args) { throw Service_denied(); } + catch (Service::Unavailable) { throw Service_denied(); } + catch (Service::Quota_exceeded) { throw Quota_exceeded(); } + + /* register session */ + try { _add_session(Session(cap, service, ram_quota, name.string())); } + catch (Ram_session::Quota_exceeded) { throw Quota_exceeded(); } + + /* finish transaction */ + donation_from_child.acknowledge(); + donation_to_service.acknowledge(); + + return cap; + } + + void upgrade(Session_capability to_session, Upgrade_args const &args) + { + Session *s = _session_pool.obj_by_cap(to_session); + + if (!s) { + PWRN("no session structure found - nothing to be done\n"); + return; + } + + if (!args.is_valid_string()) { + PWRN("no valid session-upgrade arguments"); + return; + } + + size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + /* transfer quota from client to ourself */ + Transfer donation_from_child(ram_quota, _ram, + env()->ram_session_cap()); + + /* transfer session quota from ourself to the service provider */ + Transfer donation_to_service(ram_quota, env()->ram_session_cap(), + s->service()->ram_session_cap()); + + try { s->service()->upgrade(to_session, args.string()); } + catch (Service::Quota_exceeded) { throw Quota_exceeded(); } + + /* remember new amount attached to the session */ + s->upgrade_ram_quota(ram_quota); + + /* finish transaction */ + donation_from_child.acknowledge(); + donation_to_service.acknowledge(); + } + + void close(Session_capability session_cap) + { + /* refuse to close the child's initial sessions */ + if (session_cap.local_name() == _ram.local_name() + || session_cap.local_name() == _cpu.local_name() + || session_cap.local_name() == _rm.local_name() + || session_cap.local_name() == _process.pd_session_cap().local_name()) + return; + + Session *s = _session_pool.obj_by_cap(session_cap); + + if (!s) { + PWRN("no session structure found"); + return; + } + + /* + * There is a chance that the server is not responding to + * the 'close' call, making us block infinitely. However, + * by using core's cancel-blocking mechanism, we can cancel + * the 'close' call by another (watchdog) thread that + * invokes 'cancel_blocking' at our thread after a timeout. + * The unblocking is reflected at the API level as an + * 'Blocking_canceled' exception. We catch this exception + * to proceed with normal operation after being unblocked. + */ + try { s->service()->close(s->cap()); } + catch (Blocking_canceled) { + PDBG("Got Blocking_canceled exception during %s->close call\n", + s->ident()); } + + /* + * If the session was provided by a child of us, + * 'server()->ram_session_cap()' returns the RAM session of the + * corresponding child. Since the session to the server is + * closed now, we expect that the server released all donated + * resources and we can decrease the servers' quota. + * + * If this goes wrong, the server is misbehaving. + */ + if (s->service()->ram_session_cap().valid()) { + Ram_session_client server_ram(s->service()->ram_session_cap()); + if (server_ram.transfer_quota(env()->ram_session_cap(), + s->donated_ram_quota())) { + PERR("Misbehaving server '%s'!", s->service()->name()); + } + } + + _remove_session(s); + } + + void exit(int exit_value) + { + /* + * This function receives the hint from the child that now, its + * a good time to kill it. An inherited child class could use + * this hint to schedule the destruction of the child object. + * + * Note that the child object must not be destructed from by + * this function because it is executed by the thread contained + * in the child object. + */ + return _policy->exit(exit_value); + } + }; +} + +#endif /* _INCLUDE__BASE__CHILD_H_ */ diff --git a/base/include/base/connection.h b/base/include/base/connection.h new file mode 100644 index 000000000..54366e4b2 --- /dev/null +++ b/base/include/base/connection.h @@ -0,0 +1,98 @@ +/* + * \brief Connection to a service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CONNECTION_H_ +#define _INCLUDE__BASE__CONNECTION_H_ + +#include +#include + +namespace Genode { + + /** + * Representation of an open connection to a service + */ + template + class Connection + { + public: + + enum On_destruction { CLOSE = false, KEEP_OPEN = true }; + + private: + + /* + * Because the argument string is used with the parent interface, + * the message-buffer size of the parent-interface provides a + * realistic upper bound for dimensioning the format- string + * buffer. + */ + enum { FORMAT_STRING_SIZE = Parent::Session_args::MAX_SIZE }; + + Capability _cap; + + On_destruction _on_destruction; + + public: + + /** + * Constructor + * + * \param cap session capability + * \param od session policy applied when destructing the connection + */ + Connection(Capability cap, On_destruction od = CLOSE): + _cap(cap), _on_destruction(od) { } + + /** + * Destructor + */ + ~Connection() + { + if (_on_destruction == CLOSE) + env()->parent()->close(_cap); + } + + /** + * Return session capability + */ + Capability cap() const { return _cap; } + + /** + * Define session policy + */ + void on_destruction(On_destruction od) { _on_destruction = od; } + + /** + * Shortcut for env()->parent()->session() function + */ + Capability session(const char *format_args, ...) + { + char buf[FORMAT_STRING_SIZE]; + + /* process format string */ + va_list list; + va_start(list, format_args); + + String_console sc(buf, FORMAT_STRING_SIZE); + sc.vprintf(format_args, list); + + va_end(list); + + /* call parent interface with the resulting argument buffer */ + return env()->parent()->session(buf); + } + }; +} + +#endif /* _INCLUDE__BASE__CONNECTION_H_ */ diff --git a/base/include/base/console.h b/base/include/base/console.h new file mode 100644 index 000000000..6fbe34ca1 --- /dev/null +++ b/base/include/base/console.h @@ -0,0 +1,60 @@ +/* + * \brief Simple console for debug output + * \author Norman Feske + * \date 2006-04-07 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CONSOLE_H_ +#define _INCLUDE__BASE__CONSOLE_H_ + +#include + +namespace Genode { + + class Console + { + public: + + virtual ~Console() {} + + /** + * Print format string + */ + void printf(const char *format, ...) __attribute__((format(printf, 2, 3))); + void vprintf(const char *format, va_list) __attribute__((format(printf, 2, 0))); + + protected: + + /** + * Backend function for the output of one character + */ + virtual void _out_char(char c) = 0; + + /** + * Backend function for the output of a null-terminated string + * + * The default implementation uses _out_char. This function may + * be overridden by the backend for improving efficiency. + * + * This function is virtual to enable the use an optimized + * string-output functions on some target platforms, e.g. + * a kernel debugger that offers a string-output syscall. The + * default implementation calls '_out_char' for each character. + */ + virtual void _out_string(const char *str); + + private: + + template void _out_unsigned(T value, unsigned base = 10, int pad = 0); + template void _out_signed(T value, unsigned base = 10); + }; +} + +#endif /* _INCLUDE__BASE__CONSOLE_H_ */ diff --git a/base/include/base/cpu_state.h b/base/include/base/cpu_state.h new file mode 100644 index 000000000..3487ca9ca --- /dev/null +++ b/base/include/base/cpu_state.h @@ -0,0 +1,35 @@ +/* + * \brief CPU state + * \author Christian Prochaska + * \date 2011-04-15 + * + * This file contains the generic part of the CPU state. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CPU_STATE_H_ +#define _INCLUDE__BASE__CPU_STATE_H_ + +#include + +namespace Genode { + + struct Cpu_state + { + addr_t ip; /* instruction pointer */ + addr_t sp; /* stack pointer */ + + /** + * Constructor + */ + Cpu_state(): ip(0), sp(0) { } + }; +} + +#endif /* _INCLUDE__BASE__CPU_STATE_H_ */ diff --git a/base/include/base/crt0.h b/base/include/base/crt0.h new file mode 100644 index 000000000..90a7780c8 --- /dev/null +++ b/base/include/base/crt0.h @@ -0,0 +1,45 @@ +/* + * \brief Startup code and program image specifica + * \author Christian Helmuth + * \date 2006-05-16 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__CRT0_H_ +#define _INCLUDE__BASE__CRT0_H_ + + +/************************************ + ** Program image exported symbols ** + ************************************/ + +extern unsigned _prog_img_beg; /* begin of program image (link address) */ +extern unsigned _prog_img_end; /* end of program image */ + +extern void (*_ctors_start)(); /* begin of constructor table */ +extern void (*_ctors_end)(); /* end of constructor table */ +extern void (*_dtors_start)(); /* begin of destructor table */ +extern void (*_dtors_end)(); /* end of destructor table */ + +extern unsigned _start; /* program entry point */ +extern unsigned _stack_low; /* lower bound of intial stack */ +extern unsigned _stack_high; /* upper bound of intial stack */ + + +/*************************************************** + ** Parameters for parent capability construction ** + ***************************************************/ + +/* + * The protection domain creator initializes the information about + * the parent capability prior the execution of the main thread. + */ +extern unsigned long _parent_cap; + +#endif /* _INCLUDE__BASE__CRT0_H_ */ diff --git a/base/include/base/elf.h b/base/include/base/elf.h new file mode 100644 index 000000000..57f9af478 --- /dev/null +++ b/base/include/base/elf.h @@ -0,0 +1,157 @@ +/* + * \brief ELF binary utility + * \author Christian Helmuth + * \date 2006-05-04 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__ELF_H_ +#define _INCLUDE__BASE__ELF_H_ + +#include + +namespace Genode { + + class Elf_segment; + + class Elf_binary + { + public: + + /** + * Default constructor creates invalid object + */ + Elf_binary() : _valid(false) { } + + /** + * Constructor + * + * The object is only useful if valid() returns true. + */ + explicit Elf_binary(addr_t start); + + /* special types */ + + struct Flags { + unsigned r:1; + unsigned w:1; + unsigned x:1; + unsigned skip:1; + }; + + /** + * Read information about program segments + * + * \return properties of the specified program segment + */ + Elf_segment get_segment(unsigned num); + + /** + * Check validity + */ + bool valid() { return _valid; } + + /** + * Check for dynamic elf + */ + bool is_dynamically_linked() { return (_dynamic && _interp); } + + + /************************ + ** Accessor functions ** + ************************/ + + addr_t entry() { return valid() ? _entry : 0; } + + private: + + /* validity indicator indicates if the loaded ELF is valid and supported */ + bool _valid; + + /* dynamically linked */ + bool _dynamic; + + /* dynamic linker name matches 'genode' */ + bool _interp; + + /* ELF start pointer in memory */ + addr_t _start; + + /* ELF entry point */ + addr_t _entry; + + /* program segments */ + addr_t _ph_table; + size_t _phentsize; + unsigned _phnum; + + + /************ + ** Helper ** + ************/ + + /** + * Check ELF header compatibility + */ + int _ehdr_check_compat(); + + /** + * Check program header compatibility + */ + int _ph_table_check_compat(); + + /** + * Check for dynamic program segments + */ + bool _dynamic_check_compat(unsigned type); + }; + + + class Elf_segment + { + public: + + /** + * Standard constructor creates invalid object + */ + Elf_segment() : _valid(false) { } + + Elf_segment(const Elf_binary *elf, void *start, size_t file_offset, + size_t file_size, size_t mem_size, Elf_binary::Flags flags) + : _elf(elf), _start((unsigned char *)start), _file_offset(file_offset), + _file_size(file_size), _mem_size(mem_size), _flags(flags) + { + _valid = elf ? true : false; + } + + const Elf_binary * elf() { return _elf; } + void * start() { return (void *)_start; } + size_t file_offset() { return _file_offset; } + size_t file_size() { return _file_size; } + size_t mem_size() { return _mem_size; } + Elf_binary::Flags flags() { return _flags; } + + /** + * Check validity + */ + bool valid() { return _valid; } + + private: + + const Elf_binary *_elf; + bool _valid; /* validity indicator */ + unsigned char *_start; + size_t _file_offset; + size_t _file_size; + size_t _mem_size; + Elf_binary::Flags _flags; + }; +} + +#endif /* _INCLUDE__BASE__ELF_H_ */ diff --git a/base/include/base/env.h b/base/include/base/env.h new file mode 100644 index 000000000..022d5c125 --- /dev/null +++ b/base/include/base/env.h @@ -0,0 +1,88 @@ +/* + * \brief Environment of a process + * \author Norman Feske + * \date 2006-07-01 + * + * The environment of a Genode process is defined by its parent and initialized + * on startup. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__ENV_H_ +#define _INCLUDE__BASE__ENV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + class Env + { + public: + + virtual ~Env() { } + + /** + * Communication channel to our parent + */ + virtual Parent *parent() = 0; + + /** + * RAM session for the program + * + * The RAM Session represents a quota of memory that is + * available to the program. Quota can be used to allocate + * RAM-Dataspaces. + */ + virtual Ram_session *ram_session() = 0; + virtual Ram_session_capability ram_session_cap() = 0; + + /** + * CPU session for the program + * + * This session is used to create threads. + */ + virtual Cpu_session *cpu_session() = 0; + + /** + * Region manager session of the program + */ + virtual Rm_session *rm_session() = 0; + + /** + * Pd session of the program + */ + virtual Pd_session *pd_session() = 0; + + /** + * Heap backed by the ram_session of the + * environment. + */ + virtual Allocator *heap() = 0; + }; + + extern Env *env(); + + /** + * Return parent capability + * + * Platforms have to implement this function for environment + * initialization. + */ + Parent_capability parent_cap(); +} + +#endif /* _INCLUDE__BASE__ENV_H_ */ diff --git a/base/include/base/errno.h b/base/include/base/errno.h new file mode 100644 index 000000000..4e3136d15 --- /dev/null +++ b/base/include/base/errno.h @@ -0,0 +1,22 @@ +/* + * \brief Genode error codes + * \author Norman Feske + * \date 2006-04-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__ERRNO_H_ +#define _INCLUDE__BASE__ERRNO_H_ + +namespace Genode { + + enum { ERR_INVALID_OBJECT = -70000, }; +} + +#endif /* _INCLUDE__BASE__ERRNO_H_ */ diff --git a/base/include/base/exception.h b/base/include/base/exception.h new file mode 100644 index 000000000..18937ffc6 --- /dev/null +++ b/base/include/base/exception.h @@ -0,0 +1,19 @@ +/* + * \brief Exception base class + * \author Norman Feske + * \date 2008-03-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__EXCEPTION_H_ +#define _INCLUDE__BASE__EXCEPTION_H_ + +namespace Genode { class Exception { }; } + +#endif /* _INCLUDE__BASE__EXCEPTION_H_ */ diff --git a/base/include/base/heap.h b/base/include/base/heap.h new file mode 100644 index 000000000..b4b08f53c --- /dev/null +++ b/base/include/base/heap.h @@ -0,0 +1,182 @@ +/* + * \brief Heap partition + * \author Norman Feske + * \date 2006-05-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__HEAP_H_ +#define _INCLUDE__BASE__HEAP_H_ + +#include +#include +#include +#include +#include + +namespace Genode { + + /** + * Heap that uses dataspaces as backing store + * + * The heap class provides an allocator that uses a list of dataspaces of a ram + * session as backing store. One dataspace may be used for holding multiple blocks. + */ + class Heap : public Allocator + { + private: + + enum { + MIN_CHUNK_SIZE = 4*1024, /* in machine words */ + MAX_CHUNK_SIZE = 1024*1024 + }; + + class Dataspace : public List::Element + { + public: + + Ram_dataspace_capability cap; + void *local_addr; + }; + + class Dataspace_pool : public List + { + private: + + Ram_session *_ram_session; /* ram session for backing store */ + Rm_session *_rm_session; /* region manager */ + + public: + + /** + * Constructor + */ + Dataspace_pool(Ram_session *ram_session, Rm_session *rm_session): + _ram_session(ram_session), _rm_session(rm_session) { } + + /** + * Destructor + */ + ~Dataspace_pool(); + + /** + * Expand dataspace by specified size + * + * \param size number of bytes to add to the dataspace pool + * \param md_alloc allocator to expand. This allocator is also + * used for meta data allocation (only after + * being successfully expanded). + * \throw Rm_session::Invalid_dataspace, + * Rm_session::Region_conflict + * \return 0 on success or negative error code + */ + int expand(size_t size, Range_allocator *alloc); + }; + + /* + * NOTE: The order of the member variables is important for + * the calling order of the destructors! + */ + + Lock _lock; + Dataspace_pool _ds_pool; /* list of dataspaces */ + Allocator_avl _alloc; /* local allocator */ + size_t _quota_limit; + size_t _quota_used; + size_t _chunk_size; + + /** + * Try to allocate block at our local allocator + * + * \return true on success + * + * This function is a utility used by 'alloc' to avoid + * code duplication. + */ + bool _try_local_alloc(size_t size, void **out_addr); + + public: + + enum { UNLIMITED = ~0 }; + + Heap(Ram_session *ram_session, + Rm_session *rm_session, + size_t quota_limit = UNLIMITED, + void *static_addr = 0, + size_t static_size = 0) + : + _ds_pool(ram_session, rm_session), + _alloc(0), + _quota_limit(quota_limit), _quota_used(0), + _chunk_size(MIN_CHUNK_SIZE) + { + if (static_addr) + _alloc.add_range((addr_t)static_addr, static_size); + } + + /** + * Reconfigure quota limit + * + * \return negative error code if new quota limit is higher than + * currently used quota. + */ + int quota_limit(size_t new_quota_limit); + + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t, void **); + void free(void *, size_t); + size_t consumed() { return _quota_used; } + size_t overhead(size_t size) { return _alloc.overhead(size); } + }; + + + /** + * Heap that allocates each block at a separate dataspace + */ + class Sliced_heap : public Allocator + { + private: + + class Block; + + Ram_session *_ram_session; /* ram session for backing store */ + Rm_session *_rm_session; /* region manager */ + size_t _consumed; /* number of allocated bytes */ + List _block_list; /* list of allocated blocks */ + Lock _lock; /* serialize allocations */ + + public: + + /** + * Constructor + */ + Sliced_heap(Ram_session *ram_session, Rm_session *rm_session); + + /** + * Destructor + */ + ~Sliced_heap(); + + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t, void **); + void free(void *, size_t); + size_t consumed() { return _consumed; } + size_t overhead(size_t size); + }; +} + +#endif /* _INCLUDE__BASE__HEAP_H_ */ diff --git a/base/include/base/ipc.h b/base/include/base/ipc.h new file mode 100644 index 000000000..fb649ba3b --- /dev/null +++ b/base/include/base/ipc.h @@ -0,0 +1,41 @@ +/* + * \brief Generic IPC infrastructure + * \author Norman Feske + * \date 2009-10-02 + * + * This file is used for platforms that only use the generic IPC API. A platform + * may extend the generic API with platform-specific marshalling operators by + * providing a custom version of 'ipc.h' in its 'base-' repository. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_H_ +#define _INCLUDE__BASE__IPC_H_ + +#include + +/** + * Marshalling of capabilities as plain data representation + */ +inline void +Genode::Ipc_ostream::_marshal_capability(Genode::Native_capability const &cap) +{ + _write_to_buf(cap); +} + +/** + * Unmarshalling of capabilities as plain data representation + */ +inline void +Genode::Ipc_istream::_unmarshal_capability(Genode::Native_capability &cap) +{ + _read_from_buf(cap); +} + +#endif /* _INCLUDE__BASE__IPC_H_ */ diff --git a/base/include/base/ipc_generic.h b/base/include/base/ipc_generic.h new file mode 100644 index 000000000..9dadca915 --- /dev/null +++ b/base/include/base/ipc_generic.h @@ -0,0 +1,624 @@ +/* + * \brief Generic IPC infrastructure + * \author Norman Feske + * \date 2006-06-12 + * + * Most of the marshalling and unmarshallung code is generic for IPC + * implementations among different platforms. In addition to the generic + * marshalling items, platform-specific marshalling items can be realized + * via specialized stream operators defined in the platform-specific + * 'base/ipc.h'. Hence, this header file is never to be included directly. + * It should only be included by a platform-specific 'base/ipc.h' file. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__IPC_GENERIC_H_ +#define _INCLUDE__BASE__IPC_GENERIC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + enum Ipc_ostream_send { IPC_SEND }; + enum Ipc_istream_wait { IPC_WAIT }; + enum Ipc_client_call { IPC_CALL }; + enum Ipc_server_reply { IPC_REPLY }; + enum Ipc_server_reply_wait { IPC_REPLY_WAIT }; + + + /********************* + ** Exception types ** + *********************/ + + class Ipc_error : public Exception { }; + + + /** + * Marshal arguments into send message buffer + */ + class Ipc_marshaller + { + protected: + + char *_sndbuf; + size_t _sndbuf_size; + unsigned _write_offset; + + protected: + + /** + * Write value to send buffer + */ + template + void _write_to_buf(T const &value) + { + /* check buffer range */ + if (_write_offset + sizeof(T) >= _sndbuf_size) return; + + /* write integer to buffer */ + *reinterpret_cast(&_sndbuf[_write_offset]) = value; + + /* increment write pointer to next dword-aligned value */ + _write_offset += align_natural(sizeof(T)); + } + + /** + * Write bytes to send buffer + */ + void _write_to_buf(char const *src_addr, unsigned num_bytes) + { + /* check buffer range */ + if (_write_offset + num_bytes >= _sndbuf_size) return; + + /* copy buffer */ + memcpy(&_sndbuf[_write_offset], src_addr, num_bytes); + + /* increment write pointer to next dword-aligned value */ + _write_offset += align_natural(num_bytes); + } + + /** + * Write 'Rpc_in_buffer' to send buffer + */ + void _write_buffer_to_buf(Rpc_in_buffer_base const &b) + { + size_t size = b.size(); + _write_to_buf(size); + _write_to_buf(b.base(), size); + } + + /** + * Write array to send buffer + */ + template + void _write_to_buf(T const (&array)[N]) + { + /* check buffer range */ + if (_write_offset + sizeof(array) >= _sndbuf_size) + PERR("send buffer overrun"); + + memcpy(&_sndbuf[_write_offset], array, sizeof(array)); + _write_offset += align_natural(sizeof(array)); + } + + public: + + Ipc_marshaller(char *sndbuf, size_t sndbuf_size) + : _sndbuf(sndbuf), _sndbuf_size(sndbuf_size), _write_offset(0) { } + }; + + + /** + * Unmarshal arguments from receive buffer + */ + class Ipc_unmarshaller + { + protected: + + char *_rcvbuf; + size_t _rcvbuf_size; + unsigned _read_offset; + + protected: + + /** + * Read value of type T from buffer + */ + template + void _read_from_buf(T &value) + { + /* check receive buffer range */ + if (_read_offset + sizeof(T) >= _rcvbuf_size) return; + + /* return value from receive buffer */ + value = *reinterpret_cast(&_rcvbuf[_read_offset]); + + /* increment read pointer to next dword-aligned value */ + _read_offset += align_natural(sizeof(T)); + } + + /** + * Read 'Rpc_in_buffer' from receive buffer + */ + void _read_bytebuf_from_buf(Rpc_in_buffer_base &b) + { + size_t size = 0; + _read_from_buf(size); + b = Rpc_in_buffer_base(0, 0); + + /* + * Check receive buffer range + * + * Note: The addr of the Rpc_in_buffer_base is a null pointer when this + * condition triggers. + */ + if (_read_offset + size >= _rcvbuf_size) { + PERR("message buffer overrun"); + return; + } + + b = Rpc_in_buffer_base(&_rcvbuf[_read_offset], size); + _read_offset += align_natural(size); + } + + /** + * Read array from receive buffer + */ + template + void _read_from_buf(T (&array)[N]) + { + if (_read_offset + sizeof(array) >= _rcvbuf_size) { + PERR("receive buffer overrun"); + return; + } + + memcpy(array, &_rcvbuf[_read_offset], sizeof(array)); + _read_offset += align_natural(sizeof(array)); + } + + /** + * Read long value at specified byte index of receive buffer + */ + long _long_at_idx(int idx) { return *(long *)(&_rcvbuf[idx]); } + + public: + + Ipc_unmarshaller(char *rcvbuf, size_t rcvbuf_size) + : _rcvbuf(rcvbuf), _rcvbuf_size(rcvbuf_size), _read_offset(0) { } + }; + + + /** + * Stream for sending information via a capability to an endpoint + */ + class Ipc_ostream : public Ipc_marshaller + { + protected: + + Msgbuf_base *_snd_msg; /* send message buffer */ + Native_capability _dst; + + /** + * Reset marshaller and write badge at the beginning of the message + */ + void _prepare_next_send(); + + /** + * Send message in _snd_msg to _dst + */ + void _send(); + + /** + * Insert capability to message buffer + */ + void _marshal_capability(Native_capability const &cap); + + public: + + /** + * Constructor + */ + Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg); + + /** + * Return true if Ipc_ostream is ready for send + */ + bool ready_for_send() const { return _dst.valid(); } + + /** + * Insert value into send buffer + */ + template + Ipc_ostream &operator << (T const &value) + { + _write_to_buf(value); + return *this; + } + + /** + * Insert byte buffer to send buffer + */ + Ipc_ostream &operator << (Rpc_in_buffer_base const &b) + { + _write_buffer_to_buf(b); + return *this; + } + + /** + * Insert capability to send buffer + */ + Ipc_ostream &operator << (Native_capability const &cap) + { + _marshal_capability(cap); + return *this; + } + + /** + * Insert typed capability to send buffer + */ + template + Ipc_ostream &operator << (Capability const &typed_cap) + { + _marshal_capability(typed_cap); + return *this; + } + + /** + * Issue the sending of the message buffer + */ + Ipc_ostream &operator << (Ipc_ostream_send) + { + _send(); + return *this; + } + + /** + * Return current 'IPC_SEND' destination + * + * This function is typically needed by a server than sends replies + * in a different order as the incoming calls. + */ + Native_capability dst() const { return _dst; } + + /** + * Set destination for the next 'IPC_SEND' + */ + void dst(Native_capability const &dst) { _dst = dst; } + }; + + + /** + * Stream for receiving information + */ + class Ipc_istream : public Ipc_unmarshaller, public Native_capability + { + private: + + /** + * Prevent 'Ipc_istream' objects from being copied + * + * Copying an 'Ipc_istream' object would result in a duplicated + * (and possibly inconsistent) connection state both the original + * and the copied object. + */ + Ipc_istream(const Ipc_istream &); + + protected: + + Msgbuf_base *_rcv_msg; + Native_connection_state _rcv_cs; + + /** + * Obtain capability from message buffer + */ + void _unmarshal_capability(Native_capability &cap); + + protected: + + /** + * Reset unmarshaller + */ + void _prepare_next_receive(); + + /** + * Wait for incoming message to be received in _rcv_msg + */ + void _wait(); + + public: + + explicit Ipc_istream(Msgbuf_base *rcv_msg); + + ~Ipc_istream(); + + /** + * Read badge that was supplied with the message + */ + long badge() { return _long_at_idx(0); } + + /** + * Block for an incoming message filling the receive buffer + */ + Ipc_istream &operator >> (Ipc_istream_wait) + { + _wait(); + return *this; + } + + /** + * Read values from receive buffer + */ + template + Ipc_istream &operator >> (T &value) + { + _read_from_buf(value); + return *this; + } + + /** + * Read byte buffer from receive buffer + */ + Ipc_istream &operator >> (Rpc_in_buffer_base &b) + { + _read_bytebuf_from_buf(b); + return *this; + } + + /** + * Read byte buffer from receive buffer + */ + template + Ipc_istream &operator >> (Rpc_in_buffer &b) + { + _read_bytebuf_from_buf(b); + return *this; + } + + /** + * Read capability from receive buffer + */ + Ipc_istream &operator >> (Native_capability &cap) + { + _unmarshal_capability(cap); + return *this; + } + + /** + * Read typed capability from receive buffer + */ + template + Ipc_istream &operator >> (Capability &typed_cap) + { + _unmarshal_capability(typed_cap); + return *this; + } + }; + + + class Ipc_client: public Ipc_istream, public Ipc_ostream + { + protected: + + int _result; /* result of most recent call */ + + void _prepare_next_call(); + + /** + * Send RPC message and wait for result + */ + void _call(); + + public: + + /** + * Constructor + */ + Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg, + Msgbuf_base *rcv_msg); + + /** + * Operator that issues an IPC call + * + * \throw Ipc_error + * \throw Blocking_canceled + */ + Ipc_client &operator << (Ipc_client_call) + { + _call(); + _read_from_buf(_result); + if (_result == ERR_INVALID_OBJECT) { + PERR("tried to call an invalid object"); + throw Ipc_error(); + } + return *this; + } + + template + Ipc_client &operator << (T const &value) + { + _write_to_buf(value); + return *this; + } + + Ipc_client &operator << (Rpc_in_buffer_base const &b) + { + _write_buffer_to_buf(b); + return *this; + } + + template + Ipc_client &operator << (Rpc_in_buffer const &b) + { + _write_buffer_to_buf(b); + return *this; + } + + Ipc_client &operator << (Native_capability const &cap) + { + _marshal_capability(cap); + return *this; + } + + template + Ipc_client &operator << (Capability const &typed_cap) + { + _marshal_capability(typed_cap); + return *this; + } + + Ipc_client &operator >> (Native_capability &cap) + { + _unmarshal_capability(cap); + return *this; + } + + template + Ipc_client &operator >> (Capability &typed_cap) + { + _unmarshal_capability(typed_cap); + return *this; + } + + template + Ipc_client &operator >> (T &value) + { + _read_from_buf(value); + return *this; + } + + Ipc_client &operator >> (Rpc_in_buffer_base &b) + { + _read_bytebuf_from_buf(b); + return *this; + } + + int result() const { return _result; } + }; + + + class Ipc_server : public Ipc_istream, public Ipc_ostream + { + protected: + + bool _reply_needed; /* false for the first reply_wait */ + + void _prepare_next_reply_wait(); + + /** + * Wait for incoming call + * + * In constrast to 'Ipc_istream::_wait()', this function stores the + * next reply destination from into 'dst' of the 'Ipc_ostream'. + */ + void _wait(); + + /** + * Send reply to destination + * + * In contrast to 'Ipc_ostream::_send()', this function prepares + * the 'Ipc_server' to send another subsequent reply without the + * calling '_wait()' in between. This is needed when a server + * answers calls out of order. + */ + void _reply(); + + /** + * Send result of previous RPC request and wait for new one + */ + void _reply_wait(); + + public: + + /** + * Constructor + */ + Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg); + + /** + * Set return value of server call + */ + void ret(int retval) + { + *reinterpret_cast(&_sndbuf[sizeof(umword_t)]) = retval; + } + + /** + * Set reply destination + */ + void dst(Native_capability const &reply_dst) + { + Ipc_ostream::dst(reply_dst); + _reply_needed = reply_dst.valid(); + } + + using Ipc_ostream::dst; + + /** + * Block for an incoming message filling the receive buffer + */ + Ipc_server &operator >> (Ipc_istream_wait) + { + _wait(); + return *this; + } + + /** + * Issue the sending of the message buffer + */ + Ipc_server &operator << (Ipc_server_reply) + { + _reply(); + return *this; + } + + /** + * Reply current request and wait for a new one + */ + Ipc_server &operator >> (Ipc_server_reply_wait) + { + _reply_wait(); + return *this; + } + + /** + * Write value to send buffer + * + * This operator is only used by test programs + */ + template + Ipc_server &operator << (T const &value) + { + _write_to_buf(value); + return *this; + } + + /** + * Read value from receive buffer + * + * This operator should only be used by the server framework for + * reading the function offset. The server-side processing of the + * payload is done using 'Ipc_istream' and 'Ipc_ostream'. + */ + template + Ipc_server &operator >> (T &value) + { + _read_from_buf(value); + return *this; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_GENERIC_H_ */ diff --git a/base/include/base/lock.h b/base/include/base/lock.h new file mode 100644 index 000000000..ce674e20a --- /dev/null +++ b/base/include/base/lock.h @@ -0,0 +1,47 @@ +/* + * \brief Locking primitives + * \author Norman Feske + * \date 2006-07-26 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__LOCK_H_ +#define _INCLUDE__BASE__LOCK_H_ + +#include + +namespace Genode { + + class Lock : public Cancelable_lock + { + public: + + /** + * Constructor + */ + explicit Lock(State initial = UNLOCKED) + : Cancelable_lock(initial) { } + + void lock() + { + while (1) + try { + Cancelable_lock::lock(); + return; + } catch (Blocking_canceled) { } + } + + /** + * Lock guard + */ + typedef Lock_guard Guard; + }; +} + +#endif /* _INCLUDE__BASE__LOCK_H_ */ diff --git a/base/include/base/lock_guard.h b/base/include/base/lock_guard.h new file mode 100644 index 000000000..126e2eea4 --- /dev/null +++ b/base/include/base/lock_guard.h @@ -0,0 +1,46 @@ +/* + * \brief Lock guard + * \author Norman Feske + * \date 2006-07-26 + * + * A lock guard is instantiated as a local variable. + * When a lock guard is constructed, it acquires the lock that + * is specified as constructor argument. When the control + * flow leaves the scope of the lock guard variable via + * a return statement or an exception, the lock guard's + * destructor gets called, freeing the lock. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__LOCK_GUARD_H_ +#define _INCLUDE__BASE__LOCK_GUARD_H_ + +namespace Genode { + + /** + * Lock guard template + * + * \param LT lock type + */ + template + class Lock_guard + { + private: + + LT &_lock; + + public: + + explicit Lock_guard(LT &lock) : _lock(lock) { _lock.lock(); } + + ~Lock_guard() { _lock.unlock(); } + }; +} + +#endif /* _INCLUDE__BASE__LOCK_GUARD_H_ */ diff --git a/base/include/base/object_pool.h b/base/include/base/object_pool.h new file mode 100644 index 000000000..dcb490c0b --- /dev/null +++ b/base/include/base/object_pool.h @@ -0,0 +1,130 @@ +/* + * \brief Object pool - map ids to objects + * \author Norman Feske + * \date 2006-06-26 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__OBJECT_POOL_H_ +#define _INCLUDE__BASE__OBJECT_POOL_H_ + +#include +#include +#include + +namespace Genode { + + /** + * Map object ids to local objects + * + * \param OBJ_TYPE object type (must be inherited from Object_pool::Entry) + * + * The local names of a capabilities are used to differentiate multiple server + * objects managed by one and the same object pool. + */ + template + class Object_pool + { + public: + + class Entry : public Avl_node + { + private: + + Untyped_capability _cap; + + inline long _obj_id() { return _cap.local_name(); } + + friend class Object_pool; + friend class Avl_tree; + + public: + + enum { OBJ_ID_INVALID = 0 }; + + /** + * Constructors + */ + Entry() { } + Entry(Untyped_capability cap) : _cap(cap) { } + + /** + * Avl_node interface + */ + bool higher(Entry *e) { return e->_obj_id() > _obj_id(); } + void recompute() { } /* for gcc-3.4 compatibility */ + + /** + * Support for object pool + */ + Entry *find_by_obj_id(long obj_id) + { + if (obj_id == _obj_id()) return this; + + Entry *obj = child(obj_id > _obj_id()); + + return obj ? obj->find_by_obj_id(obj_id) : 0; + } + + /** + * Assign capability to object pool entry + */ + void cap(Untyped_capability c) { _cap = c; } + + Untyped_capability const cap() const { return _cap; } + }; + + private: + + Avl_tree _tree; + Lock _lock; + + public: + + void insert(OBJ_TYPE *obj) + { + Lock::Guard lock_guard(_lock); + _tree.insert(obj); + } + + void remove(OBJ_TYPE *obj) + { + Lock::Guard lock_guard(_lock); + _tree.remove(obj); + } + + /** + * Lookup object + */ + OBJ_TYPE *obj_by_id(long obj_id) + { + Lock::Guard lock_guard(_lock); + Entry *obj = _tree.first(); + return (OBJ_TYPE *)(obj ? obj->find_by_obj_id(obj_id) : 0); + } + + OBJ_TYPE *obj_by_cap(Untyped_capability cap) + { + return obj_by_id(cap.local_name()); + } + + /** + * Return first element of tree + * + * This function is used for removing tree elements step by step. + */ + OBJ_TYPE *first() + { + Lock::Guard lock_guard(_lock); + return (OBJ_TYPE *)_tree.first(); + } + }; +} + +#endif /* _INCLUDE__BASE__OBJECT_POOL_H_ */ diff --git a/base/include/base/pager.h b/base/include/base/pager.h new file mode 100644 index 000000000..e889c5f61 --- /dev/null +++ b/base/include/base/pager.h @@ -0,0 +1,199 @@ +/* + * \brief Paging-server framework + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-04-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__PAGER_H_ +#define _INCLUDE__BASE__PAGER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + /** + * Special server object for paging + * + * A 'Pager_object' is very similar to a 'Rpc_object'. It is just a + * special implementation for page-fault handling, which does not allow to + * define a "badge" for pager capabilities. + */ + class Pager_object : public Object_pool::Entry + { + protected: + + /** + * Local name for this pager object + */ + unsigned long _badge; + + /** + * User-level signal handler registered for this pager object via + * 'Cpu_session::exception_handler()'. + */ + Signal_context_capability _exception_sigh; + + public: + + /** + * Contains information about exception state of corresponding thread. + */ + Thread_state state; + + Pager_object(unsigned long badge) : _badge(badge) { } + virtual ~Pager_object() { } + + unsigned long badge() const { return _badge; } + + /** + * Interface to be implemented by a derived class + * + * \param ps 'Ipc_pager' stream + * + * Returns !0 on error and pagefault will not be answered. + */ + virtual int pager(Ipc_pager &ps) = 0; + + void wake_up() + { + /* notify pager to wake up faulter */ + Msgbuf<16> snd, rcv; + Native_capability pager = cap(); + Ipc_client ipc_client(pager, &snd, &rcv); + ipc_client << this << IPC_CALL; + } + + /** + * Assign user-level exception handler for the pager object + */ + void exception_handler(Signal_context_capability sigh) + { + _exception_sigh = sigh; + } + + /** + * Notify exception handler about the occurrence of an exception + */ + void submit_exception_signal() + { + if (!_exception_sigh.valid()) return; + + Signal_transmitter transmitter(_exception_sigh); + transmitter.submit(); + } + }; + + /** + * A 'Pager_activation' processes one page fault of a 'Pager_object' at a time. + */ + class Pager_entrypoint; + class Pager_activation_base: public Thread_base + { + private: + + Native_capability _cap; + Pager_entrypoint *_ep; /* entry point to which the + activation belongs */ + /** + * Lock used for blocking until '_cap' is initialized + */ + Lock _cap_valid; + + public: + + Pager_activation_base(const char *name, size_t stack_size) : + Thread_base(name, stack_size), + _cap(Native_capability()), _ep(0), _cap_valid(Lock::LOCKED) { } + + /** + * Set entry point, which the activation serves + * + * This function is only called by the 'Pager_entrypoint' + * constructor. + */ + void ep(Pager_entrypoint *ep) { _ep = ep; } + + /** + * Thread interface + */ + void entry(); + + /** + * Return capability to this activation + * + * This function should only be called from 'Pager_entrypoint' + */ + Native_capability cap() + { + /* ensure that the initialization of our 'Ipc_pager' is done */ + if (!_cap.valid()) + _cap_valid.lock(); + return _cap; + } + }; + + + /** + * Paging entry point + * + * For a paging entry point can hold only one activation. So, paging is + * strictly serialized for one entry point. + */ + class Pager_entrypoint : public Object_pool + { + private: + + Pager_activation_base *_activation; + Cap_session *_cap_session; + + public: + + /** + * Constructor + * + * \param cap_session Cap_session for creating capabilities + * for the pager objects managed by this + * entry point + * \param a initial activation + */ + Pager_entrypoint(Cap_session *cap_session, Pager_activation_base *a = 0); + + /** + * Associate Pager_object with the entry point + */ + Pager_capability manage(Pager_object *obj); + + /** + * Dissolve Pager_object from entry point + */ + void dissolve(Pager_object *obj); + }; + + + template + class Pager_activation : public Pager_activation_base + { + public: + + Pager_activation() : Pager_activation_base("pager", STACK_SIZE) + { start(); } + }; +} + +#endif /* _INCLUDE__BASE__PAGER_H_ */ diff --git a/base/include/base/platform_env.h b/base/include/base/platform_env.h new file mode 100644 index 000000000..6fb1b6107 --- /dev/null +++ b/base/include/base/platform_env.h @@ -0,0 +1,150 @@ +/* + * \brief Platform environment of Genode process + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-28 + * + * This file is a generic variant of the platform environment, which is + * suitable for platforms such as L4ka::Pistachio and L4/Fiasco. On other + * platforms, it may be replaced by a platform-specific version residing + * in the corresponding 'base-' repository. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__PLATFORM_ENV_H_ +#define _INCLUDE__BASE__PLATFORM_ENV_H_ + +#include + +#include +#include +#include +#include +#include +#include + + +namespace Genode { + + class Platform_env : public Env + { + class Expanding_rm_session_client : public Rm_session_client + { + Rm_session_capability _cap; + + public: + + Expanding_rm_session_client(Rm_session_capability cap) + : Rm_session_client(cap), _cap(cap) { } + + Local_addr attach(Dataspace_capability ds, + size_t size = 0, off_t offset = 0, + bool use_local_addr = false, + Local_addr local_addr = (addr_t)0) { + + bool try_again; + do { + try_again = false; + try { + return Rm_session_client::attach(ds, size, offset, + use_local_addr, + local_addr); + + } catch (Rm_session::Out_of_metadata) { + + /* give up if the error occurred a second time */ + if (try_again) + break; + + PINF("upgrade quota donation for Env::RM session"); + env()->parent()->upgrade(_cap, "ram_quota=8K"); + try_again = true; + } + } while (try_again); + + return (addr_t)0; + } + }; + + class Expanding_ram_session_client : public Ram_session_client + { + Ram_session_capability _cap; + + public: + + Expanding_ram_session_client(Ram_session_capability cap) + : Ram_session_client(cap), _cap(cap) { } + + Ram_dataspace_capability alloc(size_t size) { + bool try_again; + do { + try_again = false; + try { + return Ram_session_client::alloc(size); + + } catch (Ram_session::Out_of_metadata) { + + /* give up if the error occurred a second time */ + if (try_again) + break; + + PINF("upgrade quota donation for Env::RAM session"); + env()->parent()->upgrade(_cap, "ram_quota=8K"); + try_again = true; + } + } while (try_again); + + return Ram_dataspace_capability(); + } + }; + + private: + + Parent_client _parent_client; + Parent *_parent; + Ram_session_capability _ram_session_cap; + Expanding_ram_session_client _ram_session_client; + Cpu_session_client _cpu_session_client; + Expanding_rm_session_client _rm_session_client; + Pd_session_client _pd_session_client; + Heap _heap; + + + public: + + /** + * Standard constructor + */ + Platform_env() + : + _parent_client(Genode::parent_cap()), _parent(&_parent_client), + _ram_session_cap(static_cap_cast(parent()->session("Env::ram_session", ""))), + _ram_session_client(_ram_session_cap), + _cpu_session_client(static_cap_cast(parent()->session("Env::cpu_session", ""))), + _rm_session_client(static_cap_cast(parent()->session("Env::rm_session", ""))), + _pd_session_client(static_cap_cast(parent()->session("Env::pd_session", ""))), + _heap(ram_session(), rm_session()) + { } + + + /******************* + ** Env interface ** + *******************/ + + Parent *parent() { return _parent; } + Ram_session *ram_session() { return &_ram_session_client; } + Ram_session_capability ram_session_cap() { return _ram_session_cap; } + Cpu_session *cpu_session() { return &_cpu_session_client; } + Rm_session *rm_session() { return &_rm_session_client; } + Pd_session *pd_session() { return &_pd_session_client; } + Allocator *heap() { return &_heap; } + }; +} + +#endif /* _INCLUDE__BASE__PLATFORM_ENV_H_ */ diff --git a/base/include/base/printf.h b/base/include/base/printf.h new file mode 100644 index 000000000..c9deaadca --- /dev/null +++ b/base/include/base/printf.h @@ -0,0 +1,105 @@ +/* + * \brief Interface of the printf backend + * \author Norman Feske + * \date 2006-04-08 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__PRINTF_H_ +#define _INCLUDE__BASE__PRINTF_H_ + +#include + +namespace Genode { + + /** + * For your convenience... + */ + void printf(const char *format, ...) __attribute__((format(printf, 1, 2))); + void vprintf(const char *format, va_list) __attribute__((format(printf, 1, 0))); +} + +#define ESC_LOG "\033[33m" +#define ESC_DBG "\033[33m" +#define ESC_INF "\033[32m" +#define ESC_WRN "\033[34m" +#define ESC_ERR "\033[31m" +#define ESC_END "\033[0m" + +/** + * Remove colored output from release version + */ +#ifdef GENODE_RELEASE +#undef ESC_LOG +#undef ESC_DBG +#undef ESC_INF +#undef ESC_WRN +#undef ESC_ERR +#undef ESC_END +#define ESC_LOG +#define ESC_DBG +#define ESC_INF +#define ESC_WRN +#define ESC_ERR +#define ESC_END +#endif /* GENODE_RELEASE */ + +/* + * We're using heavy CPP wizardry here to prevent compiler errors after macro + * expansion. Each macro works as follows: + * + * - Support one format string plus zero or more arguments. + * - Put all static strings (including the format string) in the first argument + * of the call to printf() and let the compiler merge them. + * - Append the function name (magic static string variable) as first argument. + * - (Optionally) append the arguments to the macro with ", ##__VA_ARGS__". CPP + * only appends the comma and arguments if __VA__ARGS__ is not empty, + * otherwise nothing (not even the comma) is appended. + */ + +/** + * Print debug message with function name + */ +#define PDBG(fmt, ...) \ + Genode::printf("%s: " ESC_DBG fmt ESC_END "\n", \ + __PRETTY_FUNCTION__, ##__VA_ARGS__ ) + +/** + * Suppress debug messages in release version + */ +#ifdef GENODE_RELEASE +#undef PDBG +#define PDBG(fmt, ...) +#endif /* GENODE_RELEASE */ + +/** + * Print log message + */ +#define PLOG(fmt, ...) \ + Genode::printf(ESC_LOG fmt ESC_END "\n", ##__VA_ARGS__ ) + +/** + * Print status-information message + */ +#define PINF(fmt, ...) \ + Genode::printf(ESC_INF fmt ESC_END "\n", ##__VA_ARGS__ ) + +/** + * Print warning message + */ +#define PWRN(fmt, ...) \ + Genode::printf(ESC_WRN fmt ESC_END "\n", ##__VA_ARGS__ ) + +/** + * Print error message + */ +#define PERR(fmt, ...) \ + Genode::printf(ESC_ERR fmt ESC_END "\n", ##__VA_ARGS__ ) + +#endif /* _INCLUDE__BASE__PRINTF_H_ */ diff --git a/base/include/base/process.h b/base/include/base/process.h new file mode 100644 index 000000000..c665aa6c9 --- /dev/null +++ b/base/include/base/process.h @@ -0,0 +1,92 @@ +/* + * \brief Process-creation interface + * \author Norman Feske + * \date 2006-06-22 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__PROCESS_H_ +#define _INCLUDE__BASE__PROCESS_H_ + +#include +#include +#include +#include +#include + +namespace Genode { + + class Process + { + private: + + Pd_connection _pd; + Thread_capability _thread0_cap; + Cpu_session_client _cpu_session_client; + Rm_session_client _rm_session_client; + + static Dataspace_capability _dynamic_linker_cap; + + /* + * Hook for passing additional platform-specific session + * arguments to the PD session. For example, on Linux a new + * process is created locally via 'fork' and the new PID gets + * then communicated to core via a PD-session argument. + */ + enum { PRIV_ARGBUF_LEN = 32 }; + char _priv_pd_argbuf[PRIV_ARGBUF_LEN]; + const char *_priv_pd_args(Parent_capability parent_cap, + Dataspace_capability elf_data_ds, + const char *name, char *const argv[]); + + public: + + /** + * Constructor + * + * \param elf_data_ds dataspace that contains the elf binary + * \param ram_session RAM session providing the BSS for the + * new protection domain + * \param cpu_session CPU session for the new protection domain + * \param rm_session RM session for the new protection domain + * \param parent parent of the new protection domain + * \param name name of protection domain (can be used + * in debugging) + * \param argv not used + * + * The dataspace 'elf_data_ds' can be read-only. + * + * On construction of a protection domain, execution of the initial + * thread is started immediately. + */ + Process(Dataspace_capability elf_data_ds, + Ram_session_capability ram_session, + Cpu_session_capability cpu_session, + Rm_session_capability rm_session, + Parent_capability parent, + const char *name, + char *const argv[]); + + /** + * Destructor + * + * When called, the protection domain gets killed. + */ + ~Process(); + + static void dynamic_linker(Dataspace_capability dynamic_linker_cap) + { + _dynamic_linker_cap = dynamic_linker_cap; + } + + Pd_session_capability pd_session_cap() const { return _pd.cap(); } + }; +} + +#endif /* _INCLUDE__BASE__PROCESS_H_ */ diff --git a/base/include/base/rpc.h b/base/include/base/rpc.h new file mode 100644 index 000000000..eef2c5627 --- /dev/null +++ b/base/include/base/rpc.h @@ -0,0 +1,287 @@ +/* + * \brief Support for defining and working with RPC interfaces + * \author Norman Feske + * \date 2011-03-28 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__RPC_H_ +#define _INCLUDE__BASE__RPC_H_ + +#include + + +/** + * Macro for declaring a RPC function + * + * \param rpc_name type name representing the RPC function + * \param ret_type RPC return type + * \param func_name RPC function name + * \param exc_types type list of exceptions that may be thrown by the + * function + * \param ... variable number of RPC function arguments + * + * Each RPC function is represented by a struct that contains the meta data + * about the function arguments, the return type, and the exception types. + * Furthermore, it contains an adapter function called 'serve', which is used + * on the server side to invoke the server-side implementation of the RPC + * function. It takes an a 'Pod_tuple' argument structure and calls the + * server-side function with individual arguments using the 'call_member' + * mechanism provided by 'meta.h'. + */ +#define GENODE_RPC_THROW(rpc_name, ret_type, func_name, exc_types, ...) \ + struct rpc_name { \ + typedef ::Genode::Meta::Ref_args<__VA_ARGS__>::Type Client_args; \ + typedef ::Genode::Meta::Pod_args<__VA_ARGS__>::Type Server_args; \ + typedef ::Genode::Trait::Exc_list::Type Exceptions; \ + typedef ::Genode::Trait::Call_return::Type Ret_type; \ + \ + template \ + static void serve(SERVER &server, Server_args &args, RET &ret) { \ + ::Genode::Meta::call_member \ + (ret, server, args, &SERVER::func_name); } \ + }; + +/** + * Shortcut for 'GENODE_RPC_THROW' for an RPC that throws no exceptions + */ +#define GENODE_RPC(rpc_name, ret_type, func_name, ...) \ + GENODE_RPC_THROW(rpc_name, ret_type, func_name, GENODE_TYPE_LIST(), __VA_ARGS__) + +/** + * Macro for declaring a RPC interface + * + * \param ... list of RPC functions as declared via 'GENODE_RPC' + * + * An RPC interface is represented as type list of RPC functions. The RPC + * opcode for each function is implicitly defined by its position within + * this type list. + */ +#define GENODE_RPC_INTERFACE(...) \ + typedef GENODE_TYPE_LIST(__VA_ARGS__) Rpc_functions + +/** + * Macro for declaring a RPC interface derived from another RPC interface + * + * \param base class hosting the RPC interface to be inherited + * \param ... list of the locally declared RPC functions + * + * RPC interface inheritance is simply the concatenation of the type list + * of RPC functions declared for the base interface and the locally declared + * RPC functions. By appending the local RPC functions, the RPC opcodes of + * the inherited RPC functions are preserved. + */ +#define GENODE_RPC_INTERFACE_INHERIT(base, ...) \ + typedef ::Genode::Meta::Append::Type \ + Rpc_functions; + + +namespace Genode { + + struct Rpc_arg_in { enum { IN = true, OUT = false }; }; + struct Rpc_arg_out { enum { IN = false, OUT = true }; }; + struct Rpc_arg_inout { enum { IN = true, OUT = true }; }; + + namespace Trait { + + /***************************************** + ** Type meta data used for marshalling ** + *****************************************/ + + template struct Rpc_direction; + + + template struct Rpc_direction { typedef Rpc_arg_in Type; }; + template struct Rpc_direction { typedef Rpc_arg_in Type; }; + template struct Rpc_direction { typedef Rpc_arg_in Type; }; + template struct Rpc_direction { typedef Rpc_arg_inout Type; }; + template struct Rpc_direction { typedef Rpc_arg_inout Type; }; + + /** + * Representation of function return type + * + * For RPC functions with no return value, we use a pseudo return value + * of type 'Empty' instead. This way, we can process all functions + * regardless of the presence of a return type with the same meta + * program. + */ + template struct Call_return { typedef T Type; }; + template <> struct Call_return { typedef Meta::Empty Type; }; + + /** + * Representation of the list of exception types + * + * This template maps the special case of a 'Type_list' with no arguments + * to the 'Empty' type. + */ + template struct Exc_list { typedef T Type; }; + template <> struct Exc_list > { typedef Meta::Empty Type; }; + } + + + /******************************************************* + ** Automated computation of RPC message-buffer sizes ** + *******************************************************/ + + /** + * Determine transfer size of an RPC argument + * + * For data arguments, the transfer size is the size of the data type. For + * pointer arguments, the transfer size is the size of the pointed-to + * object. + */ + template + struct Rpc_transfer_size { + enum { Value = Meta::Round_to_machine_word::Value }; }; + + template + struct Rpc_transfer_size { + enum { Value = Meta::Round_to_machine_word::Value }; }; + + + /** + * Type used for transmitting the opcode of a RPC function (used for RPC call) + */ + typedef int Rpc_opcode; + + + /** + * Type used for transmitting exception information (used for RPC reply) + */ + typedef int Rpc_exception_code; + + + /** + * Special exception code used to respond to illegal opcodes + */ + enum { RPC_INVALID_OPCODE = -1 }; + + + /** + * Opcode base used for passing exception information + */ + enum { RPC_EXCEPTION_BASE = -1000 }; + + + /** + * Return the accumulated size of RPC arguments + * + * \param ARGS typelist with RPC arguments + * \param IN true to account for RPC-input arguments + * \param OUT true to account for RPC-output arguments + */ + template + struct Rpc_args_size { + typedef typename ARGS::Head This; + enum { This_size = Rpc_transfer_size::Value }; + enum { Value = (IN && Trait::Rpc_direction::Type::IN ? This_size : 0) + + (OUT && Trait::Rpc_direction::Type::OUT ? This_size : 0) + + Rpc_args_size::Value }; }; + + template + struct Rpc_args_size { enum { Value = 0 }; }; + + + /** + * Return the size of the return value + * + * The return type of an RPC function can be either a real type or + * 'Meta::Empty' if the function has no return value. In the latter case, + * 'Retval_size' returns 0 instead of the non-zero size of 'Meta::Empty'. + */ + template + struct Rpc_retval_size { enum { Value = sizeof(RET) }; }; + + template <> + struct Rpc_retval_size { enum { Value = 0 }; }; + + + /** + * Calculate the payload size of a RPC message + * + * Setting either IN or OUT to true, the call size or respectively the + * reply size is calculated. Protocol-related message parts (such as RPC + * opcode or exception status) is not accounted for. + */ + template + struct Rpc_msg_payload_size { + typedef typename RPC_FUNCTION::Server_args Args; + enum { Value = Rpc_args_size::Value }; }; + + + /** + * RPC message type + * + * An RPC message can be either a 'RPC_CALL' (from client to server) or a + * 'RPC_REPLY' (from server to client). The message payload for each type + * depends on the RPC function arguments as well as protocol-specific + * message parts. For example, a 'RPC_CALL' requires the transmission of + * the RPC opcode to select the server-side RPC function. In contrast, a + * 'RPC_REPLY' message carries along the exception state returned from the + * server-side RPC implementation. The 'Rpc_msg_type' is used as template + * argument to specialize the calculation of message sizes for each of both + * cases. + */ + enum Rpc_msg_type { RPC_CALL, RPC_REPLY }; + + + /** + * Calculate size of RPC message + * + * The send and receive cases are handled by respective template + * specializations for the 'MSG_TYPE' template argument. + */ + template + struct Rpc_function_msg_size; + + template + struct Rpc_function_msg_size { + enum { Value = Rpc_msg_payload_size::Value + + sizeof(Rpc_opcode) }; }; + + template + struct Rpc_function_msg_size { + enum { Value = Rpc_msg_payload_size::Value + + Rpc_retval_size::Value + + sizeof(Rpc_exception_code) }; }; + + + /** + * Calculate size of message buffer needed for a list of RPC functions + * + * \param RPC_FUNCTIONS type list of RPC functions + * + * The returned 'Value' is the maximum of all function's message sizes. + */ + template + struct Rpc_function_list_msg_size { + enum { + This_size = Rpc_function_msg_size::Value, + Tail_size = Rpc_function_list_msg_size::Value, + Value = (This_size > Tail_size) ? This_size : Tail_size }; }; + + template + struct Rpc_function_list_msg_size { enum { Value = 0 }; }; + + + /** + * Calculate size of message buffer needed for an RPC interface + * + * \param RPC_IF class that hosts the RPC interface declaration + * + * This is a convenience wrapper for 'Rpc_function_list_msg_size'. + */ + template + struct Rpc_interface_msg_size { + typedef typename RPC_IF::Rpc_functions Rpc_functions; + enum { Value = Rpc_function_list_msg_size::Value }; }; +} + +#endif /* _INCLUDE__BASE__RPC_H_ */ diff --git a/base/include/base/rpc_args.h b/base/include/base/rpc_args.h new file mode 100644 index 000000000..be2a5cb3a --- /dev/null +++ b/base/include/base/rpc_args.h @@ -0,0 +1,121 @@ +/* + * \brief Helpers for non-ordinary RPC arguments + * \author Norman Feske + * \date 2011-04-06 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__RPC_ARGS_H_ +#define _INCLUDE__BASE__RPC_ARGS_H_ + +#include +#include + +namespace Genode { + + /** + * Base class of 'Rpc_in_buffer' + */ + class Rpc_in_buffer_base + { + protected: + + const char *_base; + size_t _size; + + /** + * Construct buffer from null-terminated string + */ + explicit Rpc_in_buffer_base(const char *str) + : _base(str), _size(strlen(str) + 1) { } + + /** + * Construct an empty buffer by default + */ + Rpc_in_buffer_base(): _base(0), _size(0) { } + + public: + + /** + * Construct buffer + */ + Rpc_in_buffer_base(const char *base, size_t size) + : _base(base), _size(size) { } + + const char *base() const { return _base; } + size_t size() const { return _size; } + }; + + + /** + * Buffer with size constrain + */ + template + class Rpc_in_buffer : public Rpc_in_buffer_base + { + private: + + /* + * This member is only there to pump up the size of the object such + * that 'sizeof()' returns the maximum buffer size when queried by + * the RPC framework. + */ + char _balloon[MAX]; + + public: + + enum { MAX_SIZE = MAX }; + + /** + * Construct buffer + */ + Rpc_in_buffer(const char *base, size_t size) + : Rpc_in_buffer_base(base, min(size, (size_t)MAX_SIZE)) { } + + /** + * Construct buffer from null-terminated string + */ + Rpc_in_buffer(const char *str) : Rpc_in_buffer_base(str) + { + if (_size >= MAX_SIZE - 1) + _size = MAX_SIZE - 1; + } + + /** + * Default constructor creates invalid buffer + */ + Rpc_in_buffer() { } + + void operator = (Rpc_in_buffer const &from) + { + _base = from.base(); + _size = from.size(); + } + + /** + * Return true if buffer contains a valid null-terminated string + */ + bool is_valid_string() const { + return (_size < MAX_SIZE) && (_size > 0) && (_base[_size - 1] == '\0'); } + + /** + * Return buffer content as null-terminated string + * + * \return pointer to null-terminated string + * + * The function returns an empty string if the buffer does not hold + * a valid null-terminated string. To distinguish a buffer holding + * an invalid string from a buffer holding a valid empty string, + * the function 'is_valid_string' can be used. + */ + char const *string() const { return is_valid_string() ? base() : ""; } + }; +} + +#endif /* _INCLUDE__BASE__RPC_ARGS_H_ */ diff --git a/base/include/base/rpc_client.h b/base/include/base/rpc_client.h new file mode 100644 index 000000000..5083e1dfa --- /dev/null +++ b/base/include/base/rpc_client.h @@ -0,0 +1,135 @@ +/* + * \brief Support for performing RPC calls + * \author Norman Feske + * \date 2011-04-06 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__RPC_CLIENT_H_ +#define _INCLUDE__BASE__RPC_CLIENT_H_ + +#include + +namespace Genode { + + /** + * RPC client + * + * This class template is the base class of the client-side implementation + * of the specified 'RPC_INTERFACE'. Usually, it inherits the pure virtual + * functions declared in 'RPC_INTERFACE' and has the built-in facility to + * perform RPC calls to this particular interface. Hence, the client-side + * implementation of each pure virtual interface function comes down to a + * simple wrapper in the line of 'return call(arguments...)'. + */ + template + struct Rpc_client : Capability, RPC_INTERFACE + { + typedef RPC_INTERFACE Rpc_interface; + + Rpc_client(Capability const &cap) + : Capability(cap) { } + }; + + + /********************************************************* + ** Implementation of 'Capability:call' functions ** + *********************************************************/ + + template + template + void Capability:: + _marshal_args(Ipc_client &ipc_client, ATL &args) + { + if (Trait::Rpc_direction::Type::IN) + ipc_client << args.get(); + + _marshal_args(ipc_client, args._2); + } + + + template + template + void Capability:: + _unmarshal_result(Ipc_client &ipc_client, T &arg, + Meta::Overload_selector) + { + ipc_client >> arg; + } + + + template + template + void Capability:: + _unmarshal_result(Ipc_client &ipc_client, T &arg, + Meta::Overload_selector) + { + _unmarshal_result(ipc_client, arg, Meta::Overload_selector()); + } + + + template + template + void Capability:: + _unmarshal_results(Ipc_client &ipc_client, ATL &args) + { + /* + * Unmarshal current argument. The overload of + * '_unmarshal_result' is selected depending on the RPC + * direction. + */ + typedef typename Trait::Rpc_direction::Type Rpc_dir; + _unmarshal_result(ipc_client, args.get(), Meta::Overload_selector()); + + /* unmarshal remaining arguments */ + _unmarshal_results(ipc_client, args._2); + } + + + template + template + void Capability:: + _call(typename IF::Client_args &args, typename IF::Ret_type &ret) + { + /** + * Message buffer for RPC message + * + * The message buffer gets automatically dimensioned according to the + * specified 'IF' RPC function. + */ + enum { PROTOCOL_OVERHEAD = 4*sizeof(long), + CALL_MSG_SIZE = Rpc_function_msg_size::Value, + REPLY_MSG_SIZE = Rpc_function_msg_size::Value }; + + Msgbuf call_buf; + Msgbuf reply_buf; + + Ipc_client ipc_client(*this, &call_buf, &reply_buf); + + /* determine opcode of RPC function */ + typedef typename RPC_INTERFACE::Rpc_functions Rpc_functions; + Rpc_opcode opcode = static_cast(Meta::Index_of::Value); + + /* marshal opcode and RPC input arguments */ + ipc_client << opcode; + _marshal_args(ipc_client, args); + + /* perform RPC, unmarshal return value */ + ipc_client << IPC_CALL >> ret; + + /* unmarshal RPC output arguments */ + _unmarshal_results(ipc_client, args); + + /* reflect callee-side exception at the caller */ + _check_for_exceptions(ipc_client.result(), + Meta::Overload_selector()); + } +} + +#endif /* _INCLUDE__BASE__RPC_CLIENT_H_ */ diff --git a/base/include/base/rpc_server.h b/base/include/base/rpc_server.h new file mode 100644 index 000000000..2cd165e7d --- /dev/null +++ b/base/include/base/rpc_server.h @@ -0,0 +1,394 @@ +/* + * \brief Server-side API of the RPC framework + * \author Norman Feske + * \date 2006-04-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__RPC_SERVER_H_ +#define _INCLUDE__BASE__RPC_SERVER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + /** + * RPC dispatcher implementing the specified RPC interface + * + * \param RPC_INTERFACE class providing the RPC interface description + * \param SERVER class to invoke for the server-side RPC functions + * + * This class is the base class of each server-side RPC implementation. It + * contains the logic for dispatching incoming RPC requests and calls the + * server functions according to the RPC declarations in 'RPC_INTERFACE'. + * + * If using the default argument for 'SERVER', the 'RPC_INTERFACE' is expected + * to contain the abstract interface for all RPC functions. So virtual functions + * must be declared in 'RPC_INTERFACE'. In contrast, by explicitly specifying + * the 'SERVER' argument, the server-side dispatching performs direct function + * calls to the respective member functions of the 'SERVER' class and thereby + * omits virtual functions calls. + */ + template + class Rpc_dispatcher : public RPC_INTERFACE + { + /** + * Shortcut for the type list of RPC functions provided by this server + * component + */ + typedef typename RPC_INTERFACE::Rpc_functions Rpc_functions; + + protected: + + template + void _read_args(Ipc_istream &is, ARG_LIST &args) + { + if (Trait::Rpc_direction::Type::IN) + is >> args._1; + + _read_args(is, args._2); + } + + void _read_args(Ipc_istream &is, Meta::Empty) { } + + template + void _write_results(Ipc_ostream &os, ARG_LIST &args) + { + if (Trait::Rpc_direction::Type::OUT) + os << args._1; + + _write_results(os, args._2); + } + + void _write_results(Ipc_ostream &os, Meta::Empty) { } + + template + Rpc_exception_code _do_serve(typename RPC_FUNCTION::Server_args &args, + typename RPC_FUNCTION::Ret_type &ret, + Meta::Overload_selector) + { + enum { EXCEPTION_CODE = RPC_EXCEPTION_BASE - Meta::Length::Value }; + try { + typedef typename EXC_TL::Tail Exc_tail; + return _do_serve(args, ret, + Meta::Overload_selector()); + } catch (typename EXC_TL::Head) { return EXCEPTION_CODE; } + } + + template + Rpc_exception_code _do_serve(typename RPC_FUNCTION::Server_args &args, + typename RPC_FUNCTION::Ret_type &ret, + Meta::Overload_selector) + { + RPC_FUNCTION::serve(*static_cast(this), args, ret); + return 0; + } + + template + Rpc_exception_code _do_dispatch(Rpc_opcode opcode, Ipc_istream &is, Ipc_ostream &os, + Meta::Overload_selector) + { + using namespace Meta; + + typedef typename RPC_FUNCTIONS_TO_CHECK::Head This_rpc_function; + + if (opcode == Index_of::Value) { + + /* + * Argument receive buffer + * + * To prevent the compiler from complaining about the + * 'Server_args' data structure from being uninitialized, + * we instantiate the variable as volatile and strip away + * the volatile-ness when using it. + */ + struct { + typedef typename This_rpc_function::Server_args Data; + volatile Data _data; + Data &data() { return *(Data *)(&_data); } + } args; + + /* read arguments from istream */ + _read_args(is, args.data()); + + /* + * Dispatch call to matching RPC base class, using + * 'This_rpc_function' and the list of its exceptions to + * select the overload. + */ + typedef typename This_rpc_function::Exceptions Exceptions; + + typename This_rpc_function::Ret_type ret; + Rpc_exception_code exc; + exc = _do_serve(args.data(), ret, Overload_selector()); + os << ret; + + /* write results to ostream 'os' */ + _write_results(os, args.data()); + + return exc; + } + + typedef typename RPC_FUNCTIONS_TO_CHECK::Tail Tail; + return _do_dispatch(opcode, is, os, Overload_selector()); + } + + int _do_dispatch(int opcode, Ipc_istream &, Ipc_ostream &, + Meta::Overload_selector) + { + PERR("invalid opcode %d\n", opcode); + return RPC_INVALID_OPCODE; + } + + /** + * Handle corner case of having an RPC interface with no RPC functions + */ + Rpc_exception_code _do_dispatch(int opcode, Ipc_istream &, Ipc_ostream &, + Meta::Overload_selector >) + { + return 0; + } + + /** + * Protected constructor + * + * This class is only usable as base class. + */ + Rpc_dispatcher() { } + + public: + + Rpc_exception_code dispatch(int opcode, Ipc_istream &is, Ipc_ostream &os) + { + return _do_dispatch(opcode, is, os, + Meta::Overload_selector()); + } + }; + + + class Rpc_object_base : public Object_pool::Entry + { + private: + + Lock _dispatch_lock; + + public: + + virtual ~Rpc_object_base() { } + + /* + * Serialize access with dispatch loop + * + * These methods are used for the destruction of server objects. + * They are exclusively used by 'Server_activation_base::entry()' + * and 'Rpc_entrypoint::dissolve()'. Never use this lock for other + * purposes. + */ + + void lock() { _dispatch_lock.lock(); } + void unlock() { _dispatch_lock.unlock(); } + + /** + * Interface to be implemented by a derived class + * + * \param op opcode of invoked method + * \param is Ipc_input stream with method arguments + * \param os Ipc_output stream for storing method results + */ + virtual int dispatch(int op, Ipc_istream &is, Ipc_ostream &os) = 0; + }; + + + /** + * Object that is accessible from remote protection domains + * + * A 'Rpc_object' is a locally implemented object that can be referenced + * from the outer world using a capability. The capability gets created + * when attaching a 'Rpc_object' to a 'Rpc_entrypoint'. + */ + template + struct Rpc_object : Rpc_object_base, Rpc_dispatcher + { + /***************************** + ** Server-object interface ** + *****************************/ + + Rpc_exception_code dispatch(int opcode, Ipc_istream &is, Ipc_ostream &os) + { + return Rpc_dispatcher::dispatch(opcode, is, os); + } + + Capability const cap() const + { + return reinterpret_cap_cast(Rpc_object_base::cap()); + } + }; + + + /** + * RPC entrypoint serving RPC objects + * + * The entrypoint's thread will initialize its capability but will not + * immediately enable the processing of requests. This way, the + * activation-using server can ensure that it gets initialized completely + * before the first capability invocations come in. Once the server is + * ready, it must enable the entrypoint explicitly by calling the + * 'activate()' function. The 'start_on_construction' argument is a + * shortcut for the common case where the server's capability is handed + * over to other parties _after_ the server is completely initialized. + */ + class Rpc_entrypoint : Thread_base, public Object_pool + { + private: + + /** + * Prototype capability to derive capabilities for RPC objects + * from. + */ + Untyped_capability _cap; + + enum { SND_BUF_SIZE = 1024, RCV_BUF_SIZE = 1024 }; + Msgbuf _snd_buf; + Msgbuf _rcv_buf; + + /** + * Hook to let low-level thread init code access private members + * + * This function is only used on NOVA. + */ + static void _activation_entry(); + + protected: + + Ipc_server *_ipc_server; + Rpc_object_base *_curr_obj; /* currently dispatched RPC object */ + Lock _curr_obj_lock; /* for the protection of '_curr_obj' */ + Lock _cap_valid; /* thread startup synchronization */ + Lock _delay_start; /* delay start of request dispatching */ + Cap_session *_cap_session; /* for creating capabilities */ + + /** + * Back-end function to associate RPC object with the entry point + */ + Untyped_capability _manage(Rpc_object_base *obj); + + /** + * Back-end function to Dissolve RPC object from entry point + */ + void _dissolve(Rpc_object_base *obj); + + /** + * Force activation to cancel dispatching the specified server object + */ + void _leave_server_object(Rpc_object_base *obj); + + /** + * Wait until the entrypoint activation is initialized + */ + void _block_until_cap_valid(); + + /** + * Thread interface + */ + void entry(); + + public: + + /** + * Constructor + * + * \param cap_session 'Cap_session' for creating capabilities + * for the RPC objects managed by this entry + * point + * \param stack_size stack size of entrypoint thread + * \param name name of entrypoint thread + */ + Rpc_entrypoint(Cap_session *cap_session, size_t stack_size, + char const *name, bool start_on_construction = true); + + /** + * Associate RPC object with the entry point + */ + template + Capability + manage(Rpc_object *obj) + { + Untyped_capability untyped_cap = _manage(obj); + + /* + * Turn untyped capability returned by '_entrypoint.manage()' + * to a capability with the type corresponding to the supplied + * RPC object. + */ + Capability typed_cap; + memcpy(&typed_cap, &untyped_cap, sizeof(typed_cap)); + return typed_cap; + } + + /** + * Dissolve server object from entry point + */ + template + void dissolve(Rpc_object *obj) + { + _dissolve(obj); + } + + /** + * Activate entrypoint, start processing RPC requests + */ + void activate(); + + /** + * Request reply capability for current call + * + * Note: This is a temporary API function, which is going to be + * removed. Please do not use this function. + * + * Typically, a capability obtained via this function is used as + * argument of 'intermediate_reply'. + */ + Untyped_capability reply_dst(); + + /** + * Prevent reply of current request + * + * Note: This is a temporary API function, which is going to be + * removed. Please do not use this function. + * + * This function can be used to keep the calling client blocked + * after the server has finished the processing of the client's + * request. At a later time, the server may chose to unblock the + * client via the 'intermedate_reply' function. + */ + void omit_reply(); + + /** + * Send a reply out of the normal call-reply order + * + * Note: This is a temporary API function, which is going to be + * removed. Please do not use this function. + * + * In combination with the 'reply_dst' accessor functions, this + * function can be used to implement services that dispatch client + * requests out of order. In such cases, the server activation may + * send reply messages to multiple blocking clients before + * answering the original call. + */ + void explicit_reply(Untyped_capability reply_cap, int return_value); + }; +} + +#endif /* _INCLUDE__BASE__RPC_SERVER_H_ */ diff --git a/base/include/base/semaphore.h b/base/include/base/semaphore.h new file mode 100644 index 000000000..353635e60 --- /dev/null +++ b/base/include/base/semaphore.h @@ -0,0 +1,173 @@ +/* + * \brief Semaphore + * \author Norman Feske + * \author Christian Prochaska + * \date 2006-09-22 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SEMAPHORE_H_ +#define _INCLUDE__BASE__SEMAPHORE_H_ + +#include +#include +#include + +namespace Genode { + + /** + * Semaphore queue interface + */ + class Semaphore_queue + { + public: + + /** + * Semaphore-queue elements + * + * A queue element represents a thread blocking on the + * semaphore. + */ + class Element : Lock + { + public: + + /** + * Constructor + */ + Element() : Lock(LOCKED) { } + + void block() { lock(); } + void wake_up() { unlock(); } + }; + + /** + * Add new queue member that is going to block + */ + void enqueue(Element *e); + + /** + * Dequeue queue member to wake up next + */ + Element *dequeue(); + }; + + + /** + * First-in-first-out variant of the semaphore-queue interface + */ + class Fifo_semaphore_queue : public Semaphore_queue + { + public: + + class Element : public Semaphore_queue::Element, + public Fifo::Element { }; + + private: + + Fifo _fifo; + + public: + + void enqueue(Element *e) { _fifo.enqueue(e); } + + Element *dequeue() { return _fifo.dequeue(); } + }; + + + /** + * Semaphore base template + * + * \param QT semaphore wait queue type implementing the + * 'Semaphore_queue' interface + * \param QTE wait-queue element type implementing the + * 'Semaphore_queue::Element' interface + * + * The queuing policy is defined via the QT and QTE types. + * This way, the platform-specific semaphore-queueing policies + * such as priority-sorted queueing can be easily supported. + */ + template + class Semaphore_template + { + protected: + + int _cnt; + Lock _meta_lock; + QT _queue; + + public: + + /** + * Constructor + * + * \param n initial counter value of the semphore + */ + Semaphore_template(int n = 0) : _cnt(n) { } + + ~Semaphore_template() + { + /* synchronize destruction with unfinished 'up()' */ + try { _meta_lock.lock(); } catch (...) { } + } + + void up() + { + Lock::Guard lock_guard(_meta_lock); + + if (++_cnt > 0) + return; + + /* + * Remove element from queue and wake up the corresponding + * blocking thread + */ + _queue.dequeue()->wake_up(); + } + + void down() + { + _meta_lock.lock(); + + if (--_cnt < 0) { + + /* + * Create semaphore queue element representing the thread + * in the wait queue. + */ + QTE queue_element; + _queue.enqueue(&queue_element); + _meta_lock.unlock(); + + /* + * The thread is going to block on a local lock now, + * waiting for getting waked from another thread + * calling 'up()' + * */ + queue_element.block(); + + } else { + _meta_lock.unlock(); + } + } + + /** + * Return current semaphore counter + */ + int cnt() { return _cnt; } + }; + + + /** + * Semaphore with default behaviour + */ + typedef Semaphore_template Semaphore; +} + +#endif /* _INCLUDE__BASE__SEMAPHORE_H_ */ diff --git a/base/include/base/service.h b/base/include/base/service.h new file mode 100644 index 000000000..a574fa13b --- /dev/null +++ b/base/include/base/service.h @@ -0,0 +1,414 @@ +/* + * \brief Service management framework + * \author Norman Feske + * \date 2006-07-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SERVICE_H_ +#define _INCLUDE__BASE__SERVICE_H_ + +#include +#include +#include +#include +#include + +namespace Genode { + + /** + * Client role + * + * A client is someone who applies for a service. If the service is not + * available yet, we enqueue the client into a wait queue and wake him up + * as soon as the requested service gets available. + */ + class Client : public List::Element + { + private: + + Cancelable_lock _service_apply_lock; + const char *_apply_for; + + public: + + /** + * Constructor + */ + Client(): _service_apply_lock(Lock::LOCKED), _apply_for(0) { } + + virtual ~Client() { } + + /** + * Set/Request service name that we are currently applying for + */ + void apply_for(const char *apply_for) { _apply_for = apply_for; } + const char *apply_for() { return _apply_for; } + + /** + * Service wait queue support + */ + void sleep() { _service_apply_lock.lock(); } + void wakeup() { _service_apply_lock.unlock(); } + }; + + + /** + * Server role + * + * A server is a process that provides one or multiple services. For the + * most part, this class is used as an opaque key to represent the server + * role. + */ + class Server + { + private: + + Ram_session_capability _ram; + + public: + + /** + * Constructor + * + * \param ram RAM session capability of the server process used, + * for quota transfers from/to the server + */ + Server(Ram_session_capability ram): _ram(ram) { } + + /** + * Return RAM session capability of the server process + */ + Ram_session_capability ram_session_cap() const { return _ram; } + }; + + + class Service : public List::Element + { + public: + + enum { MAX_NAME_LEN = 32 }; + + private: + + char _name[MAX_NAME_LEN]; + + public: + + /********************* + ** Exception types ** + *********************/ + + class Invalid_args { }; + class Unavailable { }; + class Quota_exceeded { }; + + /** + * Constructor + * + * \param name service name + */ + Service(const char *name) { strncpy(_name, name, sizeof(_name)); } + + virtual ~Service() { } + + /** + * Return service name + */ + const char *name() const { return _name; } + + /** + * Create session + * + * \param args session-construction arguments + * + * \throw Invalid_args + * \throw Unavailable + * \throw Quota_exceeded + */ + virtual Session_capability session(const char *args) = 0; + + /** + * Extend resource donation to an existing session + */ + virtual void upgrade(Session_capability session, const char *args) = 0; + + /** + * Close session + */ + virtual void close(Session_capability session) { } + + /** + * Return server providing the service + */ + virtual Server *server() const { return 0; } + + /** + * Return the RAM session to be used for trading resources + */ + Ram_session_capability ram_session_cap() + { + if (server()) + return server()->ram_session_cap(); + return Ram_session_capability(); + } + }; + + + /** + * Representation of a locally implemented service + */ + class Local_service : public Service + { + private: + + Root *_root; + + public: + + Local_service(const char *name, Root *root) + : Service(name), _root(root) { } + + Session_capability session(const char *args) + { + try { return _root->session(args); } + catch (Root::Invalid_args) { throw Invalid_args(); } + catch (Root::Unavailable) { throw Unavailable(); } + catch (Root::Quota_exceeded) { throw Quota_exceeded(); } + } + + void upgrade(Session_capability session, const char *args) { + _root->upgrade(session, args); } + + void close(Session_capability session) { + _root->close(session); } + }; + + + /** + * Representation of a service provided by our parent + */ + class Parent_service : public Service + { + public: + + Parent_service(const char *name) : Service(name) { } + + Session_capability session(const char *args) + { + try { return env()->parent()->session(name(), args); } + catch (Parent::Unavailable) { + PWRN("parent has no service \"%s\"", name()); + throw Unavailable(); + } + catch (Parent::Quota_exceeded) { throw Quota_exceeded(); } + } + + void upgrade(Session_capability session, const char *args) { + env()->parent()->upgrade(session, args); } + + void close(Session_capability session) { + env()->parent()->close(session); } + }; + + + /** + * Representation of a service that is implemented in a child + */ + class Child_service : public Service + { + private: + + Root_capability _root_cap; + Root_client _root; + Server *_server; + + public: + + /** + * Constructor + * + * \param name name of service + * \param root capability to root interface + * \param server server process providing the service + */ + Child_service(const char *name, + Root_capability root, + Server *server) + : Service(name), _root_cap(root), _root(root), _server(server) { } + + Server *server() const { return _server; } + + Session_capability session(const char *args) + { + if (!_root_cap.valid()) + throw Unavailable(); + + try { return _root.session(args); } + catch (Root::Invalid_args) { throw Invalid_args(); } + catch (Root::Unavailable) { throw Unavailable(); } + catch (Root::Quota_exceeded) { throw Quota_exceeded(); } + } + + void upgrade(Session_capability sc, const char *args) + { + if (!_root_cap.valid()) + throw Unavailable(); + + try { _root.upgrade(sc, args); } + catch (Root::Invalid_args) { throw Invalid_args(); } + catch (Root::Unavailable) { throw Unavailable(); } + catch (Root::Quota_exceeded) { throw Quota_exceeded(); } + } + + void close(Session_capability sc) { _root.close(sc); } + }; + + + /** + * Container for holding service representations + */ + class Service_registry + { + protected: + + Lock _service_wait_queue_lock; + List _service_wait_queue; + List _services; + + public: + + /** + * Probe for service with specified name + * + * \param name service name + * \param server server providing the service, + * default (0) for any server + */ + Service *find(const char *name, Server *server = 0) + { + if (!name) return 0; + + Lock::Guard lock_guard(_service_wait_queue_lock); + + for (Service *s = _services.first(); s; s = s->next()) + if (strcmp(s->name(), name) == 0 + && (!server || s->server() == server)) return s; + + return 0; + } + + /** + * Check if service name is ambiguous + * + * \return true if the same service is provided multiple + * times + */ + bool is_ambiguous(const char *name) + { + Lock::Guard lock_guard(_service_wait_queue_lock); + + /* count number of services with the specified name */ + unsigned cnt = 0; + for (Service *s = _services.first(); s; s = s->next()) + cnt += (strcmp(s->name(), name) == 0); + return cnt > 1; + } + + /** + * Return first service provided by specified server + */ + Service *find_by_server(Server *server) + { + Lock::Guard lock_guard(_service_wait_queue_lock); + + for (Service *s = _services.first(); s; s = s->next()) + if (s->server() == server) + return s; + + return 0; + } + + /** + * Wait for service + * + * This function is called by the clients's thread + * when requesting a session creation. It blocks + * if the requested service is not available. + * + * \return service structure that matches the request or + * 0 if the waiting was canceled. + */ + Service *wait_for_service(const char *name, Client *client, const char *client_name) + { + Service *service; + + client->apply_for(name); + + _service_wait_queue_lock.lock(); + _service_wait_queue.insert(client); + _service_wait_queue_lock.unlock(); + + do { + service = find(name); + + /* + * The service that we are seeking is not available today. + * Lets sleep a night over it. + */ + if (!service) { + printf("%s: service %s not yet available - sleeping\n", + client_name, name); + + try { + client->sleep(); + printf("%s: service %s got available\n", client_name, name); + } catch (Blocking_canceled) { + printf("%s: cancel waiting for service\n", client_name); + break; + } + } + + } while (!service); + + /* we got what we needed, stop applying */ + _service_wait_queue_lock.lock(); + _service_wait_queue.remove(client); + _service_wait_queue_lock.unlock(); + + client->apply_for(0); + + return service; + } + + /** + * Register service + * + * This function is called by the server's thread. + */ + void insert(Service *service) + { + /* make new service known */ + _services.insert(service); + + /* wake up applicants waiting for the service */ + Lock::Guard lock_guard(_service_wait_queue_lock); + for (Client *c = _service_wait_queue.first(); c; c = c->next()) + if (strcmp(service->name(), c->apply_for()) == 0) + c->wakeup(); + } + + /** + * Unregister service + */ + void remove(Service *service) { _services.remove(service); } + }; +} + +#endif /* _INCLUDE__BASE__SERVICE_H_ */ diff --git a/base/include/base/signal.h b/base/include/base/signal.h new file mode 100644 index 000000000..f142e03b1 --- /dev/null +++ b/base/include/base/signal.h @@ -0,0 +1,284 @@ +/* + * \brief Delivery and reception of asynchronous notifications + * \author Norman Feske + * \date 2008-09-05 + * + * Each transmitter sends signals to one fixed destination. + * A receiver can receive signals from multiple sources. + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SIGNAL_H__ +#define _INCLUDE__BASE__SIGNAL_H__ + +#include +#include + +namespace Genode { + + class Signal_receiver; + class Signal_context; + + + /** + * Signal + * + * A signal represents a number of asynchronous notifications produced by + * one transmitter. If notifications are generated at a higher rate than as + * they can be processed at the receiver, the transmitter counts the + * notifications and delivers the total amount with the next signal + * transmission. This way, the total number of notifications gets properly + * communicated to the receiver even if the receiver is not highly + * responsive. + * + * Asynchronous notifications do not carry any payload because this payload + * would need to be queued at the transmitter. However, each transmitter + * imprints a signal-context reference into each signal. This context + * can be used by the receiver to distinguish signals coming from different + * transmitters. + */ + class Signal + { + private: + + friend class Signal_receiver; + friend class Signal_context; + + Signal_context *_context; + int _num; + + /** + * Constructor + * + * \param context signal context specific for the signal-receiver + * capability used for signal transmission + * \param num number of signals received from the same transmitter + * + * Signal objects are constructed only by signal receivers. + */ + Signal(Signal_context *context, int num) + : _context(context), _num(num) + { } + + public: + + /** + * Default constructor, creating an invalid signal + */ + Signal() : _context(0), _num(0) { } + + /** + * Return signal context + */ + Signal_context *context() { return _context; } + + /** + * Return number of signals received from the same transmitter + */ + int num() { return _num; } + }; + + /** + * Signal context + * + * A signal context is a destination for signals. One receiver can listen + * to multple contexts. If a signal arrives, the context is provided with the + * signel. This enables the receiver to distinguish different signal sources + * and dispatch incoming signals context-specific. + */ + class Signal_context + { + private: + + /** + * Helper class to handle a 'Signal_context' as list element + */ + struct List_element : public List::Element { + Signal_context *context; }; + + /** + * List element in the receiver's context list + */ + List_element _list_element; + + /** + * Receiver to which the context is associated with + * + * This member is initialized by the receiver when associating + * the context with the receiver via the 'cap' function. + */ + Signal_receiver *_receiver; + + Lock _lock; /* protect '_curr_signal' */ + Signal _curr_signal; /* most-currently received signal */ + bool _pending; /* current signal is valid */ + + /** + * Capability assigned to this context after being assocated with + * a 'Signal_receiver' via the 'manage' function. We store this + * capability in the 'Signal_context' for the mere reason to + * properly destruct the context (see '_unsynchronized_dissolve'). + */ + Signal_context_capability _cap; + + friend class Signal_receiver; + + public: + + /** + * Constructor + */ + Signal_context() : _receiver(0), _pending(0) { } + + /** + * Destructor + * + * The virtual destructor is just there to generate a vtable for + * signal-context objects such that signal contexts can be dynamically + * casted. + */ + virtual ~Signal_context() { } + + /* + * Signal contexts are never invoked but only used as arguments for + * 'Signal_session' functions. Hence, there exists a capability + * type for it but no real RPC interface. + */ + GENODE_RPC_INTERFACE(); + }; + + + /** + * Signal transmitter + * + * Each signal-transmitter instance acts on behalf the context specified + * as constructor argument. Therefore, the resources needed for the + * transmitter such as the consumed memory 'sizeof(Signal_transmitter)' + * should be accounted to the owner of the context. + */ + class Signal_transmitter + { + private: + + Signal_context_capability _context; /* destination */ + + public: + + /** + * Constructor + * + * \param context capability to signal context that is going to + * receive signals produced by the transmitter + */ + Signal_transmitter(Signal_context_capability context = Signal_context_capability()); + + /** + * Set signal context + */ + void context(Signal_context_capability context); + + /** + * Trigger signal submission to context + * + * \param cnt number of signals to submit at once + */ + void submit(int cnt = 1); + }; + + + /** + * Signal receiver + */ + class Signal_receiver + { + private: + + Semaphore _signal_available; /* signal(s) awaiting to be picked up */ + + /** + * List of associated contexts + */ + Lock _contexts_lock; + List _contexts; + + /** + * Helper to dissolve given context + * + * This function prevents duplicated code in '~Signal_receiver' + * and 'dissolve'. Note that '_contexts_lock' must be held when + * calling this function. + */ + void _unsynchronized_dissolve(Signal_context *context); + + public: + + /** + * Exception class + */ + class Context_already_in_use { }; + class Context_not_associated { }; + + /** + * Constructor + */ + Signal_receiver(); + + /** + * Destructor + */ + ~Signal_receiver(); + + /** + * Manage signal context and return new signal-context capability + * + * \param context context associated with signals delivered to the + * receiver + * \throw 'Context_already_in_use' + * \return new signal-context capability that can be + * passed to a signal transmitter + */ + Signal_context_capability manage(Signal_context *context); + + /** + * Dissolve signal context from receiver + * + * \param context context to remove from receiver + * \throw 'Context_not_associated' + */ + void dissolve(Signal_context *context); + + /** + * Return true if signal was received + */ + bool pending(); + + /** + * Block until a signal is received + * + * \return received signal + */ + Signal wait_for_signal(); + + /** + * Locally submit signal to the receiver + */ + void local_submit(Signal signal); + + /** + * Framework-internal signal-dispatcher + * + * This function is called from the thread that monitors the signal + * source associated with the process. It must not be used for other + * purposes. + */ + static void dispatch_signals(Signal_source *signal_source); + }; +} + +#endif /* _INCLUDE__BASE__SIGNAL_H__ */ diff --git a/base/include/base/slab.h b/base/include/base/slab.h new file mode 100644 index 000000000..d3eeec0e8 --- /dev/null +++ b/base/include/base/slab.h @@ -0,0 +1,255 @@ +/* + * \brief Slab allocator + * \author Norman Feske + * \date 2006-04-18 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SLAB_H_ +#define _INCLUDE__BASE__SLAB_H_ + +#include +#include + +namespace Genode { + + class Slab; + class Slab_entry; + class Allocator; + + /** + * A slab block holds an array of slab entries. + */ + class Slab_block + { + public: + + Slab_block *next; /* next block */ + Slab_block *prev; /* previous block */ + + private: + + enum { FREE, USED }; + + Slab *_slab; /* back reference to slab allocator */ + unsigned _avail; /* free entries of this block */ + + /* + * Each slab block consists of three areas, a fixed-size header + * that contains the member variables declared above, a byte array + * called state table that holds the allocation state for each slab + * entry, and an area holding the actual slab entries. The number + * of state-table elements corresponds to the maximum number of slab + * entries per slab block (the '_num_elem' member variable of the + * Slab allocator). + */ + + char _data[]; /* dynamic data (state table and slab entries) */ + + /* + * Caution! no member variables allowed below this line! + */ + + /** + * Accessor functions to allocation state + * + * \param idx index of slab entry + */ + inline bool state(int idx) { return _data[idx]; } + inline void state(int idx, bool state) { _data[idx] = state; } + + /** + * Request address of slab entry by its index + */ + Slab_entry *slab_entry(int idx); + + /** + * Determine block index of specified slab entry + */ + int slab_entry_idx(Slab_entry *e); + + public: + + /** + * Constructor + * + * Normally, Slab_blocks are constructed by a Slab allocator + * that specifies itself as constructor argument. + */ + explicit Slab_block(Slab *s = 0) { if (s) slab(s); } + + /** + * Configure block to be managed by the specified slab allocator + */ + void slab(Slab *slab); + + /** + * Request number of available entries in block + */ + unsigned avail() { return _avail; } + + /** + * Allocate slab entry from block + */ + void *alloc(); + + /** + * Return a used slab block entry + */ + Slab_entry *first_used_entry(); + + /** + * These functions are called by Slab_entry. + */ + void inc_avail(Slab_entry *e); + void dec_avail(); + + /** + * Debug and test hooks + */ + void dump(); + int check_bounds(); + }; + + + class Slab_entry + { + private: + + Slab_block *_sb; + char _data[]; + + /* + * Caution! no member variables allowed below this line! + */ + + public: + + void init() { _sb = 0; } + + void occupy(Slab_block *sb) + { + _sb = sb; + _sb->dec_avail(); + } + + void free() + { + _sb->inc_avail(this); + _sb = 0; + } + + void *addr() { return _data; } + + /** + * Lookup Slab_entry by given address + * + * The specified address is supposed to point to _data[0]. + */ + static Slab_entry *slab_entry(void *addr) { + return (Slab_entry *)((addr_t)addr - sizeof(Slab_entry)); } + }; + + + /** + * Slab allocator + */ + class Slab : public Allocator + { + private: + + size_t _slab_size; /* size of one slab entry */ + size_t _block_size; /* size of slab block */ + size_t _num_elem; /* number of slab entries per block */ + Slab_block *_first_sb; /* first slab block */ + Slab_block *_initial_sb; /* initial (static) slab block */ + bool _alloc_state; /* indicator for 'currently in service' */ + + Allocator *_backing_store; + + /** + * Allocate and initialize new slab block + */ + Slab_block *_new_slab_block(); + + public: + + inline size_t slab_size() { return _slab_size; } + inline size_t block_size() { return _block_size; } + inline size_t num_elem() { return _num_elem; } + inline size_t entry_size() { return sizeof(Slab_entry) + _slab_size; } + + /** + * Constructor + * + * At construction time, there exists one initial slab + * block that is used for the first couple of allocations, + * especially for the allocation of the second slab + * block. + */ + Slab(size_t slab_size, size_t block_size, Slab_block *initial_sb, + Allocator *backing_store = 0); + + /** + * Destructor + */ + ~Slab(); + + /** + * Debug function for dumping the current slab block list + */ + void dump_sb_list(); + + /** + * Remove block from slab block list + */ + void remove_sb(Slab_block *sb); + + /** + * Insert block into slab block list + */ + void insert_sb(Slab_block *sb, Slab_block *at = 0); + + /** + * Allocate slab entry + */ + void *alloc(); + + /** + * Free slab entry + */ + static void free(void *addr); + + /** + * Return a used slab element + */ + void *first_used_elem(); + + /** + * Return true if number of free slab entries is higher than n + */ + bool num_free_entries_higher_than(int n); + + /** + * Define/request backing-store allocator + */ + void backing_store(Allocator *bs) { _backing_store = bs; } + Allocator *backing_store() { return _backing_store; } + + /** + * Allocator interface + */ + bool alloc(size_t, void **); + void free(void *addr, size_t) { free(addr); } + size_t consumed(); + size_t overhead(size_t size) { return _block_size/_num_elem; } + }; +} + +#endif /* _INCLUDE__BASE__SLAB_H_ */ diff --git a/base/include/base/sleep.h b/base/include/base/sleep.h new file mode 100644 index 000000000..737f379b2 --- /dev/null +++ b/base/include/base/sleep.h @@ -0,0 +1,29 @@ +/* + * \brief Lay back and relax + * \author Norman Feske + * \date 2006-07-19 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SLEEP_H_ +#define _INCLUDE__BASE__SLEEP_H_ + +#include + +namespace Genode { + + __attribute__((noreturn)) inline void sleep_forever() + { + Msgbuf<16> buf; + Ipc_server s(&buf, &buf); + while (1) s >> IPC_WAIT; + } +} + +#endif /* _INCLUDE__BASE__SLEEP_H_ */ diff --git a/base/include/base/snprintf.h b/base/include/base/snprintf.h new file mode 100644 index 000000000..08e3bc9a5 --- /dev/null +++ b/base/include/base/snprintf.h @@ -0,0 +1,82 @@ +/* + * \brief Facility to write format string into character buffer + * \author Norman Feske + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SNPRINTF_H_ +#define _INCLUDE__BASE__SNPRINTF_H_ + +#include +#include + +namespace Genode { + + class String_console : public Console + { + private: + + char *_dst; + size_t _dst_len; + size_t _w_offset; + + public: + + /** + * Constructor + * + * \param dst destination character buffer + * \param dst_len size of dst + */ + String_console(char *dst, size_t dst_len) + : _dst(dst), _dst_len(dst_len), _w_offset(0) + { _dst[0] = 0; } + + /** + * Return number of characters in destination buffer + */ + size_t len() { return _w_offset; } + + + /*********************** + ** Console interface ** + ***********************/ + + void _out_char(char c) + { + /* ensure to leave space for null-termination */ + if (_w_offset > _dst_len - 2) + return; + + _dst[_w_offset++] = c; + _dst[_w_offset] = 0; + } + }; + + /** + * Print format string into character buffer + * + * \return number of characters written to destination buffer + */ + inline int snprintf(char *, size_t, const char *, ...) __attribute__((format(printf, 3, 4))); + inline int snprintf(char *dst, size_t dst_len, const char *format, ...) + { + va_list list; + va_start(list, format); + + String_console sc(dst, dst_len); + sc.vprintf(format, list); + + va_end(list); + return sc.len(); + } +} + +#endif /* _INCLUDE__BASE__SNPRINTF_H_ */ diff --git a/base/include/base/stdint.h b/base/include/base/stdint.h new file mode 100644 index 000000000..a06109d8f --- /dev/null +++ b/base/include/base/stdint.h @@ -0,0 +1,43 @@ +/* + * \brief Genode-specific integer types + * \author Christian Helmuth + * \date 2006-05-10 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__STDINT_H_ +#define _INCLUDE__BASE__STDINT_H_ + +/* fixed-width integer types */ +#include + +namespace Genode { + + /** + * Integer type for non-negative size values + */ + typedef __SIZE_TYPE__ size_t; + + /** + * Integer type for memory addresses + */ + typedef unsigned long addr_t; + + /** + * Integer type for memory offset values + */ + typedef long off_t; + + /** + * Integer type corresponding to a machine register + */ + typedef unsigned long umword_t; +} + +#endif /* _INCLUDE__BASE__STDINT_H_ */ diff --git a/base/include/base/sync_allocator.h b/base/include/base/sync_allocator.h new file mode 100644 index 000000000..042e9b001 --- /dev/null +++ b/base/include/base/sync_allocator.h @@ -0,0 +1,168 @@ +/* + * \brief Lock-guarded allocator interface + * \author Norman Feske + * \date 2008-08-05 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SYNC_ALLOCATOR_H_ +#define _INCLUDE__BASE__SYNC_ALLOCATOR_H_ + +#include +#include + +namespace Genode { + + /** + * Lock-guarded range allocator + * + * This class wraps the complete 'Range_allocator' interface while + * preventing concurrent calls to the wrapped allocator implementation. + * + * \param ALLOCATOR_IMPL class implementing the 'Range_allocator' + * interface + */ + template + class Synchronized_range_allocator : public Range_allocator + { + private: + + Lock _default_lock; + Lock *_lock; + ALLOCATOR_IMPL _alloc; + + public: + + /** + * Constructor + * + * This constructor uses an embedded lock for synchronizing the + * access to the allocator. + */ + Synchronized_range_allocator() + : _lock(&_default_lock) { } + + /** + * Constructor + * + * This constructor uses an embedded lock for synchronizing the + * access to the allocator. + */ + explicit Synchronized_range_allocator(Allocator *metadata_alloc) + : _lock(&_default_lock), _alloc(metadata_alloc) { } + + /** + * Constructor + * + * \param lock use specified lock rather then an embedded lock for + * synchronization + * + * This constructor is useful if multiple allocators must be + * synchronized with each other. In such as case, the shared + * lock can be passed to each 'Synchronized_range_allocator' + * instance. + */ + Synchronized_range_allocator(Lock *lock, Allocator *metadata_alloc) + : _lock(lock), _alloc(metadata_alloc) { } + + /** + * Return reference to wrapped (non-thread-safe) allocator + * + * This is needed, for example, if the wrapped allocator implements + * methods in addition to the Range_allocator interface. + * + * NOTE: Synchronize accesses to the raw allocator by facilitating + * the lock() member function. + */ + ALLOCATOR_IMPL *raw() { return &_alloc; } + + /** + * Return reference to synchronization lock + */ + Lock *lock() { return _lock; } + + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t size, void **out_addr) + { + Lock::Guard lock_guard(*_lock); + return _alloc.alloc(size, out_addr); + } + + void free(void *addr, size_t size) + { + Lock::Guard lock_guard(*_lock); + _alloc.free(addr, size); + } + + size_t consumed() + { + Lock::Guard lock_guard(*_lock); + return _alloc.consumed(); + } + + size_t overhead(size_t size) + { + Lock::Guard lock_guard(*_lock); + return _alloc.overhead(size); + } + + + /******************************* + ** Range-allocator interface ** + *******************************/ + + int add_range(addr_t base, size_t size) + { + Lock::Guard lock_guard(*_lock); + return _alloc.add_range(base, size); + } + + int remove_range(addr_t base, size_t size) + { + Lock::Guard lock_guard(*_lock); + return _alloc.remove_range(base, size); + } + + bool alloc_aligned(size_t size, void **out_addr, int align = 0) + { + Lock::Guard lock_guard(*_lock); + return _alloc.alloc_aligned(size, out_addr, align); + } + + Alloc_return alloc_addr(size_t size, addr_t addr) + { + Lock::Guard lock_guard(*_lock); + return _alloc.alloc_addr(size, addr); + } + + void free(void *addr) + { + Lock::Guard lock_guard(*_lock); + _alloc.free(addr); + } + + size_t avail() + { + Lock::Guard lock_guard(*_lock); + return _alloc.avail(); + } + + bool valid_addr(addr_t addr) + { + Lock::Guard lock_guard(*_lock); + return _alloc.valid_addr(addr); + } + }; +} + +#endif /* _INCLUDE__BASE__SYNC_ALLOCATOR_H_ */ diff --git a/base/include/base/thread.h b/base/include/base/thread.h new file mode 100644 index 000000000..d82368da5 --- /dev/null +++ b/base/include/base/thread.h @@ -0,0 +1,367 @@ +/* + * \brief Thread interface + * \author Norman Feske + * \date 2006-04-28 + * + * For storing thread-specific data (called thread context) such as the stack + * and thread-local data, there is a dedicated portion of the virtual address + * space. This portion is called thread-context area. Within the thread-context + * area, each thread has a fixed-sized slot, a thread context. The layout of + * each thread context looks as follows + * + * ! lower address + * ! ... + * ! ============================ <- aligned at 'CONTEXT_VIRTUAL_SIZE' + * ! + * ! empty + * ! + * ! ---------------------------- + * ! + * ! stack + * ! (top) <- initial stack pointer + * ! ---------------------------- <- address of 'Context' object + * ! additional context members + * ! ---------------------------- + * ! UTCB + * ! ============================ <- aligned at 'CONTEXT_VIRTUAL_SIZE' + * ! ... + * ! higher address + * + * On some platforms, a user-level thread-control block (UTCB) area contains + * data shared between the user-level thread and the kernel. It is typically + * used for transferring IPC message payload or for system-call arguments. + * The additional context members are a reference to the corresponding + * 'Thread_base' object and the name of the thread. + * + * The thread context is a virtual memory area, initially not backed by real + * memory. When a new thread is created, an empty thread context gets assigned + * to the new thread and populated with memory pages for the stack and the + * additional context members. Note that this memory is allocated from the RAM + * session of the process environment and not accounted for when using the + * 'sizeof()' operand on a 'Thread_base' object. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__THREAD_H_ +#define _INCLUDE__BASE__THREAD_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include /* for 'Ram_dataspace_capability' type */ +#include /* for 'Thread_capability' type */ + + +namespace Genode { + + class Rm_session; + + /** + * Concurrent control flow + * + * A 'Thread_base' object corresponds to a physical thread. The execution + * starts at the 'entry()' function as soon as 'start()' is called. + */ + class Thread_base + { + public: + + class Context_alloc_failed : public Exception { }; + class Stack_too_large : public Exception { }; + class Stack_alloc_failed : public Exception { }; + + /* + * Thread-context area configuration. + * + * Please update platform-specific files after changing these + * values, e.g., 'base-linux/src/platform/context_area.*.ld'. + */ + enum { CONTEXT_AREA_VIRTUAL_BASE = 0x40000000 }; + enum { CONTEXT_AREA_VIRTUAL_SIZE = 0x10000000 }; + + /** + * Size of virtual address region holding the context of one thread + */ + enum { CONTEXT_VIRTUAL_SIZE = 0x00100000 }; + enum { CONTEXT_VIRTUAL_BASE_MASK = ~(CONTEXT_VIRTUAL_SIZE - 1) }; + + private: + + /** + * List-element helper to enable inserting threads in a list + */ + List_element _list_element; + + public: + + /** + * Thread context located within the thread-context area + * + * The end of a thread context is placed at a boundary aligned at + * 'CONTEXT_VIRTUAL_SIZE'. + */ + struct Context + { + /** + * Top of the stack + */ + long stack[]; + + /** + * Pointer to corresponding 'Thread_base' object + */ + Thread_base *thread_base; + + /** + * Virtual address of the start of the stack + * + * This address is pointing to the begin of the dataspace used + * for backing the thread context except for the UTCB (which is + * managed by the kernel). + */ + addr_t stack_base; + + /** + * Dataspace containing the backing store for the thread context + * + * We keep the dataspace capability to be able to release the + * backing store on thread destruction. + */ + Ram_dataspace_capability ds_cap; + + /** + * Maximum length of thread name, including null-termination + */ + enum { NAME_LEN = 64 }; + + /** + * Thread name, used for debugging + */ + char name[NAME_LEN]; + + /* + * <- end of regular memory area + * + * The following part of the thread context is backed by + * kernel-managed memory. No member variables are allowed + * beyond this point. + */ + + /** + * Kernel-specific user-level thread control block + */ + Native_utcb utcb; + }; + + private: + + /** + * Manage the allocation of thread contexts + * + * There exists only one instance of this class per process. + */ + class Context_allocator + { + private: + + List > _threads; + Lock _threads_lock; + + /** + * Detect if a context already exists at the specified address + */ + bool _is_in_use(addr_t base); + + public: + + /** + * Allocate thread context for specified thread + * + * \param thread thread for which to allocate the new context + * \return virtual address of new thread context, or + * 0 if the allocation failed + */ + Context *alloc(Thread_base *thread); + + /** + * Release thread context + */ + void free(Thread_base *thread); + + /** + * Return 'Context' object for a given base address + */ + static Context *base_to_context(addr_t base); + + /** + * Return base address of context containing the specified address + */ + static addr_t addr_to_base(void *addr); + }; + + /** + * Return thread-context allocator + */ + static Context_allocator *_context_allocator(); + + /** + * Allocate and locally attach a new thread context + */ + Context *_alloc_context(size_t stack_size); + + /** + * Detach and release thread context of the thread + */ + void _free_context(); + + /** + * Platform-specific thread-startup code + * + * On some platforms, each new thread has to perform a startup + * protocol, e.g., waiting for a message from the kernel. This hook + * function allows for the implementation of such protocols. + */ + void _thread_bootstrap(); + + /** + * Helper for thread startup + */ + static void _thread_start(); + + /** + * Hook for platform-specific constructor supplements + */ + void _init_platform_thread(); + + /** + * Hook for platform-specific destructor supplements + */ + void _deinit_platform_thread(); + + /* hook only used for microblaze kernel */ + void _init_context(Context* c); + + protected: + + /** + * Capability for this thread (set by _start()) + * + * Used if thread creation involves core's CPU service. + * Currently, this is not the case for NOVA. + */ + Genode::Thread_capability _thread_cap; + + /** + * Pointer to corresponding thread context + */ + Context *_context; + + /** + * Physical thread ID + */ + Native_thread _tid; + + public: + + /** + * Constructor + * + * \param name thread name for debugging + * \param stack_size stack size + * + * The stack for the new thread will be allocated from the RAM + * session of the process environment. A small portion of the + * stack size is internally used by the framework for storing + * thread-context information such as the thread's name (see + * 'struct Context'). + */ + Thread_base(const char *name, size_t stack_size); + + /** + * Destructor + */ + virtual ~Thread_base(); + + /** + * Entry function of the thread + */ + virtual void entry() = 0; + + /** + * Start execution of the thread + * + * This function is virtual to enable the customization of threads + * used as server activation. + */ + virtual void start(); + + /** + * Request name of thread + */ + void name(char *dst, size_t dst_len); + + /** + * Request capability of thread + */ + Genode::Thread_capability cap() const { return _thread_cap; } + + /** + * Cancel currently blocking operation + */ + void cancel_blocking(); + + /** + * Only to be called from platform-specific code + */ + Native_thread & tid() { return _tid; } + + /** + * Return top of stack + * + * \return pointer to first stack element + */ + void *stack_top() { return &_context->stack[-1]; } + + /** + * Return 'Thread_base' object corresponding to the calling thread + * + * \return pointer to 'Thread_base' object, or + * 0 if the calling thread is the main thread + */ + static Thread_base *myself(); + + /** + * Return user-level thread control block + * + * Note that it is safe to call this function on the result of the + * 'myself' function. It handles the special case of 'myself' being + * 0 when called by the main thread. + */ + Native_utcb *utcb(); + }; + + + template + class Thread : public Thread_base + { + public: + + /** + * Constructor + * + * \param name thread name (for debugging) + */ + explicit Thread(const char *name = "") + : Thread_base(name, STACK_SIZE) { } + }; +} + +#endif /* _INCLUDE__BASE__THREAD_H_ */ diff --git a/base/include/base/thread_state.h b/base/include/base/thread_state.h new file mode 100644 index 000000000..c19bd943c --- /dev/null +++ b/base/include/base/thread_state.h @@ -0,0 +1,26 @@ +/* + * \brief Thread state + * \author Norman Feske + * \date 2007-07-30 + * + * This file contains the generic part of the thread state. + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__THREAD_STATE_H_ +#define _INCLUDE__BASE__THREAD_STATE_H_ + +#include + +namespace Genode { + + struct Thread_state : public Cpu_state { }; +} + +#endif /* _INCLUDE__BASE__THREAD_STATE_H_ */ diff --git a/base/include/base/tslab.h b/base/include/base/tslab.h new file mode 100644 index 000000000..b709b27d5 --- /dev/null +++ b/base/include/base/tslab.h @@ -0,0 +1,35 @@ +/* + * \brief Typed slab allocator + * \author Norman Feske + * \date 2006-05-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__TSLAB_H_ +#define _INCLUDE__BASE__TSLAB_H_ + +#include + +namespace Genode { + + template + class Tslab : public Slab + { + public: + + Tslab(Allocator *backing_store, + Slab_block *initial_sb = 0) + : Slab(sizeof(T), BLOCK_SIZE, initial_sb, backing_store) + { } + + T *first_object() { return (T *)Slab::first_used_elem(); } + }; +} + +#endif /* _INCLUDE__BASE__TSLAB_H_ */ diff --git a/base/include/cap_session/cap_session.h b/base/include/cap_session/cap_session.h new file mode 100644 index 000000000..fd8a3657e --- /dev/null +++ b/base/include/cap_session/cap_session.h @@ -0,0 +1,59 @@ +/* + * \brief CAP-session interface + * \author Norman Feske + * \date 2006-06-23 + * + * A 'Cap_session' is an allocator of user-level capabilities. + * User-level capabilities are used to reference server objects + * across address spaces. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CAP_SESSION__CAP_SESSION_H_ +#define _INCLUDE__CAP_SESSION__CAP_SESSION_H_ + +#include +#include + +namespace Genode { + + struct Cap_session : Session + { + static const char *service_name() { return "CAP"; } + + virtual ~Cap_session() { } + + /** + * Allocate new unique userland capability + * + * \param ep entry point that will use this capability + * + * \return new userland capability + */ + virtual Native_capability alloc(Native_capability ep) = 0; + + /** + * Free userland capability + * + * \param cap userland capability to free + */ + virtual void free(Native_capability cap) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_alloc, Native_capability, alloc, Native_capability); + GENODE_RPC(Rpc_free, void, free, Native_capability); + GENODE_RPC_INTERFACE(Rpc_alloc, Rpc_free); + }; +} + +#endif /* _INCLUDE__CAP_SESSION__CAP_SESSION_H_ */ diff --git a/base/include/cap_session/capability.h b/base/include/cap_session/capability.h new file mode 100644 index 000000000..83aea89ef --- /dev/null +++ b/base/include/cap_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief CAP-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CAP_SESSION__CAPABILITY_H_ +#define _INCLUDE__CAP_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Cap_session_capability; } + +#endif /* _INCLUDE__CAP_SESSION__CAPABILITY_H_ */ diff --git a/base/include/cap_session/client.h b/base/include/cap_session/client.h new file mode 100644 index 000000000..3a6d006a8 --- /dev/null +++ b/base/include/cap_session/client.h @@ -0,0 +1,35 @@ +/* + * \brief Client-side CAP session interface + * \author Norman Feske + * \date 2006-07-10 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CAP_SESSION__CLIENT_H_ +#define _INCLUDE__CAP_SESSION__CLIENT_H_ + +#include +#include +#include + +namespace Genode { + + struct Cap_session_client : Rpc_client + { + explicit Cap_session_client(Cap_session_capability session) + : Rpc_client(session) { } + + Native_capability alloc(Native_capability ep) { + return call(ep); } + + void free(Native_capability cap) { call(cap); } + }; +} + +#endif /* _INCLUDE__CAP_SESSION__CLIENT_H_ */ diff --git a/base/include/cap_session/connection.h b/base/include/cap_session/connection.h new file mode 100644 index 000000000..a08d49b28 --- /dev/null +++ b/base/include/cap_session/connection.h @@ -0,0 +1,32 @@ +/* + * \brief Connection to CAP service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CAP_SESSION__CONNECTION_H_ +#define _INCLUDE__CAP_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Cap_connection : Connection, Cap_session_client + { + Cap_connection() + : + Connection(session("ram_quota=4K")), + Cap_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__CAP_SESSION__CONNECTION_H_ */ diff --git a/base/include/cpu_session/capability.h b/base/include/cpu_session/capability.h new file mode 100644 index 000000000..edfe17814 --- /dev/null +++ b/base/include/cpu_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief CPU-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CPU_SESSION__CAPABILITY_H_ +#define _INCLUDE__CPU_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Cpu_session_capability; } + +#endif /* _INCLUDE__CPU_SESSION__CAPABILITY_H_ */ diff --git a/base/include/cpu_session/client.h b/base/include/cpu_session/client.h new file mode 100644 index 000000000..3db87deb1 --- /dev/null +++ b/base/include/cpu_session/client.h @@ -0,0 +1,65 @@ +/* + * \brief Client-side cpu session interface + * \author Christian Helmuth + * \date 2006-07-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CPU_SESSION__CLIENT_H_ +#define _INCLUDE__CPU_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Cpu_session_client : Rpc_client + { + explicit Cpu_session_client(Cpu_session_capability session) + : Rpc_client(session) { } + + Thread_capability create_thread(Name const &name) { + return call(name); } + + void kill_thread(Thread_capability thread) { + call(thread); } + + Thread_capability first() { + return call(); } + + Thread_capability next(Thread_capability curr) { + return call(curr); } + + int set_pager(Thread_capability thread, Pager_capability pager) { + return call(thread, pager); } + + int start(Thread_capability thread, addr_t ip, addr_t sp) { + return call(thread, ip, sp); } + + void pause(Thread_capability thread) { + call(thread); } + + void resume(Thread_capability thread) { + call(thread); } + + void cancel_blocking(Thread_capability thread) { + call(thread); } + + int state(Thread_capability thread, Thread_state *dst_state) { + return call(thread, dst_state); } + + void exception_handler(Thread_capability thread, Signal_context_capability handler) { + call(thread, handler); } + + void single_step(Thread_capability thread, bool enable) { + call(thread, enable); } + }; +} + +#endif /* _INCLUDE__CPU_SESSION__CLIENT_H_ */ diff --git a/base/include/cpu_session/connection.h b/base/include/cpu_session/connection.h new file mode 100644 index 000000000..4fcf6cfed --- /dev/null +++ b/base/include/cpu_session/connection.h @@ -0,0 +1,42 @@ +/* + * \brief Connection to CPU service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CPU_SESSION__CONNECTION_H_ +#define _INCLUDE__CPU_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Cpu_connection : Connection, Cpu_session_client + { + enum { RAM_QUOTA = 32*1024 }; + + /** + * Constructor + * + * \param label initial session label + * \param priority designated priority of all threads created + * with this CPU session + */ + Cpu_connection(const char *label = "", long priority = DEFAULT_PRIORITY) + : + Connection( + session("priority=0x%lx, ram_quota=32K, label=\"%s\"", + priority, label)), + Cpu_session_client(cap()) { } + }; +} + +#endif /* _INCLUDE__CPU_SESSION__CONNECTION_H_ */ diff --git a/base/include/cpu_session/cpu_session.h b/base/include/cpu_session/cpu_session.h new file mode 100644 index 000000000..d0e6f5052 --- /dev/null +++ b/base/include/cpu_session/cpu_session.h @@ -0,0 +1,230 @@ +/* + * \brief CPU (processing time) manager session interface + * \author Christian Helmuth + * \date 2006-06-27 + * + * :Question: + * + * Why are thread operations not methods of the thread but + * methods of the CPU session? + * + * :Answer: + * + * This enables the CPU session to impose policies on thread + * operations. These policies are based on the session + * construction arguments. If thread operations would be + * provided as thread methods, Thread would need to consult + * its container object (its CPU session) about the authorization + * of each operation and, thereby, would introduce a circular + * dependency between CPU session and Thread. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CPU_SESSION__CPU_SESSION_H_ +#define _INCLUDE__CPU_SESSION__CPU_SESSION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + struct Cpu_session : Session + { + /********************* + ** Exception types ** + *********************/ + + class Thread_creation_failed : public Exception { }; + + static const char *service_name() { return "CPU"; } + + enum { THREAD_NAME_LEN = 48 }; + enum { PRIORITY_LIMIT = 1 << 16 }; + enum { DEFAULT_PRIORITY = 0 }; + + typedef Rpc_in_buffer Name; + + virtual ~Cpu_session() { } + + /** + * Create a new thread + * + * \param name name for the thread + * \return capability representing the new thread + * \throw Thread_creation_failed + */ + virtual Thread_capability create_thread(Name const &name) = 0; + + /** + * Kill an existing thread + * + * \param thread capability of the thread to kill + */ + virtual void kill_thread(Thread_capability thread) = 0; + + /** + * Retrieve thread list of CPU session + * + * The next() function returns an invalid capability if the + * specified thread does not exists or if it is the last one + * of the CPU session. + */ + virtual Thread_capability first() = 0; + virtual Thread_capability next(Thread_capability curr) = 0; + + /** + * Set paging capabilities for thread + * + * \param thread thread to configure + * \param pager capability used to propagate page faults + */ + virtual int set_pager(Thread_capability thread, + Pager_capability pager) = 0; + + /** + * Modify instruction and stack pointer of thread - start the + * thread + * + * \param thread thread to start + * \param ip initial instruction pointer + * \param sp initial stack pointer + * + * \return 0 on success + */ + virtual int start(Thread_capability thread, addr_t ip, addr_t sp) = 0; + + /** + * Pause the specified thread + * + * After calling this function, the execution of the thread can be + * continued by calling 'resume'. + */ + virtual void pause(Thread_capability thread) = 0; + + /** + * Resume the specified thread + */ + virtual void resume(Thread_capability thread) = 0; + + /** + * Cancel a currently blocking operation + * + * \param thread thread to unblock + */ + virtual void cancel_blocking(Thread_capability thread) = 0; + + /** + * Return thread state + * + * \param thread thread to spy on + * \param state_dst result + * + * \return 0 on success + */ + virtual int state(Thread_capability thread, + Thread_state *state_dst) = 0; + + /** + * Register signal handler for exceptions of the specified thread + */ + virtual void exception_handler(Thread_capability thread, + Signal_context_capability handler) = 0; + + /** + * Enable/disable single stepping for specified thread. + * + * Since this functions is currently supported by a small number of + * platforms, we provide a default implementation + * + * \param thread thread to set into single step mode + * \param enable true = enable single-step mode; false = disable + */ + virtual void single_step(Thread_capability thread, bool enable) {} + + /** + * Translate generic priority value to kernel-specific priority levels + * + * \param pf_prio_limit maximum priority used for the kernel, must + * be power of 2 + * \param prio generic priority value as used by the CPU + * session interface + * \param inverse order of platform priorities, if true + * 'pf_prio_limit' corresponds to the highest + * priority, otherwise it refers to the + * lowest priority. + * \return platform-specific priority value + */ + static unsigned scale_priority(unsigned pf_prio_limit, unsigned prio, + bool inverse = true) + { + /* if no priorities are used, use the platform priority limit */ + if (prio == 0) return pf_prio_limit; + + /* + * Generic priority values are (0 is highest, 'PRIORITY_LIMIT' + * is lowest. On platforms where priority levels are defined + * the other way round, we have to invert the priority value. + */ + prio = inverse ? Cpu_session::PRIORITY_LIMIT - prio : prio; + + /* scale value to platform priority range 0..pf_prio_limit */ + return (prio*pf_prio_limit)/Cpu_session::PRIORITY_LIMIT; + } + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC_THROW(Rpc_create_thread, Thread_capability, create_thread, + GENODE_TYPE_LIST(Thread_creation_failed), Name const &); + GENODE_RPC(Rpc_kill_thread, void, kill_thread, Thread_capability); + GENODE_RPC(Rpc_first, Thread_capability, first,); + GENODE_RPC(Rpc_next, Thread_capability, next, Thread_capability); + GENODE_RPC(Rpc_set_pager, int, set_pager, Thread_capability, Pager_capability); + GENODE_RPC(Rpc_start, int, start, Thread_capability, addr_t, addr_t); + GENODE_RPC(Rpc_pause, void, pause, Thread_capability); + GENODE_RPC(Rpc_resume, void, resume, Thread_capability); + GENODE_RPC(Rpc_cancel_blocking, void, cancel_blocking, Thread_capability); + GENODE_RPC(Rpc_state, int, state, Thread_capability, Thread_state *); + GENODE_RPC(Rpc_exception_handler, void, exception_handler, + Thread_capability, Signal_context_capability); + GENODE_RPC(Rpc_single_step, void, single_step, Thread_capability, bool); + + /* + * 'GENODE_RPC_INTERFACE' declaration done manually + * + * The number of RPC function of this interface exceeds the maximum + * number of elements supported by 'Meta::Type_list'. Therefore, we + * construct the type list by hand using nested type tuples instead + * of employing the convenience macro 'GENODE_RPC_INTERFACE'. + */ + typedef Meta::Type_tuple + > > > > > > > > > > > Rpc_functions; + }; +} + +#endif /* _INCLUDE__CPU_SESSION__CPU_SESSION_H_ */ diff --git a/base/include/dataspace/capability.h b/base/include/dataspace/capability.h new file mode 100644 index 000000000..4878038c3 --- /dev/null +++ b/base/include/dataspace/capability.h @@ -0,0 +1,22 @@ +/* + * \brief Dataspace capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__DATASPACE__CAPABILITY_H_ +#define _INCLUDE__DATASPACE__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Dataspace_capability; } + +#endif /* _INCLUDE__DATASPACE__CAPABILITY_H_ */ diff --git a/base/include/dataspace/client.h b/base/include/dataspace/client.h new file mode 100644 index 000000000..eb32c78d5 --- /dev/null +++ b/base/include/dataspace/client.h @@ -0,0 +1,33 @@ +/* + * \brief Dataspace client interface + * \author Norman Feske + * \date 2006-05-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__DATASPACE__CLIENT_H_ +#define _INCLUDE__DATASPACE__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Dataspace_client : Rpc_client + { + explicit Dataspace_client(Dataspace_capability ds) + : Rpc_client(ds) { } + + size_t size() { return call(); } + addr_t phys_addr() { return call(); } + bool writable() { return call(); } + }; +} + +#endif /* _INCLUDE__DATASPACE__CLIENT_H_ */ diff --git a/base/include/dataspace/dataspace.h b/base/include/dataspace/dataspace.h new file mode 100644 index 000000000..6a538b134 --- /dev/null +++ b/base/include/dataspace/dataspace.h @@ -0,0 +1,54 @@ +/* + * \brief Dataspace interface + * \author Norman Feske + * \date 2006-07-05 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__DATASPACE__DATASPACE_H_ +#define _INCLUDE__DATASPACE__DATASPACE_H_ + +#include +#include + +namespace Genode { + + struct Dataspace + { + virtual ~Dataspace() { } + + /** + * Request size of dataspace + */ + virtual size_t size() = 0; + + /** + * Request base address in physical address space + */ + virtual addr_t phys_addr() = 0; + + /** + * Return true if dataspace is writable + */ + virtual bool writable() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_size, size_t, size); + GENODE_RPC(Rpc_phys_addr, addr_t, phys_addr); + GENODE_RPC(Rpc_writable, bool, writable); + + GENODE_RPC_INTERFACE(Rpc_size, Rpc_phys_addr, Rpc_writable); + }; +} + +#endif /* _INCLUDE__DATASPACE__DATASPACE_H_ */ diff --git a/base/include/io_mem_session/capability.h b/base/include/io_mem_session/capability.h new file mode 100644 index 000000000..2f6ca0763 --- /dev/null +++ b/base/include/io_mem_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief I/O-memory session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_MEM_SESSION__CAPABILITY_H_ +#define _INCLUDE__IO_MEM_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Io_mem_session_capability; } + +#endif /* _INCLUDE__IO_MEM_SESSION__CAPABILITY_H_ */ diff --git a/base/include/io_mem_session/client.h b/base/include/io_mem_session/client.h new file mode 100644 index 000000000..96ee31841 --- /dev/null +++ b/base/include/io_mem_session/client.h @@ -0,0 +1,31 @@ +/* + * \brief Client-side I/O-memory session interface + * \author Christian Helmuth + * \date 2006-08-01 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_MEM_SESSION__CLIENT_H_ +#define _INCLUDE__IO_MEM_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Io_mem_session_client : Rpc_client + { + explicit Io_mem_session_client(Io_mem_session_capability session) + : Rpc_client(session) { } + + Io_mem_dataspace_capability dataspace() { return call(); } + }; +} + +#endif /* _INCLUDE__IO_MEM_SESSION__CLIENT_H_ */ diff --git a/base/include/io_mem_session/connection.h b/base/include/io_mem_session/connection.h new file mode 100644 index 000000000..b240bb0bd --- /dev/null +++ b/base/include/io_mem_session/connection.h @@ -0,0 +1,42 @@ +/* + * \brief Connection to I/O-memory service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_MEM_SESSION__CONNECTION_H_ +#define _INCLUDE__IO_MEM_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Io_mem_connection : Connection, Io_mem_session_client + { + /** + * Constructor + * + * \param base physical base address of memory-mapped I/O resource + * \param size size memory-mapped I/O resource + * \param write_combined enable write-combined access to I/O memory + */ + Io_mem_connection(addr_t base, size_t size, bool write_combined = false) + : + Connection( + session("ram_quota=4K, base=0x%p, size=0x%zx, wc=%s", + base, size, write_combined ? "yes" : "no")), + + Io_mem_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__IO_MEM_SESSION__CONNECTION_H_ */ diff --git a/base/include/io_mem_session/io_mem_session.h b/base/include/io_mem_session/io_mem_session.h new file mode 100644 index 000000000..9861c7baa --- /dev/null +++ b/base/include/io_mem_session/io_mem_session.h @@ -0,0 +1,51 @@ +/* + * \brief I/O-memory session interface + * \author Christian Helmuth + * \date 2006-08-01 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_MEM_SESSION__IO_MEM_SESSION_H_ +#define _INCLUDE__IO_MEM_SESSION__IO_MEM_SESSION_H_ + +#include +#include + +namespace Genode { + + struct Io_mem_dataspace : Dataspace { }; + + typedef Capability Io_mem_dataspace_capability; + + struct Io_mem_session : Session + { + static const char *service_name() { return "IO_MEM"; } + + virtual ~Io_mem_session() { } + + /** + * Request dataspace containing the IO_MEM session data + * + * \return capability to IO_MEM dataspace + * (may be invalid) + */ + virtual Io_mem_dataspace_capability dataspace() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_dataspace, Io_mem_dataspace_capability, dataspace); + + GENODE_RPC_INTERFACE(Rpc_dataspace); + }; +} + +#endif /* _INCLUDE__IO_MEM_SESSION__IO_MEM_SESSION_H_ */ diff --git a/base/include/io_port_session/capability.h b/base/include/io_port_session/capability.h new file mode 100644 index 000000000..98345512a --- /dev/null +++ b/base/include/io_port_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief I/O-port session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_PORT_SESSION__CAPABILITY_H_ +#define _INCLUDE__IO_PORT_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Io_port_session_capability; } + +#endif /* _INCLUDE__IO_PORT_SESSION__CAPABILITY_H_ */ diff --git a/base/include/io_port_session/client.h b/base/include/io_port_session/client.h new file mode 100644 index 000000000..7c69e34be --- /dev/null +++ b/base/include/io_port_session/client.h @@ -0,0 +1,47 @@ +/* + * \brief Client-side I/O-port session interface + * \author Christian Helmuth + * \date 2007-04-17 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_PORT_SESSION__CLIENT_H_ +#define _INCLUDE__IO_PORT_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Io_port_session_client : Rpc_client + { + explicit Io_port_session_client(Io_port_session_capability session) + : Rpc_client(session) { } + + unsigned char inb(unsigned short address) { + return call(address); } + + unsigned short inw(unsigned short address) { + return call(address); } + + unsigned inl(unsigned short address) { + return call(address); } + + void outb(unsigned short address, unsigned char value) { + call(address, value); } + + void outw(unsigned short address, unsigned short value) { + call(address, value); } + + void outl(unsigned short address, unsigned value) { + call(address, value); } + }; +} + +#endif /* _INCLUDE__IO_PORT_SESSION__CLIENT_H_ */ diff --git a/base/include/io_port_session/connection.h b/base/include/io_port_session/connection.h new file mode 100644 index 000000000..688874364 --- /dev/null +++ b/base/include/io_port_session/connection.h @@ -0,0 +1,42 @@ +/* + * \brief Connection to I/O-port service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_PORT_SESSION__CONNECTION_H_ +#define _INCLUDE__IO_PORT_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Io_port_connection : Connection, + Io_port_session_client + { + /** + * Constructor + * + * \param base base address of port range + * \param size size of port range + */ + Io_port_connection(unsigned base, unsigned size) + : + Connection( + session("ram_quota=4K, io_port_base=%u, io_port_size=%u", + base, size)), + + Io_port_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__IO_PORT_SESSION__CONNECTION_H_ */ diff --git a/base/include/io_port_session/io_port_session.h b/base/include/io_port_session/io_port_session.h new file mode 100644 index 000000000..7a4ec96fd --- /dev/null +++ b/base/include/io_port_session/io_port_session.h @@ -0,0 +1,116 @@ +/* + * \brief I/O-port session interface + * \author Christian Helmuth + * \date 2007-04-17 + * + * An I/O port session permits access to a range of ports. Inside this range + * variable-sized accesses (i.e., 8, 16, 32 bit) at arbitrary addresses are + * allowed - currently, alignment is not enforced. Core enforces that access is + * limited to the session-defined range while the user provides physical I/O port + * addresses as function parameters. + * + * The design is founded on experiences while programming PCI configuration + * space which needs two 32-bit port registers. Each byte, word and dword in + * the data register must be explicitly accessible for read and write. The old + * design needs six capabilities only for the data register. + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IO_PORT_SESSION__IO_PORT_SESSION_H_ +#define _INCLUDE__IO_PORT_SESSION__IO_PORT_SESSION_H_ + +#include +#include + +namespace Genode { + + struct Io_port_session : Session + { + static const char *service_name() { return "IO_PORT"; } + + virtual ~Io_port_session() { } + + /****************************** + ** Read value from I/O port ** + ******************************/ + + /** + * Read byte (8 bit) + * + * \param address physical I/O port address + * + * \return value read from port + */ + virtual unsigned char inb(unsigned short address) = 0; + + /** + * Read word (16 bit) + * + * \param address physical I/O port address + * + * \return value read from port + */ + virtual unsigned short inw(unsigned short address) = 0; + + /** + * Read double word (32 bit) + * + * \param address physical I/O port address + * + * \return value read from port + */ + virtual unsigned inl(unsigned short address) = 0; + + + /***************************** + ** Write value to I/O port ** + *****************************/ + + /** + * Write byte (8 bit) + * + * \param address physical I/O port address + * \param value value to write to port + */ + virtual void outb(unsigned short address, unsigned char value) = 0; + + /** + * Write word (16 bit) + * + * \param address physical I/O port address + * \param value value to write to port + */ + virtual void outw(unsigned short address, unsigned short value) = 0; + + /** + * Write double word (32 bit) + * + * \param address physical I/O port address + * \param value value to write to port + */ + virtual void outl(unsigned short address, unsigned value) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_inb, unsigned char, inb, unsigned short); + GENODE_RPC(Rpc_inw, unsigned short, inw, unsigned short); + GENODE_RPC(Rpc_inl, unsigned, inl, unsigned short); + + GENODE_RPC(Rpc_outb, void, outb, unsigned short, unsigned char); + GENODE_RPC(Rpc_outw, void, outw, unsigned short, unsigned short); + GENODE_RPC(Rpc_outl, void, outl, unsigned short, unsigned); + + GENODE_RPC_INTERFACE(Rpc_inb, Rpc_inw, Rpc_inl, Rpc_outb, Rpc_outw, Rpc_outl); + }; +} + +#endif /* _INCLUDE__IO_PORT_SESSION__IO_PORT_SESSION_H_ */ diff --git a/base/include/irq_session/capability.h b/base/include/irq_session/capability.h new file mode 100644 index 000000000..e8a137991 --- /dev/null +++ b/base/include/irq_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief IRQ-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IRQ_SESSION__CAPABILITY_H_ +#define _INCLUDE__IRQ_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Irq_session_capability; } + +#endif /* _INCLUDE__IRQ_SESSION__CAPABILITY_H_ */ diff --git a/base/include/irq_session/client.h b/base/include/irq_session/client.h new file mode 100644 index 000000000..b05c73e43 --- /dev/null +++ b/base/include/irq_session/client.h @@ -0,0 +1,31 @@ +/* + * \brief Client-side IRQ session interface + * \author Christian Helmuth + * \date 2007-09-13 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IRQ_SESSION__CLIENT_H_ +#define _INCLUDE__IRQ_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Irq_session_client : Rpc_client + { + explicit Irq_session_client(Irq_session_capability session) + : Rpc_client(session) { } + + void wait_for_irq() { call(); } + }; +} + +#endif /* _INCLUDE__IRQ_SESSION__CLIENT_H_ */ diff --git a/base/include/irq_session/connection.h b/base/include/irq_session/connection.h new file mode 100644 index 000000000..e2afb4f5b --- /dev/null +++ b/base/include/irq_session/connection.h @@ -0,0 +1,39 @@ +/* + * \brief Connection to IRQ service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IRQ_SESSION__CONNECTION_H_ +#define _INCLUDE__IRQ_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Irq_connection : Connection, Irq_session_client + { + /** + * Constructor + * + * \param irq physical interrupt number + */ + Irq_connection(unsigned irq) + : + Connection( + session("ram_quota=4K, irq_number=%u", irq)), + + Irq_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__IRQ_SESSION__CONNECTION_H_ */ diff --git a/base/include/irq_session/irq_session.h b/base/include/irq_session/irq_session.h new file mode 100644 index 000000000..283ebf7ff --- /dev/null +++ b/base/include/irq_session/irq_session.h @@ -0,0 +1,47 @@ +/* + * \brief IRQ session interface + * \author Christian Helmuth + * \date 2007-09-13 + * + * An open IRQ session represents a valid IRQ attachment/association. + * Initially, the interrupt is masked and will only occur if enabled. This is + * done by calling wait_for_irq(). When the interrupt is delivered to the + * client, it was acknowledged and masked at the interrupt controller before. + * + * Disassociation from an IRQ is done by closing the session. + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__IRQ_SESSION__IRQ_SESSION_H_ +#define _INCLUDE__IRQ_SESSION__IRQ_SESSION_H_ + +#include +#include + +namespace Genode { + + struct Irq_session : Session + { + static const char *service_name() { return "IRQ"; } + + virtual ~Irq_session() { } + + virtual void wait_for_irq() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_wait_for_irq, void, wait_for_irq); + GENODE_RPC_INTERFACE(Rpc_wait_for_irq); + }; +} + +#endif /* _INCLUDE__IRQ_SESSION__IRQ_SESSION_H_ */ diff --git a/base/include/log_session/capability.h b/base/include/log_session/capability.h new file mode 100644 index 000000000..d404b3588 --- /dev/null +++ b/base/include/log_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief LOG-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__LOG_SESSION__CAPABILITY_H_ +#define _INCLUDE__LOG_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Log_session_capability; } + +#endif /* _INCLUDE__LOG_SESSION__CAPABILITY_H_ */ diff --git a/base/include/log_session/client.h b/base/include/log_session/client.h new file mode 100644 index 000000000..711ee0bf6 --- /dev/null +++ b/base/include/log_session/client.h @@ -0,0 +1,32 @@ +/* + * \brief Client-side log text output session interface + * \author Norman Feske + * \date 2006-09-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__LOG_SESSION__CLIENT_H_ +#define _INCLUDE__LOG_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Log_session_client : Rpc_client + { + explicit Log_session_client(Log_session_capability session) + : Rpc_client(session) { } + + size_t write(String const &string) { + return call(string); } + }; +} + +#endif /* _INCLUDE__LOG_SESSION__CLIENT_H_ */ diff --git a/base/include/log_session/connection.h b/base/include/log_session/connection.h new file mode 100644 index 000000000..8dbf4703d --- /dev/null +++ b/base/include/log_session/connection.h @@ -0,0 +1,32 @@ +/* + * \brief Connection to LOG service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__LOG_SESSION__CONNECTION_H_ +#define _INCLUDE__LOG_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Log_connection : Connection, Log_session_client + { + Log_connection() + : + Connection(session("ram_quota=8K")), + Log_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__LOG_SESSION__CONNECTION_H_ */ diff --git a/base/include/log_session/log_session.h b/base/include/log_session/log_session.h new file mode 100644 index 000000000..da1762920 --- /dev/null +++ b/base/include/log_session/log_session.h @@ -0,0 +1,49 @@ +/* + * \brief Log text output session interface + * \author Norman Feske + * \date 2006-09-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__LOG_SESSION__LOG_SESSION_H_ +#define _INCLUDE__LOG_SESSION__LOG_SESSION_H_ + +#include +#include +#include +#include + +namespace Genode { + + struct Log_session : Session + { + static const char *service_name() { return "LOG"; } + + virtual ~Log_session() { } + + typedef Rpc_in_buffer<256> String; + + /** + * Output null-terminated string + * + * \return number of written characters + */ + virtual size_t write(String const &string) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_write, size_t, write, String const &); + GENODE_RPC_INTERFACE(Rpc_write); + }; +} + +#endif /* _INCLUDE__LOG_SESSION__LOG_SESSION_H_ */ diff --git a/base/include/pager/capability.h b/base/include/pager/capability.h new file mode 100644 index 000000000..c3b9781ed --- /dev/null +++ b/base/include/pager/capability.h @@ -0,0 +1,30 @@ +/* + * \brief Pager capability type + * \author Norman Feske + * \date 2010-01-27 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PAGER__CAPABILITY_H_ +#define _INCLUDE__PAGER__CAPABILITY_H_ + +#include + +namespace Genode { + + /* + * The 'Pager_capability' type is returned by 'Rm_session::add_client' and + * passed as argument to 'Cpu_session::set_pager'. It is never invoked or + * otherwise used. + */ + class Pager_object; + typedef Capability Pager_capability; +} + +#endif /* _INCLUDE__PAGER__CAPABILITY_H_ */ diff --git a/base/include/parent/capability.h b/base/include/parent/capability.h new file mode 100644 index 000000000..91dd7a220 --- /dev/null +++ b/base/include/parent/capability.h @@ -0,0 +1,22 @@ +/* + * \brief Parent capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PARENT__CAPABILITY_H_ +#define _INCLUDE__PARENT__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Parent_capability; } + +#endif /* _INCLUDE__PARENT__CAPABILITY_H_ */ diff --git a/base/include/parent/client.h b/base/include/parent/client.h new file mode 100644 index 000000000..1ec7f9653 --- /dev/null +++ b/base/include/parent/client.h @@ -0,0 +1,43 @@ +/* + * \brief Client-side parent interface + * \author Norman Feske + * \date 2006-05-10 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PARENT__CLIENT_H_ +#define _INCLUDE__PARENT__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Parent_client : Rpc_client + { + explicit Parent_client(Parent_capability parent) + : Rpc_client(parent) { } + + void exit(int exit_value) { call(exit_value); } + + void announce(Service_name const &service, Root_capability root) { + call(service, root); } + + Session_capability session(Service_name const &service, + Session_args const &args) { + return call(service, args); } + + void upgrade(Session_capability to_session, Upgrade_args const &args) { + call(to_session, args); } + + void close(Session_capability session) { call(session); } + }; +} + +#endif /* _INCLUDE__PARENT__CLIENT_H_ */ diff --git a/base/include/parent/parent.h b/base/include/parent/parent.h new file mode 100644 index 000000000..a74803ee0 --- /dev/null +++ b/base/include/parent/parent.h @@ -0,0 +1,152 @@ +/* + * \brief Parent interface + * \author Norman Feske + * \date 2006-05-10 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PARENT__PARENT_H_ +#define _INCLUDE__PARENT__PARENT_H_ + +#include +#include +#include +#include +#include + +namespace Genode { + + struct Parent + { + /********************* + ** Exception types ** + *********************/ + + class Exception : public ::Genode::Exception { }; + class Service_denied : public Exception { }; + class Quota_exceeded : public Exception { }; + class Unavailable : public Exception { }; + + typedef Rpc_in_buffer<64> Service_name; + typedef Rpc_in_buffer<160> Session_args; + typedef Rpc_in_buffer<160> Upgrade_args; + + + virtual ~Parent() { } + + /** + * Tell parent to exit the program + */ + virtual void exit(int exit_value) = 0; + + /** + * Announce service to the parent + */ + virtual void announce(Service_name const &service_name, + Root_capability service_root) = 0; + + /** + * Announce service to the parent + * + * \param service_root root capability + * + * The type of the specified 'service_root' capability match with + * an interface that provides a 'Session_type' type (i.e., a + * 'Typed_root' interface). This 'Session_type' is expected to + * host a static function called 'service_name' returning the + * name of the provided interface as null-terminated string. + */ + template + void announce(Capability const &service_root) + { + announce(ROOT_INTERFACE::Session_type::service_name(), service_root); + } + + /** + * Create session to a service + * + * \param service_name name of the requested interface + * \param args session constructor arguments + * + * \throw Service_denied parent denies session request + * \throw Quota_exceeded our own quota does not suffice for + * the creation of the new session + * \throw Unavailable + * + * \return untyped capability to new session + * + * The use of this function is discouraged. Please use the type safe + * 'session()' template instead. + */ + virtual Session_capability session(Service_name const &service_name, + Session_args const &args) = 0; + + /** + * Create session to a service + * + * \param SESSION_TYPE session interface type + * \param args session constructor arguments + * + * \throw Service_denied parent denies session request + * \throw Quota_exceeded our own quota does not suffice for + * the creation of the new session + * \throw Unavailable + * + * \return capability to new session + */ + template + Capability session(Session_args const &args) + { + Session_capability cap = session(SESSION_TYPE::service_name(), args); + return reinterpret_cap_cast(cap); + } + + /** + * Transfer our quota to the server that provides the specified session + * + * \param to_session recipient session + * \param args description of the amount of quota to transfer + * + * \throw Quota_exceeded quota could not be transferred + * + * The 'args' argument has the same principle format as the 'args' + * argument of the 'session' function. + * The error case indicates that there is not enough unused quota on + * the source side. + */ + virtual void upgrade(Session_capability to_session, + Upgrade_args const &args) = 0; + + /** + * Close session + */ + virtual void close(Session_capability session) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_exit, void, exit, int); + GENODE_RPC(Rpc_announce, void, announce, + Service_name const &, Root_capability); + GENODE_RPC_THROW(Rpc_session, Session_capability, session, + GENODE_TYPE_LIST(Service_denied, Quota_exceeded, Unavailable), + Service_name const &, Session_args const &); + GENODE_RPC_THROW(Rpc_upgrade, void, upgrade, + GENODE_TYPE_LIST(Quota_exceeded), + Session_capability, Upgrade_args const &); + GENODE_RPC(Rpc_close, void, close, Session_capability); + + GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session, Rpc_upgrade, + Rpc_close); + }; +} + +#endif /* _INCLUDE__PARENT__PARENT_H_ */ diff --git a/base/include/pd_session/capability.h b/base/include/pd_session/capability.h new file mode 100644 index 000000000..55dac2388 --- /dev/null +++ b/base/include/pd_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief PD-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PD_SESSION__CAPABILITY_H_ +#define _INCLUDE__PD_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Pd_session_capability; } + +#endif /* _INCLUDE__PD_SESSION__CAPABILITY_H_ */ diff --git a/base/include/pd_session/client.h b/base/include/pd_session/client.h new file mode 100644 index 000000000..3eb703063 --- /dev/null +++ b/base/include/pd_session/client.h @@ -0,0 +1,35 @@ +/* + * \brief Client-side pd session interface + * \author Christian Helmuth + * \date 2006-07-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PD_SESSION__CLIENT_H_ +#define _INCLUDE__PD_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Pd_session_client : Rpc_client + { + explicit Pd_session_client(Pd_session_capability session) + : Rpc_client(session) { } + + int bind_thread(Thread_capability thread) { + return call(thread); } + + int assign_parent(Parent_capability parent) { + return call(parent); } + }; +} + +#endif /* _INCLUDE__PD_SESSION__CLIENT_H_ */ diff --git a/base/include/pd_session/connection.h b/base/include/pd_session/connection.h new file mode 100644 index 000000000..999f37a02 --- /dev/null +++ b/base/include/pd_session/connection.h @@ -0,0 +1,41 @@ +/* + * \brief Connection to PD service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PD_SESSION__CONNECTION_H_ +#define _INCLUDE__PD_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Pd_connection : Connection, Pd_session_client + { + /** + * Constructor + * + * \param args additional session arguments + */ + Pd_connection(const char *args = 0) + : + Connection( + session("ram_quota=4K%s%s", + args ? ", " : "", + args ? args : "")), + + Pd_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__PD_SESSION__CONNECTION_H_ */ diff --git a/base/include/pd_session/pd_session.h b/base/include/pd_session/pd_session.h new file mode 100644 index 000000000..7c1c46fff --- /dev/null +++ b/base/include/pd_session/pd_session.h @@ -0,0 +1,63 @@ +/* + * \brief Protection domain (PD) session interface + * \author Christian Helmuth + * \date 2006-06-27 + * + * A pd session represents the protection domain of a program. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PD_SESSION__PD_SESSION_H_ +#define _INCLUDE__PD_SESSION__PD_SESSION_H_ + +#include +#include +#include + +namespace Genode { + + struct Pd_session : Session + { + static const char *service_name() { return "PD"; } + + virtual ~Pd_session() { } + + /** + * Bind thread to protection domain + * + * \param thread capability of thread to bind + * + * \return 0 on success or negative error code + * + * After successful bind, the thread will execute inside this + * protection domain when started. + */ + virtual int bind_thread(Thread_capability thread) = 0; + + /** + * Assign parent to protection domain + * + * \param parent capability of parent interface + * \return 0 on success, or negative error code + */ + virtual int assign_parent(Parent_capability parent) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_bind_thread, int, bind_thread, Thread_capability); + GENODE_RPC(Rpc_assign_parent, int, assign_parent, Parent_capability); + + GENODE_RPC_INTERFACE(Rpc_bind_thread, Rpc_assign_parent); + }; +} + +#endif /* _INCLUDE__PD_SESSION__PD_SESSION_H_ */ diff --git a/base/include/ram_session/capability.h b/base/include/ram_session/capability.h new file mode 100644 index 000000000..902b68599 --- /dev/null +++ b/base/include/ram_session/capability.h @@ -0,0 +1,29 @@ +/* + * \brief RAM-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RAM_SESSION__CAPABILITY_H_ +#define _INCLUDE__RAM_SESSION__CAPABILITY_H_ + +#include + +namespace Genode { + + /* + * We cannot include 'ram_session/ram_session.h' because this file relies + * on the the 'Ram_session_capability' type. + */ + class Ram_session; + typedef Capability Ram_session_capability; +} + +#endif /* _INCLUDE__RAM_SESSION__CAPABILITY_H_ */ diff --git a/base/include/ram_session/client.h b/base/include/ram_session/client.h new file mode 100644 index 000000000..55eaae1df --- /dev/null +++ b/base/include/ram_session/client.h @@ -0,0 +1,45 @@ +/* + * \brief Client-side ram session interface + * \author Norman Feske + * \date 2006-05-31 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RAM_SESSION__CLIENT_H_ +#define _INCLUDE__RAM_SESSION__CLIENT_H_ + +#include +#include +#include + +namespace Genode { + + struct Ram_session_client : Rpc_client + { + explicit Ram_session_client(Ram_session_capability session) + : Rpc_client(session) { } + + Ram_dataspace_capability alloc(size_t size) { + return call(size); } + + void free(Ram_dataspace_capability ds) { call(ds); } + + int ref_account(Ram_session_capability ram_session) { + return call(ram_session); } + + int transfer_quota(Ram_session_capability ram_session, size_t amount) { + return call(ram_session, amount); } + + size_t quota() { return call(); } + + size_t used() { return call(); } + }; +} + +#endif /* _INCLUDE__RAM_SESSION__CLIENT_H_ */ diff --git a/base/include/ram_session/connection.h b/base/include/ram_session/connection.h new file mode 100644 index 000000000..ad1c50c26 --- /dev/null +++ b/base/include/ram_session/connection.h @@ -0,0 +1,40 @@ +/* + * \brief Connection to RAM service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RAM_SESSION__CONNECTION_H_ +#define _INCLUDE__RAM_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Ram_connection : Connection, Ram_session_client + { + enum { RAM_QUOTA = 64*1024 }; + /** + * Constructor + * + * \param label session label + */ + Ram_connection(const char *label = "") + : + Connection( + session("ram_quota=64K, label=\"%s\"", label)), + + Ram_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__RAM_SESSION__CONNECTION_H_ */ diff --git a/base/include/ram_session/ram_session.h b/base/include/ram_session/ram_session.h new file mode 100644 index 000000000..12c092c4d --- /dev/null +++ b/base/include/ram_session/ram_session.h @@ -0,0 +1,127 @@ +/* + * \brief RAM session interface + * \author Norman Feske + * \date 2006-05-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RAM_SESSION__RAM_SESSION_H_ +#define _INCLUDE__RAM_SESSION__RAM_SESSION_H_ + +#include +#include +#include +#include +#include +#include + +namespace Genode { + + struct Ram_dataspace : Dataspace { }; + + typedef Capability Ram_dataspace_capability; + + struct Ram_session : Session + { + static const char *service_name() { return "RAM"; } + + + /********************* + ** Exception types ** + *********************/ + + class Alloc_failed : public Exception { }; + class Quota_exceeded : public Alloc_failed { }; + class Out_of_metadata : public Alloc_failed { }; + + /** + * Destructor + */ + virtual ~Ram_session() { } + + /** + * Allocate RAM dataspace + * + * \param size size of RAM dataspace + * + * \throw Quota_exceeded + * \throw Out_of_metadata + * \return capability to new RAM dataspace + */ + virtual Ram_dataspace_capability alloc(size_t size) = 0; + + /** + * Free RAM dataspace + * + * \param ds dataspace capability as returned by alloc + */ + virtual void free(Ram_dataspace_capability ds) = 0; + + /** + * Define reference account for the RAM session + * + * \param ram_session reference account + * + * \return 0 on success + * + * Each RAM session requires another RAM session as reference + * account to transfer quota to and from. The reference account can + * be defined only once. + */ + virtual int ref_account(Ram_session_capability ram_session) = 0; + + /** + * Transfer quota the another ram session + * + * \param ram_session receiver of quota donation + * \param amount amount of quota to donate + * \return 0 on success + * + * Quota can only be transfered if the specified RAM session is + * either the reference account for this session or vice versa. + */ + virtual int transfer_quota(Ram_session_capability ram_session, size_t amount) = 0; + + /** + * Return current quota limit + */ + virtual size_t quota() = 0; + + /** + * Return used quota + */ + virtual size_t used() = 0; + + /** + * Return amount of available quota + */ + size_t avail() + { + size_t q = quota(), u = used(); + return q > u ? q - u : 0; + } + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC_THROW(Rpc_alloc, Ram_dataspace_capability, alloc, + GENODE_TYPE_LIST(Quota_exceeded, Out_of_metadata), size_t); + GENODE_RPC(Rpc_free, void, free, Ram_dataspace_capability); + GENODE_RPC(Rpc_ref_account, int, ref_account, Ram_session_capability); + GENODE_RPC(Rpc_transfer_quota, int, transfer_quota, Ram_session_capability, size_t); + GENODE_RPC(Rpc_quota, size_t, quota); + GENODE_RPC(Rpc_used, size_t, used); + + GENODE_RPC_INTERFACE(Rpc_alloc, Rpc_free, Rpc_ref_account, + Rpc_transfer_quota, Rpc_quota, Rpc_used); + }; +} + +#endif /* _INCLUDE__RAM_SESSION__RAM_SESSION_H_ */ diff --git a/base/include/rm_session/capability.h b/base/include/rm_session/capability.h new file mode 100644 index 000000000..3b9bddf5f --- /dev/null +++ b/base/include/rm_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief RM-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RM_SESSION__CAPABILITY_H_ +#define _INCLUDE__RM_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Rm_session_capability; } + +#endif /* _INCLUDE__RM_SESSION__CAPABILITY_H_ */ diff --git a/base/include/rm_session/client.h b/base/include/rm_session/client.h new file mode 100644 index 000000000..730e5dd42 --- /dev/null +++ b/base/include/rm_session/client.h @@ -0,0 +1,51 @@ +/* + * \brief Client-side region manager session interface + * \author Christian Helmuth + * \date 2006-07-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RM_SESSION__CLIENT_H_ +#define _INCLUDE__RM_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Rm_session_client : Rpc_client + { + explicit Rm_session_client(Rm_session_capability session) + : Rpc_client(session) { } + + Local_addr attach(Dataspace_capability ds, size_t size, off_t offset, + bool use_local_addr, Local_addr local_addr) + { + return call(ds, size, offset, + use_local_addr, local_addr); + } + + void detach(Local_addr local_addr) { + call(local_addr); } + + Pager_capability add_client(Thread_capability thread) { + return call(thread); } + + void fault_handler(Signal_context_capability handler) { + call(handler); } + + State state() { + return call(); } + + Dataspace_capability dataspace() { + return call(); } + }; +} + +#endif /* _INCLUDE__RM_SESSION__CLIENT_H_ */ diff --git a/base/include/rm_session/connection.h b/base/include/rm_session/connection.h new file mode 100644 index 000000000..eee5ebc6c --- /dev/null +++ b/base/include/rm_session/connection.h @@ -0,0 +1,40 @@ +/* + * \brief Connection to RM service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RM_SESSION__CONNECTION_H_ +#define _INCLUDE__RM_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Rm_connection : Connection, Rm_session_client + { + enum { RAM_QUOTA = 64*1024 }; + + /** + * Constructor + * + * \param start start of the managed VM-region + * \param size size of the VM-region to manage + */ + Rm_connection(addr_t start = ~0UL, size_t size = 0) : + Connection( + session("ram_quota=64K, start=0x%p, size=0x%zx", + start, size)), + Rm_session_client(cap()) { } + }; +} + +#endif /* _INCLUDE__RM_SESSION__CONNECTION_H_ */ diff --git a/base/include/rm_session/rm_session.h b/base/include/rm_session/rm_session.h new file mode 100644 index 000000000..39ca44e24 --- /dev/null +++ b/base/include/rm_session/rm_session.h @@ -0,0 +1,196 @@ +/* + * \brief Region manager session interface + * \author Norman Feske + * \date 2006-05-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__RM_SESSION__RM_SESSION_H_ +#define _INCLUDE__RM_SESSION__RM_SESSION_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + struct Rm_session : Session + { + enum Fault_type { + READY = 0, READ_FAULT = 1, WRITE_FAULT = 2, EXEC_FAULT = 3 }; + + /** + * State of region-manager session + * + * If a client accesses a location outside the regions attached to + * the region-manager session, a fault occurs and gets signalled to + * the registered fault handler. The fault handler, in turn needs + * the information about the fault address and fault type to + * resolve the fault. This information is represented by this + * structure. + */ + struct State + { + /** + * Type of occurred fault + */ + Fault_type type; + + /** + * Fault address + */ + addr_t addr; + + /** + * Default constructor + */ + State() : type(READY), addr(0) { } + + /** + * Constructor + */ + State(Fault_type fault_type, addr_t fault_addr) : + type(fault_type), addr(fault_addr) { } + }; + + + /** + * Helper for tranferring the bit representation of a pointer as RPC + * argument. + */ + class Local_addr + { + private: + + void *_ptr; + + public: + + template + Local_addr(T ptr) : _ptr((void *)ptr) { } + + Local_addr() : _ptr(0) { } + + template + operator T () { return (T)_ptr; } + }; + + + static const char *service_name() { return "RM"; } + + + /********************* + ** Exception types ** + *********************/ + + class Attach_failed : public Exception { }; + class Invalid_args : public Attach_failed { }; + class Invalid_dataspace : public Attach_failed { }; + class Region_conflict : public Attach_failed { }; + class Out_of_metadata : public Attach_failed { }; + + class Invalid_thread : public Exception { }; + class Out_of_memory : public Exception { }; + + /** + * Destructor + */ + virtual ~Rm_session() { } + + /** + * Map dataspace into local address space + * + * \param ds capability of dataspace to map + * \param size size of the locally mapped region + * default (0) is the whole dataspace + * \param offset start at offset in dataspace (page-aligned) + * \param use_local_addr if set to true, attach the dataspace at + * the specified 'local_addr' + * \param local_addr local destination address + * + * \throw Attach_failed if dataspace or offset is invalid, + * or on region conflict + * \throw Out_of_metadata if meta-data backing store is exhausted + * + * \return local address of mapped dataspace + * + */ + virtual Local_addr attach(Dataspace_capability ds, + size_t size = 0, off_t offset = 0, + bool use_local_addr = false, + Local_addr local_addr = (addr_t)0) = 0; + + /** + * Shortcut for attaching a dataspace at a predefined local address + */ + Local_addr attach_at(Dataspace_capability ds, addr_t local_addr, + size_t size = 0, off_t offset = 0) { + return attach(ds, size, offset, true, local_addr); } + + /** + * Remove region from local address space + */ + virtual void detach(Local_addr local_addr) = 0; + + /** + * Add client to pager + * + * \param thread thread that will be paged + * \throw Invalid_thread + * \throw Out_of_memory + * \return capability to be used for handling page faults + * + * This method must be called at least once to establish a valid + * communication channel between the pager part of the region manager + * and the client thread. + */ + virtual Pager_capability add_client(Thread_capability thread) = 0; + + /** + * Register signal handler for region-manager faults + */ + virtual void fault_handler(Signal_context_capability handler) = 0; + + /** + * Request current state of RM session + */ + virtual State state() = 0; + + /** + * Return dataspace representation of region-manager session + */ + virtual Dataspace_capability dataspace() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC_THROW(Rpc_attach, Local_addr, attach, + GENODE_TYPE_LIST(Invalid_dataspace, Region_conflict, + Out_of_metadata, Invalid_args), + Dataspace_capability, size_t, off_t, bool, Local_addr); + GENODE_RPC(Rpc_detach, void, detach, Local_addr); + GENODE_RPC_THROW(Rpc_add_client, Pager_capability, add_client, + GENODE_TYPE_LIST(Invalid_thread, Out_of_memory), + Thread_capability); + GENODE_RPC(Rpc_fault_handler, void, fault_handler, Signal_context_capability); + GENODE_RPC(Rpc_state, State, state); + GENODE_RPC(Rpc_dataspace, Dataspace_capability, dataspace); + + GENODE_RPC_INTERFACE(Rpc_attach, Rpc_detach, Rpc_add_client, + Rpc_fault_handler, Rpc_state, Rpc_dataspace); + }; +} + +#endif /* _INCLUDE__RM_SESSION__RM_SESSION_H_ */ diff --git a/base/include/rom_session/capability.h b/base/include/rom_session/capability.h new file mode 100644 index 000000000..34ab8a113 --- /dev/null +++ b/base/include/rom_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief ROM-session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROM_SESSION__CAPABILITY_H_ +#define _INCLUDE__ROM_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Rom_session_capability; } + +#endif /* _INCLUDE__ROM_SESSION__CAPABILITY_H_ */ diff --git a/base/include/rom_session/client.h b/base/include/rom_session/client.h new file mode 100644 index 000000000..e80604ff7 --- /dev/null +++ b/base/include/rom_session/client.h @@ -0,0 +1,32 @@ +/* + * \brief Client-side ROM session interface + * \author Norman Feske + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROM_SESSION__CLIENT_H_ +#define _INCLUDE__ROM_SESSION__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Rom_session_client : Rpc_client + { + explicit Rom_session_client(Rom_session_capability session) + : Rpc_client(session) { } + + Rom_dataspace_capability dataspace() { + return call(); } + }; +} + +#endif /* _INCLUDE__ROM_SESSION__CLIENT_H_ */ diff --git a/base/include/rom_session/connection.h b/base/include/rom_session/connection.h new file mode 100644 index 000000000..d872597f6 --- /dev/null +++ b/base/include/rom_session/connection.h @@ -0,0 +1,60 @@ +/* + * \brief Connection to ROM file service + * \author Norman Feske + * \date 2008-08-22 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROM_SESSION__CONNECTION_H_ +#define _INCLUDE__ROM_SESSION__CONNECTION_H_ + +#include +#include +#include + +namespace Genode { + + class Rom_connection : public Connection, + public Rom_session_client + { + public: + + class Rom_connection_failed : public Parent::Exception { }; + + private: + + Rom_session_capability _create_session(const char *filename, const char *label) + { + try { + return session("ram_quota=4K, filename=\"%s\", label=%s", + filename, label); } + catch (...) { + PERR("Could not open file \"%s\"", filename); + throw Rom_connection_failed(); + } + } + + public: + + /** + * Constructor + * + * \param filename name of ROM file + * \param label initial session label + * + * \throw Rom_connection_failed + */ + Rom_connection(const char *filename, const char *label = "") : + Connection(_create_session(filename, label)), + Rom_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__ROM_SESSION__CONNECTION_H_ */ diff --git a/base/include/rom_session/rom_session.h b/base/include/rom_session/rom_session.h new file mode 100644 index 000000000..505631e24 --- /dev/null +++ b/base/include/rom_session/rom_session.h @@ -0,0 +1,55 @@ +/* + * \brief ROM session interface + * \author Norman Feske + * \date 2006-07-06 + * + * A ROM session corresponds to an open file. The file name is specified as an + * argument on session creation. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROM_SESSION__ROM_SESSION_H_ +#define _INCLUDE__ROM_SESSION__ROM_SESSION_H_ + +#include +#include + +namespace Genode { + + struct Rom_dataspace : Dataspace { }; + + typedef Capability Rom_dataspace_capability; + + struct Rom_session : Session + { + static const char *service_name() { return "ROM"; } + + virtual ~Rom_session() { } + + /** + * Request dataspace containing the ROM session data + * + * \return capability to ROM dataspace + * + * The capability may be invalid. + */ + virtual Rom_dataspace_capability dataspace() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_dataspace, Rom_dataspace_capability, dataspace); + + GENODE_RPC_INTERFACE(Rpc_dataspace); + }; +} + +#endif /* _INCLUDE__ROM_SESSION__ROM_SESSION_H_ */ diff --git a/base/include/root/capability.h b/base/include/root/capability.h new file mode 100644 index 000000000..da0677a7b --- /dev/null +++ b/base/include/root/capability.h @@ -0,0 +1,22 @@ +/* + * \brief Root capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROOT__CAPABILITY_H_ +#define _INCLUDE__ROOT__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Root_capability; } + +#endif /* _INCLUDE__ROOT__CAPABILITY_H_ */ diff --git a/base/include/root/client.h b/base/include/root/client.h new file mode 100644 index 000000000..eb9d12fe5 --- /dev/null +++ b/base/include/root/client.h @@ -0,0 +1,38 @@ +/* + * \brief Root client interface + * \author Norman Feske + * \date 2006-05-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROOT__CLIENT_H_ +#define _INCLUDE__ROOT__CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Root_client : Rpc_client + { + explicit Root_client(Root_capability root) + : Rpc_client(root) { } + + Session_capability session(Session_args const &args) { + return call(args); } + + void upgrade(Session_capability session, Upgrade_args const &args) { + call(session, args); } + + void close(Session_capability session) { + call(session); } + }; +} + +#endif /* _INCLUDE__ROOT__CLIENT_H_ */ diff --git a/base/include/root/component.h b/base/include/root/component.h new file mode 100644 index 000000000..0cd6beb15 --- /dev/null +++ b/base/include/root/component.h @@ -0,0 +1,250 @@ +/* + * \brief Generic root component implementation + * \author Norman Feske + * \date 2006-05-22 + * + * This class is there for your convenience. It performs the common actions + * that must always be taken when creating a new session. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROOT__COMPONENT_H_ +#define _INCLUDE__ROOT__COMPONENT_H_ + +#include +#include +#include +#include +#include +#include + +namespace Genode { + + /** + * Session creation policy for a single-client service + */ + class Single_client + { + private: + + bool _used; + + public: + + Single_client() : _used(0) { } + + void aquire(const char *args) + { + if (_used) + throw Root::Unavailable(); + + _used = true; + } + + void release() { _used = false; } + }; + + + /** + * Session-creation policy for a multi-client service + */ + struct Multiple_clients + { + void aquire(const char *args) { } + void release() { } + }; + + + /** + * Template for implementing the root interface + * + * \param SESSION_TYPE session-component type to manage, + * derived from 'Rpc_object' + * \param POLICY session-creation policy + * + * The 'POLICY' template parameter allows for constraining the session + * creation to only one instance at a time (using the 'Single_session' + * policy) or multiple instances (using the 'Multiple_sessions' policy). + * + * The 'POLICY' class must provide the following two functions: + * + * :'aquire(const char *args)': is called with the session arguments + * at creation time of each new session. It can therefore implement + * a session-creation policy taking session arguments into account. + * If the policy denies the creation of a new session, it throws + * one of the exceptions defined in the 'Root' interface. + * + * :'release': is called at the destruction time of a session. It enables + * the policy to keep track of and impose restrictions on the number + * of existing sessions. + * + * The default policy 'Multiple_clients' imposes no restrictions on the + * creation of new sessions. + */ + template + class Root_component : public Rpc_object >, private POLICY + { + private: + + /* + * Entry point that manages the session objects + * created by this root interface + */ + Rpc_entrypoint *_ep; + + /* + * Allocator for allocating session objects. + * This allocator must be used by the derived + * class when calling the 'new' operator for + * creating a new session. + */ + Allocator *_md_alloc; + + protected: + + /** + * Create new session (to be implemented by a derived class) + * + * Only a derived class knows the constructor arguments of + * a specific session. Therefore, we cannot unify the call + * of its 'new' operator and must implement the session + * creation at a place, where the required knowledge exist. + * + * In the implementation of this function, the heap, provided + * by 'Root_component' must be used for allocating the session + * object. + * + * \throw Allocator::Out_of_memory typically caused by the + * meta-data allocator + * \throw Root::Invalid_args typically caused by the + * session-component constructor + */ + virtual SESSION_TYPE *_create_session(const char *args) = 0; + + /** + * Inform session about a quota upgrade + * + * Once a session is created, its client can successively extend + * its quota donation via the 'Parent::transfer_quota' function. + * This will result in the invokation of 'Root::upgrade' at the + * root interface the session was created with. The root interface, + * in turn, informs the session about the new resources via the + * '_upgrade_session' function. The default implementation is + * suited for sessions that use a static amount of resources + * accounted for at session-creation time. For such sessions, an + * upgrade is not useful. However, sessions that dynamically + * allocate resources on behalf of its client, should respond to + * quota upgrades by implementing this function. + * + * \param session session to upgrade + * \param args description of additional resources in the + * same format as used at session creation + */ + virtual void _upgrade_session(SESSION_TYPE *session, const char *args) { } + + virtual void _destroy_session(SESSION_TYPE *session) { + destroy(_md_alloc, session); } + + /** + * Return allocator to allocate server object in '_create_session()' + */ + Allocator *md_alloc() { return _md_alloc; } + Rpc_entrypoint *ep() { return _ep; } + + public: + + /** + * Constructor + * + * \param ep entry point that manages the sessions of this + * root interface. + * \param ram_session provider of dataspaces for the backing store + * of session objects and session data + */ + Root_component(Rpc_entrypoint *ep, Allocator *metadata_alloc) + : _ep(ep), _md_alloc(metadata_alloc) { } + + + /******************** + ** Root interface ** + ********************/ + + Session_capability session(Root::Session_args const &args) + { + if (!args.is_valid_string()) throw Root::Invalid_args(); + + POLICY::aquire(args.string()); + + /* + * We need to decrease 'ram_quota' by + * the size of the session object. + */ + size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").long_value(0); + size_t const remaining_ram_quota = ram_quota - sizeof(SESSION_TYPE) - + md_alloc()->overhead(sizeof(SESSION_TYPE)); + if (remaining_ram_quota < 0) { + PERR("Insufficient ram quota, provided=%zd, required=%zd", + ram_quota, sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE))); + throw Root::Quota_exceeded(); + } + + /* + * Deduce ram quota needed for allocating the session object from the + * donated ram quota. + * + * XXX the size of the 'adjusted_args' buffer should dependent + * on the message-buffer size and stack size. + */ + enum { MAX_ARGS_LEN = 256 }; + char adjusted_args[MAX_ARGS_LEN]; + strncpy(adjusted_args, args.string(), sizeof(adjusted_args)); + char ram_quota_buf[64]; + snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%zd", + remaining_ram_quota); + Arg_string::set_arg(adjusted_args, sizeof(adjusted_args), + "ram_quota", ram_quota_buf); + + SESSION_TYPE *s = 0; + try { s = _create_session(adjusted_args); } + catch (Allocator::Out_of_memory) { throw Root::Quota_exceeded(); } + + return _ep->manage(s); + } + + void upgrade(Session_capability session, Root::Upgrade_args const &args) + { + if (!args.is_valid_string()) throw Root::Invalid_args(); + + SESSION_TYPE *s = + dynamic_cast(_ep->obj_by_cap(session)); + + if (!s) return; + + _upgrade_session(s, args.string()); + } + + void close(Session_capability session) + { + SESSION_TYPE *s = + dynamic_cast(_ep->obj_by_cap(session)); + + if (!s) return; + + /* let the entry point forget the session object */ + _ep->dissolve(s); + + _destroy_session(s); + + POLICY::release(); + return; + } + }; +} + +#endif /* _INCLUDE__ROOT__COMPONENT_H_ */ diff --git a/base/include/root/root.h b/base/include/root/root.h new file mode 100644 index 000000000..51a059fd2 --- /dev/null +++ b/base/include/root/root.h @@ -0,0 +1,93 @@ +/* + * \brief Root interface + * \author Norman Feske + * \date 2006-05-11 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__ROOT__ROOT_H_ +#define _INCLUDE__ROOT__ROOT_H_ + +#include +#include +#include +#include + +namespace Genode { + + struct Root + { + /********************* + ** Exception types ** + *********************/ + + class Exception : public ::Genode::Exception { }; + class Unavailable : public Exception { }; + class Quota_exceeded : public Exception { }; + class Invalid_args : public Exception { }; + + typedef Rpc_in_buffer<160> Session_args; + typedef Rpc_in_buffer<160> Upgrade_args; + + virtual ~Root() { } + + /** + * Create session + * + * \throw Unavailable + * \throw Quota_exceeded + * \throw Invalid_args + * + * \return capability to new session + */ + virtual Session_capability session(Session_args const &args) = 0; + + /** + * Extend resource donation to an existing session + */ + virtual void upgrade(Session_capability session, Upgrade_args const &args) = 0; + + /** + * Close session + */ + virtual void close(Session_capability session) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC_THROW(Rpc_session, Session_capability, session, + GENODE_TYPE_LIST(Unavailable, Quota_exceeded, Invalid_args), + Session_args const &); + GENODE_RPC_THROW(Rpc_upgrade, void, upgrade, + GENODE_TYPE_LIST(Invalid_args), + Session_capability, Upgrade_args const &); + GENODE_RPC(Rpc_close, void, close, Session_capability); + + GENODE_RPC_INTERFACE(Rpc_session, Rpc_upgrade, Rpc_close); + }; + + + /** + * Root interface supplemented with information about the managed + * session type + * + * This class template is used to automatically propagate the + * correct session type to 'Parent::announce()' when announcing + * a service. + */ + template + struct Typed_root : Root + { + typedef SESSION_TYPE Session_type; + }; +} + +#endif /* _INCLUDE__ROOT__ROOT_H_ */ diff --git a/base/include/session/capability.h b/base/include/session/capability.h new file mode 100644 index 000000000..0af51e6b9 --- /dev/null +++ b/base/include/session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief Session capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SESSION__CAPABILITY_H_ +#define _INCLUDE__SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { typedef Capability Session_capability; } + +#endif /* _INCLUDE__SESSION__CAPABILITY_H_ */ diff --git a/base/include/session/session.h b/base/include/session/session.h new file mode 100644 index 000000000..e8fb2db79 --- /dev/null +++ b/base/include/session/session.h @@ -0,0 +1,38 @@ +/* + * \brief Session + * \author Norman Feske + * \date 2011-05-15 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SESSION_H_ +#define _INCLUDE__SESSION_H_ + +/* + * Each session interface declares an RPC interface and, therefore, relies on + * the RPC framework. By including 'base/rpc.h' here, we relieve the interfaces + * from including 'base/rpc.h' in addition to 'session/session.h'. + */ +#include + +namespace Genode { + + /** + * Base class of session interfaces + * + * Each session interface must implement the function 'service_name' + * ! static const char *service_name(); + * This function returns the name of the service provided via the session + * interface. + */ + class Session { }; +} + + +#endif /* _INCLUDE__SESSION_H_ */ diff --git a/base/include/signal_session/capability.h b/base/include/signal_session/capability.h new file mode 100644 index 000000000..7372c8257 --- /dev/null +++ b/base/include/signal_session/capability.h @@ -0,0 +1,25 @@ +/* + * \brief Signal-session capability type + * \author Norman Feske + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__CAPABILITY_H_ +#define _INCLUDE__SIGNAL_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Genode { + + typedef Capability Signal_session_capability; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__CAPABILITY_H_ */ diff --git a/base/include/signal_session/client.h b/base/include/signal_session/client.h new file mode 100644 index 000000000..ea6fb7c82 --- /dev/null +++ b/base/include/signal_session/client.h @@ -0,0 +1,43 @@ +/* + * \brief Client-side signal session interface + * \author Norman Feske + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__CLIENT_H_ +#define _INCLUDE__SIGNAL_SESSION__CLIENT_H_ + +#include +#include +#include +#include + +namespace Genode { + + struct Signal_session_client : Rpc_client + { + explicit Signal_session_client(Signal_session_capability session) + : Rpc_client(session) { } + + Signal_source_capability signal_source() { + return call(); } + + Signal_context_capability alloc_context(long imprint) { + return call(imprint); } + + void free_context(Signal_context_capability cap) { + call(cap); } + + void submit(Signal_context_capability receiver, unsigned cnt = 1) { + call(receiver, cnt); } + }; +} + +#endif /* _INCLUDE__CAP_SESSION__CLIENT_H_ */ diff --git a/base/include/signal_session/connection.h b/base/include/signal_session/connection.h new file mode 100644 index 000000000..fe3d9fa6e --- /dev/null +++ b/base/include/signal_session/connection.h @@ -0,0 +1,32 @@ +/* + * \brief Connection to signal service + * \author Norman Feske + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__CONNECTION_H_ +#define _INCLUDE__SIGNAL_SESSION__CONNECTION_H_ + +#include +#include + +namespace Genode { + + struct Signal_connection : Connection, Signal_session_client + { + Signal_connection() + : + Connection(session("ram_quota=8K")), + Signal_session_client(cap()) + { } + }; +} + +#endif /* _INCLUDE__CAP_SESSION__CONNECTION_H_ */ diff --git a/base/include/signal_session/signal_session.h b/base/include/signal_session/signal_session.h new file mode 100644 index 000000000..0179bd793 --- /dev/null +++ b/base/include/signal_session/signal_session.h @@ -0,0 +1,96 @@ +/* + * \brief Signal session interface + * \author Norman Feske + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SIGNAL_SESSION_H_ +#define _INCLUDE__SIGNAL_SESSION__SIGNAL_SESSION_H_ + +#include +#include +#include +#include + +namespace Genode { + + class Signal_context; + + + typedef Capability Signal_context_capability; + typedef Capability Signal_source_capability; + + + struct Signal_session : Session + { + static const char *service_name() { return "SIGNAL"; } + + virtual ~Signal_session() { } + + class Out_of_metadata : public Exception { }; + + /** + * Request capability for the signal-source interface + */ + virtual Signal_source_capability signal_source() = 0; + + /** + * Allocate signal context + * + * \param imprint opaque value that gets delivered with signals + * originating from the allocated signal-context + * capability + * \return new signal-context capability + * \throw Out_of_metadata + */ + virtual Signal_context_capability alloc_context(long imprint) = 0; + + /** + * Free signal-context + * + * \param cap capability of signal-context to release + */ + virtual void free_context(Signal_context_capability cap) = 0; + + /** + * Submit signals to the specified signal context + * + * \param context signal destination + * \param cnt number of signals to submit at once + * + * Note that the 'context' argument does not necessarily belong to + * the signal session. Normally, it is a capability obtained from + * a potentially untrusted source. Because we cannot trust this + * capability, signals are not submitted by invoking 'cap' directly + * but by using it as argument to our trusted signal-session + * interface. Otherwise, a potential signal receiver could supply + * a capability with a blocking interface to compromise the + * nonblocking behaviour of the submit function. + */ + virtual void submit(Signal_context_capability context, + unsigned cnt = 1) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_signal_source, Signal_source_capability, signal_source); + GENODE_RPC_THROW(Rpc_alloc_context, Signal_context_capability, alloc_context, + GENODE_TYPE_LIST(Out_of_metadata), long); + GENODE_RPC(Rpc_free_context, void, free_context, Signal_context_capability); + GENODE_RPC(Rpc_submit, void, submit, Signal_context_capability, unsigned); + + GENODE_RPC_INTERFACE(Rpc_submit, Rpc_signal_source, Rpc_alloc_context, + Rpc_free_context); + }; +} + +#endif /* _INCLUDE__CAP_SESSION__CAP_SESSION_H_ */ diff --git a/base/include/signal_session/source.h b/base/include/signal_session/source.h new file mode 100644 index 000000000..e3965c0f3 --- /dev/null +++ b/base/include/signal_session/source.h @@ -0,0 +1,75 @@ +/* + * \brief Signal-source interface + * \author Norman Feske + * \date 2010-02-03 + * + * This file is only included by 'signal_session/signal_session.h' and relies + * on the headers included there. No include guards are needed. It is a + * separate header file to make it easily replaceable by a platform-specific + * implementation. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SOURCE_H_ +#define _INCLUDE__SIGNAL_SESSION__SOURCE_H_ + +#include + +namespace Genode { + + /** + * Blocking part of the signal-session interface + * + * The blocking 'wait_for_signal()' function cannot be part of the + * signal-session interface because otherwise, context allocations or + * signal submissions would not be possible while blocking for signals. + * Therefore, the blocking part is implemented a separate interface, + * which can be used by an independent thread. + */ + struct Signal_source + { + class Signal + { + private: + + long _imprint; + int _num; + + public: + + Signal(long imprint, int num) : + _imprint(imprint), + _num(num) + { } + + Signal() : _imprint(0), _num(0) { } + + long imprint() { return _imprint; } + + int num() { return _num; } + }; + + virtual ~Signal_source() { } + + /** + * Wait for signal + */ + virtual Signal wait_for_signal() = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_wait_for_signal, Signal, wait_for_signal); + GENODE_RPC_INTERFACE(Rpc_wait_for_signal); + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SOURCE_H_ */ diff --git a/base/include/signal_session/source_client.h b/base/include/signal_session/source_client.h new file mode 100644 index 000000000..4527a9385 --- /dev/null +++ b/base/include/signal_session/source_client.h @@ -0,0 +1,33 @@ +/* + * \brief Signal-source client interface + * \author Norman Feske + * \date 2010-02-03 + * + * See documentation in 'signal_session/source.h'. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ +#define _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ + +#include +#include + +namespace Genode { + + struct Signal_source_client : Rpc_client + { + Signal_source_client(Signal_source_capability signal_source) + : Rpc_client(signal_source) { } + + Signal wait_for_signal() { return call(); } + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SOURCE_CLIENT_H_ */ diff --git a/base/include/signal_session/source_rpc_object.h b/base/include/signal_session/source_rpc_object.h new file mode 100644 index 000000000..db2dc3537 --- /dev/null +++ b/base/include/signal_session/source_rpc_object.h @@ -0,0 +1,28 @@ +/* + * \brief Server-side signal-source interface + * \author Norman Feske + * \date 2011-04-12 + * + * This class solely exists as a hook to insert platform-specific + * implementation bits (i.e., for the NOVA base platform, there exists + * an enriched version of this class). + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__SIGNAL_SESSION__SOURCE_RPC_OBJECT_H_ +#define _INCLUDE__SIGNAL_SESSION__SOURCE_RPC_OBJECT_H_ + +#include +#include + +namespace Genode { + struct Signal_source_rpc_object : Rpc_object { }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SOURCE_RPC_OBJECT_H_ */ diff --git a/base/include/thread/capability.h b/base/include/thread/capability.h new file mode 100644 index 000000000..ed169718c --- /dev/null +++ b/base/include/thread/capability.h @@ -0,0 +1,29 @@ +/* + * \brief Thread capability type + * \author Norman Feske + * \date 2008-08-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__THREAD__CAPABILITY_H_ +#define _INCLUDE__THREAD__CAPABILITY_H_ + +#include + +namespace Genode { + + /* + * The 'Thread_capability' type is created by the CPU session. + * Hence, we use the CPU session's 'Cpu_thread' as association. + */ + class Cpu_thread; + typedef Capability Thread_capability; +} + +#endif /* _INCLUDE__THREAD__CAPABILITY_H_ */ diff --git a/base/include/util/arg_string.h b/base/include/util/arg_string.h new file mode 100644 index 000000000..384b8c78a --- /dev/null +++ b/base/include/util/arg_string.h @@ -0,0 +1,325 @@ +/* + * \brief Argument list string handling + * \author Norman Feske + * \date 2006-05-22 + * + * Each argument has the form: + * + * ! = + * + * Key is an identifier that begins with a letter or underline + * and may also contain digits. + * + * A list of arguments is specified by using comma as separator, + * whereas the first argument is considered as the weakest. If + * we replace an argument value of an existing argument, the + * existing argument is removed and we append a new argument at + * the end of the string. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__ARG_STRING_H_ +#define _INCLUDE__UTIL__ARG_STRING_H_ + +#include +#include +#include + +namespace Genode { + + class Arg_string; + + class Arg + { + /** + * Define tokenizer used for argument-string parsing + * + * Argument-string tokens accept C-style identifiers. + */ + typedef ::Genode::Token Token; + + friend class Arg_string; + + private: + + Token _key; + Token _value; + + /** + * Return long value of argument + * + * \param out_value argument converted to unsigned long value + * \param out_sign 1 if positive; -1 if negative + * \return true if no syntactic anomaly occured + * + * This function handles the numberic modifiers G (2^30), + * M (2^20), and K (2^10). + */ + bool read_ulong(unsigned long *out_value, int *out_sign) const + { + Token t = _value; + + /* check for sign; default is positive */ + *out_sign = 1; + if (t[0] == '+') + t = t.next(); + else if (t[0] == '-') { + *out_sign = -1; + t = t.next(); + } + + /* stop if token after sign is no number */ + if (t.type() != Token::NUMBER) + return false; + + /* read numeric value and skip the corresponding tokens */ + Number_of_bytes value; + size_t n = ascii_to(t.start(), &value); + + if (n == 0) + return false; + + t = Token(t.start() + n); + *out_value = value; + + /* check for strange characters at the end of the number */ + t = t.eat_whitespace(); + if (t && (t[0] != ',')) return false; + + return true; + } + + public: + + /** + * Construct argument from Token(s) + */ + Arg(Token t = Token()) : _key(t), _value(0) + { + for (; t && (t[0] != ','); t = t.next().eat_whitespace()) + if (t[0] == '=') { + _value = t.next().eat_whitespace(); + break; + } + } + + inline bool valid() const { return _key; } + + unsigned long ulong_value(unsigned long default_value) const + { + unsigned long value = 0; + int sign = 1; + + bool valid = read_ulong(&value, &sign); + if (sign < 0) + return default_value; + + return valid ? value : default_value; + } + + long long_value(long default_value) const + { + unsigned long value = 0; + int sign = 1; + + bool valid = read_ulong(&value, &sign); + + /* FIXME we should check for overflows here! */ + return valid ? sign*value : default_value; + } + + bool bool_value(bool default_value) const + { + /* check for known idents */ + if (_value.type() == Token::IDENT) { + char *p = _value.start(); + size_t l = _value.len(); + + if (!strcmp(p, "yes", l)) return true; + if (!strcmp(p, "true", l)) return true; + if (!strcmp(p, "on", l)) return true; + + if (!strcmp(p, "no", l)) return false; + if (!strcmp(p, "false", l)) return false; + if (!strcmp(p, "off", l)) return false; + + /* saxony mode ;) */ + if (!strcmp(p, "nu", l)) return true; + if (!strcmp(p, "nee", l)) return false; + + return default_value; + } + + /* read values 0 (false) / !0 (true) */ + unsigned long value; + int sign; + bool valid = read_ulong(&value, &sign); + + return valid ? value : default_value; + } + + void key(char *dst, size_t dst_len) const + { + _key.string(dst, dst_len); + } + + void string(char *dst, size_t dst_len, const char *default_string) const + { + /* check for one-word string w/o quotes */ + if (_value.type() == Token::IDENT) { + size_t len = min(dst_len - 1, _value.len()); + memcpy(dst, _value.start(), len); + dst[len] = 0; + return; + } + + /* stop here if _value is not a string */ + if (_value.type() != Token::STRING) { + strncpy(dst, default_string, dst_len); + return; + } + + /* unpack string to dst */ + size_t num_chars = min(dst_len - 1, _value.len()); + unpack_string(_value.start(), dst, num_chars); + } + }; + + + class Arg_string + { + typedef Arg::Token Token; + + private: + + static Token _next_key(Token t) + { + for (; t; t = t.next().eat_whitespace()) + + /* if we find a comma, return token after comma */ + if (t[0] == ',') return t.next().eat_whitespace(); + + return Token(); + } + + /** + * Find key token in argument string + */ + static Token _find_key(const char *args, const char *key) + { + for (Token t(args); t; t = _next_key(t)) + + /* check if key matches */ + if ((t.type() == Token::IDENT) && !strcmp(key, t.start(), t.len())) + return t; + + return Token(); + } + + /** + * Append source string to destination string + * + * NOTE: check string length before calling this function! + * + * \return last character of result string + */ + static char *_append(char *dst, const char *src) + { + unsigned src_len = strlen(src); + while (*dst) dst++; + memcpy(dst, src, src_len + 1); + return dst + src_len; + } + + public: + + /** + * Find argument by its key + */ + static Arg find_arg(const char *args, const char *key) { + return (args && key) ? Arg(_find_key(args, key)) : Arg(); } + + static Arg first_arg(const char *args) { + return Arg(Token(args)); } + + /** + * Remove argument with the specified key + */ + static bool remove_arg(char *args, const char *key) + { + if (!args || !key) return false; + + Token beg = _find_key(args, key); + Token next = _next_key(beg); + + /* no such key to remove - we are done */ + if (!beg) return true; + + /* if argument is the last one, null-terminate string right here */ + if (!next) { + + /* eat all pending whitespaces at the end of the string */ + char *s = max(beg.start() - 1, args); + while (s > args && (*s == ' ')) s--; + + /* write string-terminating zero */ + *s = 0; + } else + memcpy(beg.start(), next.start(), strlen(next.start()) + 1); + + return true; + } + + /** + * Add new argument + */ + static bool add_arg(char *args, unsigned args_len, + const char *key, const char *value) + { + if (!args || !key || !value) return false; + + unsigned old_len = strlen(args); + + /* check if args string has enough capacity */ + if (old_len + strlen(key) + strlen(value) + 2 > args_len) + return false; + + args += old_len; + + if (old_len) + args = _append(args, ", "); + + _append(_append(_append(args, key), "="), value); + return true; + } + + /** + * Assign new value to argument + */ + static bool set_arg(char *args, unsigned args_len, + const char *key, const char *value) + { + return remove_arg(args, key) && add_arg(args, args_len, key, value); + } + + /** + * Assign new integer argument + */ + static bool set_arg(char *args, unsigned args_len, + const char *key, int value) + { + enum { STRING_LONG_MAX = 32 }; + char buf[STRING_LONG_MAX]; + snprintf(buf, sizeof(buf), "%d", value); + return remove_arg(args, key) && add_arg(args, args_len, key, buf); + } + }; +} + +#endif /* _INCLUDE__UTIL__ARG_STRING_H_ */ diff --git a/base/include/util/avl_string.h b/base/include/util/avl_string.h new file mode 100644 index 000000000..753766b20 --- /dev/null +++ b/base/include/util/avl_string.h @@ -0,0 +1,77 @@ +/* + * \brief Utility for handling strings as AVL-node keys + * \author Norman Feske + * \date 2006-07-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__AVL_STRING_H_ +#define _INCLUDE__UTIL__AVL_STRING_H_ + +#include +#include + +namespace Genode { + + class Avl_string_base : public Avl_node + { + private: + + const char *_str; + + protected: + + Avl_string_base(const char *str) : _str(str) { } + + public: + + const char *name() const { return _str; } + + + /************************ + ** Avl node interface ** + ************************/ + + bool higher(Avl_string_base *c) { return (strcmp(c->_str, _str) > 0); } + + /** + * Find by name + */ + Avl_string_base *find_by_name(const char *name) + { + if (strcmp(name, _str) == 0) return this; + + Avl_string_base *c = Avl_node::child(strcmp(name, _str) > 0); + return c ? c->find_by_name(name) : 0; + } + }; + + + /* + * The template pumps up the Avl_string_base object and + * provides the buffer for the actual string. + */ + template + class Avl_string : public Avl_string_base + { + private: + + char _str_buf[STR_LEN]; + + public: + + Avl_string(const char *str) : Avl_string_base(_str_buf) + { + strncpy(_str_buf, str, sizeof(_str_buf)); + _str_buf[STR_LEN - 1] = 0; + } + }; +} + +#endif /* _INCLUDE__UTIL__AVL_STRING_H_ */ diff --git a/base/include/util/avl_tree.h b/base/include/util/avl_tree.h new file mode 100644 index 000000000..eba36da28 --- /dev/null +++ b/base/include/util/avl_tree.h @@ -0,0 +1,203 @@ +/* + * \brief AVL tree + * \author Norman Feske + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__AVL_TREE_H_ +#define _INCLUDE__UTIL__AVL_TREE_H_ + +#include + +namespace Genode { + + class Avl_node_base + { + protected: + + /** + * Internal policy interface + * + * The implementation of this interface is provided by the AVL tree. + */ + struct Policy + { + virtual ~Policy() { } + + /** + * Compare two nodes + * + * \retval false if n2 is lower than n1 + * \retval true if n2 is higher than or equal to n1 + * + * This function must be provided by the derived class. + * It determines the order of nodes inside the avl tree. + */ + virtual bool higher(Avl_node_base *n1, Avl_node_base *n2) const = 0; + + /** + * Node recomputation hook + * + * If a node gets rearranged, this function is called. + * It can be used to update avl-tree-position dependent + * meta data. + */ + virtual void recompute(Avl_node_base *) { } + }; + + Avl_node_base *_child[2]; /* left and right subtrees */ + Avl_node_base *_parent; /* parent of subtree */ + unsigned char _depth; /* depth of subtree */ + + public: + + typedef bool Side; + + enum { LEFT = false, RIGHT = true }; + + private: + + /** + * Determine depth of subtree + */ + inline int _child_depth(Side i) { + return _child[i] ? _child[i]->_depth : 0; } + + /** + * Update depth of node + */ + void _recompute_depth(Policy &policy); + + /** + * Determine left-right bias of both subtrees + */ + inline Side _bias() { + return (_child_depth(RIGHT) > _child_depth(LEFT)); } + + /** + * Insert subtree into specified side of the node + */ + void _adopt(Avl_node_base *node, Side i, Policy &policy); + + /** + * Rotate subtree + * + * \param side direction of rotate operation + * \param node subtree to rotate + * + * The local node_* variable names describe node locations for + * the left (default) rotation. For example, node_r_l is the + * left of the right of node. + */ + void _rotate_subtree(Avl_node_base *node, Side side, Policy &policy); + + /** + * Rebalance subtree + * + * \param node immediate child that needs balancing + * + * 'this' is parent of the subtree to rebalance + */ + void _rebalance_subtree(Avl_node_base *node, Policy &policy); + + public: + + /** + * Constructor + */ + Avl_node_base(); + + /** + * Insert new node into subtree + */ + void insert(Avl_node_base *node, Policy &policy); + + /** + * Remove node from tree + */ + void remove(Policy &policy); + }; + + + /** + * AVL node + * + * \param NT type of the class derived from 'Avl_node' + * + * Each object to be stored in the avl tree must be derived from + * 'Avl_node'. The type of the derived class is to be specified as + * template argument to enable 'Avl_node' to call virtual functions + * specific for the derived class. + */ + template + class Avl_node : public Avl_node_base + { + public: + + inline NT *child(Side i) const { return static_cast(_child[i]); } + + /** + * Default policy + */ + void recompute() { } + }; + + + /** + * Root node of the AVL tree + * + * The real nodes are always attached at the left branch of + * this root node. + */ + template + class Avl_tree : Avl_node + { + private: + + /** + * Auto-generated policy class specific for NT + */ + class Policy : public Avl_node_base::Policy + { + bool higher(Avl_node_base *n1, Avl_node_base *n2) const + { + return static_cast(n1)->higher(static_cast(n2)); + } + + void recompute(Avl_node_base *node) + { + static_cast(node)->recompute(); + } + + } _policy; + + public: + + /** + * Insert node into AVL tree + */ + void insert(Avl_node *node) { Avl_node_base::insert(node, _policy); } + + /** + * Remove node from AVL tree + */ + void remove(Avl_node *node) { node->remove(_policy); } + + /** + * Request first node of the tree + * + * \return first node + * \retval NULL if tree is empty + */ + inline NT *first() const { return this->child(Avl_node::LEFT); } + }; +} + +#endif /* _INCLUDE__UTIL__AVL_TREE_H_ */ diff --git a/base/include/util/fifo.h b/base/include/util/fifo.h new file mode 100644 index 000000000..5ebf6c77c --- /dev/null +++ b/base/include/util/fifo.h @@ -0,0 +1,108 @@ +/* + * \brief Queue with first-in first-out semantics + * \author Norman Feske + * \date 2008-08-15 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__FIFO_H_ +#define _INCLUDE__UTIL__FIFO_H_ + +namespace Genode { + + /* + * \param QT queue element type + */ + template + class Fifo + { + private: + + QT *_head; /* oldest element */ + QT *_tail; /* newest element */ + + public: + + class Element + { + protected: + + friend class Fifo; + + QT *_next; + bool _is_enqueued; + + public: + + Element(): _next(0), _is_enqueued(false) { } + + /** + * Return true is fifo element is enqueued in a fifo + */ + bool is_enqueued() { return _is_enqueued; } + }; + + public: + + /** + * Return true if queue is empty + */ + bool empty() { return _tail == 0; } + + /** + * Constructor + * + * Start with an empty list. + */ + Fifo(): _head(0), _tail(0) { } + + /** + * Attach element at the end of the queue + */ + void enqueue(QT *e) + { + e->_next = 0; + e->_is_enqueued = true; + + if (empty()) { + _tail = _head = e; + return; + } + + _tail->_next = e; + _tail = e; + } + + /** + * Obtain head element of the queue and remove element from queue + * + * \return head element or 0 if queue is empty + */ + QT *dequeue() + { + QT *result = _head; + + /* check if queue has only one last element */ + if (_head == _tail) + _head = _tail = 0; + else + _head = _head->_next; + + /* mark fifo queue element as free */ + if (result) { + result->_next = 0; + result->_is_enqueued = false; + } + + return result; + } + }; +} + +#endif /* _INCLUDE__UTIL__FIFO_H_ */ diff --git a/base/include/util/list.h b/base/include/util/list.h new file mode 100644 index 000000000..a57160a7c --- /dev/null +++ b/base/include/util/list.h @@ -0,0 +1,126 @@ +/* + * \brief Single connected list + * \author Norman Feske + * \date 2006-08-02 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__LIST_H_ +#define _INCLUDE__UTIL__LIST_H_ + +namespace Genode { + + /* + * \param LT list element type + */ + template + class List + { + private: + + LT *_first; + + public: + + class Element + { + protected: + + friend class List; + + LT *_next; + + public: + + Element(): _next(0) { } + + /** + * Return next element in list + */ + LT *next() const { return _next; } + }; + + public: + + /** + * Constructor + * + * Start with an empty list. + */ + List(): _first(0) { } + + /** + * Return first list element + */ + LT *first() const { return _first; } + + /** + * Insert element into list + */ + void insert(LT *le) + { + le->Element::_next = _first; + _first = le; + } + + /** + * Remove element from list + */ + void remove(LT *le) + { + if (!_first) return; + + /* if specified element is the first of the list */ + if (le == _first) + _first = le->Element::_next; + + else { + + /* search specified element in the list */ + Element *e = _first; + while (e->_next && (e->_next != le)) + e = e->_next; + + /* element is not member of the list */ + if (!e->_next) return; + + /* e->_next is the element to remove, skip it in list */ + e->Element::_next = e->Element::_next->Element::_next; + } + + le->Element::_next = 0; + } + }; + + + /** + * Helper for using member variables as list elements + * + * \param T type of compound object to be organized in a list + * + * This helper allow the creation of lists that use member variables to + * connect their elements. This way, the organized type does not need to + * publicly inherit 'List::Element'. Furthermore objects can easily + * be organized in multiple lists by embedding multiple 'List_element' + * member variables. + */ + template + class List_element : public List >::Element + { + T *_object; + + public: + + List_element(T *object) : _object(object) { } + + T *object() { return _object; } + }; +} + +#endif /* _INCLUDE__UTIL__LIST_H_ */ diff --git a/base/include/util/meta.h b/base/include/util/meta.h new file mode 100644 index 000000000..cfbae0cea --- /dev/null +++ b/base/include/util/meta.h @@ -0,0 +1,636 @@ +/* + * \brief Utilities for template-based meta programming + * \author Norman Feske + * \date 2011-02-28 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__UTIL__META_H_ +#define _INCLUDE__BASE__UTIL__META_H_ + +namespace Genode { + + namespace Trait { + + /*************************************** + ** Reference and non-reference types ** + ***************************************/ + + template struct Reference { typedef T& Type; }; + template struct Reference { typedef T* Type; }; + template struct Reference { typedef T& Type; }; + + template struct Non_reference { typedef T Type; }; + template struct Non_reference { typedef T Type; }; + template struct Non_reference { typedef T Type; }; + + template struct Non_const { typedef T Type; }; + template struct Non_const { typedef T Type; }; + + /** + * Determine plain-old-data type corresponding to type 'T' + */ + template struct Pod { + typedef typename Non_const::Type>::Type Type; }; + + } /* namespace Trait */ + + namespace Meta { + + /*************** + ** Type list ** + ***************/ + + /** + * Type representing an omitted template argument + */ + struct Void { }; + + /** + * Marker for end of type list + */ + struct Empty { }; + + /** + * Basic building block for creating type lists + */ + template + struct Type_tuple + { + typedef HEAD Head; + typedef TAIL Tail; + }; + + /** + * Type list with variable number of types + */ + template + struct Type_list; + + template <> + struct Type_list { typedef Empty Head; }; + + template + struct Type_list : public Type_tuple { }; + + template + struct Type_list : public Type_tuple > { }; + + template + struct Type_list : public Type_tuple > { }; + + template + struct Type_list : public Type_tuple > { }; + + template + struct Type_list : public Type_tuple > { }; + + template + struct Type_list : public Type_tuple > { }; + + template + struct Type_list : public Type_tuple > { }; + + template + struct Type_list : public Type_tuple > { }; + + template + struct Type_list : public Type_tuple > { }; + + + /** + * Macro for wrapping the 'Type_list' template + * + * This macro allows for specifying a type list as macro argument. If we supplied + * the 'Type_list' template with the types as arguments, the preprocessor would + * take the comma between the type-list arguments as separator for the macro + * arguments. + */ +#define GENODE_TYPE_LIST(...) ::Genode::Meta::Type_list<__VA_ARGS__> + + /** + * Calculate the length of typelist 'TL' + */ + template + struct Length { enum { Value = Length::Value + 1 }; }; + + template <> struct Length { enum { Value = 0 }; }; + + + /** + * Return index of type 'T' within typelist 'TL' + */ + template + struct Index_of { + enum { Value = Index_of::Value }; }; + + template + struct Index_of { enum { Value = I }; }; + + + /** + * Append type list 'APPENDIX' to type list 'TL' + */ + template + class Append + { + /* pass appendix towards the end of the typelist */ + typedef typename Append::Type _Tail; + + public: + + /* keep head, replace tail */ + typedef Type_tuple Type; + }; + + + /* replace end of type list ('Empty' type) with appendix */ + template + struct Append { typedef APPENDIX Type; }; + + + /** + * Return type at index 'I' of type list 'TL' + */ + template + struct Type_at { + typedef typename Type_at::Type Type; }; + + /* end recursion if we reached the type */ + template + struct Type_at { typedef typename TL::Head Type; }; + + /* end recursion at the end of type list */ + template struct Type_at { typedef void Type; }; + + /* resolve ambiguous specializations */ + template <> struct Type_at { typedef void Type; }; + + + /** + * Statically check if all elements of type list 'CTL' are contained in + * type list 'TL' + */ + template + struct Contains { + enum { Check = Index_of::Value + + Contains::Check }; }; + + template struct Contains { enum { Check = 0 }; }; + + + /** + * Tuple holding references + */ + template + struct Ref_tuple : public Type_tuple + { + typename Trait::Reference::Type _1; + typename Trait::Reference::Type _2; + + Ref_tuple(typename Trait::Reference::Type v1, + typename Trait::Reference::Type v2) : _1(v1), _2(v2) { } + + typename Trait::Reference::Type get() { return _1; } + }; + + /** + * Specialization of 'Ref_tuple' used if the 'HEAD' is a pointer type + * + * The differentiation between pointer and non-pointer types is + * necessary to obtain a reference to the pointed-to object via the + * 'get' function when marshalling or unmarshalling a pointer. + */ + template + struct Ref_tuple : public Type_tuple + { + HEAD *_1; + typename Trait::Reference::Type _2; + + Ref_tuple(HEAD *v1, typename Trait::Reference::Type v2) + : _1(v1), _2(v2) { } + + typename Trait::Reference::Type get() { return *_1; } + }; + + template + struct Ref_tuple_3 : public Ref_tuple > + { + Ref_tuple _t2; + Ref_tuple_3(typename Trait::Reference::Type v1, + typename Trait::Reference::Type v2, + typename Trait::Reference::Type v3) + : Ref_tuple >(v1, _t2), _t2(v2, v3) { } + }; + + template + struct Ref_tuple_4 : public Ref_tuple > + { + Ref_tuple_3 _t2; + Ref_tuple_4(typename Trait::Reference::Type v1, + typename Trait::Reference::Type v2, + typename Trait::Reference::Type v3, + typename Trait::Reference::Type v4) + : Ref_tuple >(v1, _t2), _t2(v2, v3, v4) { } + }; + + template + struct Ref_tuple_5 : public Ref_tuple > + { + Ref_tuple_4 _t2; + Ref_tuple_5(typename Trait::Reference::Type v1, + typename Trait::Reference::Type v2, + typename Trait::Reference::Type v3, + typename Trait::Reference::Type v4, + typename Trait::Reference::Type v5) + : Ref_tuple >(v1, _t2), _t2(v2, v3, v4, v5) { } + }; + + template + struct Ref_tuple_6 : public Ref_tuple > + { + Ref_tuple_5 _t2; + Ref_tuple_6(typename Trait::Reference::Type v1, + typename Trait::Reference::Type v2, + typename Trait::Reference::Type v3, + typename Trait::Reference::Type v4, + typename Trait::Reference::Type v5, + typename Trait::Reference::Type v6) + : Ref_tuple >(v1, _t2), _t2(v2, v3, v4, v5, v6) { } + }; + + template + struct Ref_tuple_7 : public Ref_tuple > + { + Ref_tuple_6 _t2; + Ref_tuple_7(typename Trait::Reference::Type v1, + typename Trait::Reference::Type v2, + typename Trait::Reference::Type v3, + typename Trait::Reference::Type v4, + typename Trait::Reference::Type v5, + typename Trait::Reference::Type v6, + typename Trait::Reference::Type v7) + : Ref_tuple >(v1, _t2), _t2(v2, v3, v4, v5, v6, v7) { } + }; + + template + struct Ref_tuple_8 : public Ref_tuple > + { + Ref_tuple_7 _t2; + Ref_tuple_8(typename Trait::Reference::Type v1, + typename Trait::Reference::Type v2, + typename Trait::Reference::Type v3, + typename Trait::Reference::Type v4, + typename Trait::Reference::Type v5, + typename Trait::Reference::Type v6, + typename Trait::Reference::Type v7, + typename Trait::Reference::Type v8) + : Ref_tuple >(v1, _t2), _t2(v2, v3, v4, v5, v6, v7, v8) { } + }; + + /** + * Tuple holding raw (plain old) data + */ + template + struct Pod_tuple : public Type_tuple + { + typename Trait::Pod::Type _1; + typename Trait::Pod::Type _2; + + /** + * Accessor for requesting the data reference to '_1' + */ + typename Trait::Pod::Type &get() { return _1; } + }; + + /** + * Specialization of 'Pod_tuple' for pointer types + * + * For pointer types, the corresponding data structure must be able to + * host a copy of the pointed-to object. However, the accessor for data + * must be consistent with the input (pointer) type. Hence, the 'get' + * function returns a pointer to the stored copy. + */ + template + struct Pod_tuple : public Type_tuple + { + typename Trait::Non_reference::Type _1; + typename Trait::Non_reference::Type _2; + + HEAD *get() { return &_1; } + }; + + template + struct Pod_tuple_3 : public Pod_tuple > { }; + + template + struct Pod_tuple_4 : public Pod_tuple > { }; + + template + struct Pod_tuple_5 : public Pod_tuple > { }; + + template + struct Pod_tuple_6 : public Pod_tuple > { }; + + template + struct Pod_tuple_7 : public Pod_tuple > { }; + + template + struct Pod_tuple_8 : public Pod_tuple > { }; + + + /************************************************************************* + ** Support for representing function arguments in a normalized fashion ** + *************************************************************************/ + + /** + * Return recursive type for holding the specified reference argument types + * + * Depending on the number of supplied template arguments, a differently + * dimensioned type is returned. This template is called with the variable + * argument list used by the 'GENODE_RPC' macro and effectifely translates the + * argument list to a recursive type that can be processed with template meta + * programming. The result of the translation is returned as 'Ref_args::Type'. + */ + template + struct Ref_args; + + template <> + struct Ref_args { + typedef Empty Type; }; + + template + struct Ref_args { + typedef Ref_tuple Type; }; + + template + struct Ref_args { + typedef Ref_tuple_3 Type; }; + + template + struct Ref_args { + typedef Ref_tuple_4 Type; }; + + template + struct Ref_args { + typedef Ref_tuple_5 Type; }; + + template + struct Ref_args { + typedef Ref_tuple_6 Type; }; + + template + struct Ref_args { + typedef Ref_tuple_7 Type; }; + + template + struct Ref_args { + typedef Ref_tuple_8 Type; }; + + + /** + * Return recursive type for storing the specified data types + * + * The 'Pod_args' template works analogously to the 'Ref_args' template, except + * for returning a type for storing values, not references. + */ + template + struct Pod_args; + + template <> + struct Pod_args { typedef Empty Type; }; + + template + struct Pod_args { typedef Pod_tuple Type; }; + + template + struct Pod_args { typedef Pod_tuple_3 Type; }; + + template + struct Pod_args { typedef Pod_tuple_4 Type; }; + + template + struct Pod_args { typedef Pod_tuple_5 Type; }; + + template + struct Pod_args { typedef Pod_tuple_6 Type; }; + + template + struct Pod_args { typedef Pod_tuple_7 Type; }; + + template + struct Pod_args { typedef Pod_tuple_8 Type; }; + + /** + * Helper for calling member functions via a uniform interface + * + * Member functions differ in their types and numbers of arguments as + * well as their return types or their lack of a return type. This + * makes them difficult to call from generic template code. The + * 'call_member' function template remedies this issue by providing a + * wrapper function with a unified signature. For each case, the + * compiler generates a new overload of the 'call_member' function. For + * each number of function arguments, there exists a pair of overloads, + * one used if a return type is present, the other used for functions + * with no return value. + * + * \param RET_TYPE return type of member function, or 'Meta::Empty' + * if the function has no return type + * \param SERVER class that hosts the member function to call + * \param ARGS recursively defined 'Pod_args' type composed of + * the function-argument types expected by the member + * function + * \param ret reference for storing the return value of the + * member function + * \param server reference to the object to be used for the call + * \param args function arguments + * \param func pointer-to-member function to invoke + */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)()); + + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)()) + { ret = (server.*func)(); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)()) + { (server.*func)(); } + + /* 1 */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)(typename Type_at::Type)) + { ret = (server.*func)(args.get()); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)(typename Type_at::Type)) + { (server.*func)(args.get()); } + + /* 2 */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type)) + { ret = (server.*func)(args.get(), args._2.get()); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type)) + { (server.*func)(args.get(), args._2.get()); } + + /* 3 */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { ret = (server.*func)(args.get(), args._2.get(), args._2._2.get()); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { (server.*func)(args.get(), args._2.get(), args._2._2.get()); } + + /* 4 */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { ret = (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get()); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get()); } + + /* 5 */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { ret = (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get(), args._2._2._2._2.get()); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get(), args._2._2._2._2.get()); } + + /* 6 */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { ret = (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get(), + args._2._2._2._2.get(), args._2._2._2._2._2.get()); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get(), + args._2._2._2._2.get(), args._2._2._2._2._2.get()); } + + /* 7 */ + template + static inline void call_member(RET_TYPE &ret, SERVER &server, ARGS &args, + RET_TYPE (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { ret = (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get(), + args._2._2._2._2.get(), args._2._2._2._2._2.get(), args._2._2._2._2._2._2.get()); } + + template + static inline void call_member(Meta::Empty &ret, SERVER &server, ARGS &args, + void (SERVER::*func)(typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type, + typename Type_at::Type)) + { (server.*func)(args.get(), args._2.get(), args._2._2.get(), args._2._2._2.get(), + args._2._2._2._2.get(), args._2._2._2._2_._2.get(), args._2._2._2._2._2.get()); } + + + /******************** + ** Misc utilities ** + ********************/ + + /** + * Round unsigned long value to next machine-word-aligned value + */ + template + struct Round_to_machine_word { + enum { Value = (SIZE + sizeof(long) - 1) & ~(sizeof(long) - 1) }; }; + + /** + * Utility for partial specialization of member function templates + * + * By passing an artificial 'Overload_selector' argument to a function + * template, we can use overloading to partially specify such a + * function template. The selection of the overload to use is directed + * by one or two types specified as template arguments of + * 'Overload_selector'. + */ + template + struct Overload_selector + { + /* + * Make class unique for different template arguments. The types + * are never used. + */ + typedef T1 _T1; + typedef T2 _T2; + + /* prevent zero initialization of objects */ + Overload_selector() { } + }; + + } /* namespace Meta */ +} + +#endif /* _INCLUDE__BASE__UTIL__META_H_ */ diff --git a/base/include/util/misc_math.h b/base/include/util/misc_math.h new file mode 100644 index 000000000..09da7b605 --- /dev/null +++ b/base/include/util/misc_math.h @@ -0,0 +1,72 @@ +/* + * \brief Commonly used math functions + * \author Norman Feske + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__MISC_MATH_H_ +#define _INCLUDE__UTIL__MISC_MATH_H_ + +namespace Genode { + + template + T max(T v1, T v2) { return v1 > v2 ? v1 : v2; } + + template + T min(T v1, T v2) { return v1 < v2 ? v1 : v2; } + + template + T abs(T value) { return value >= 0 ? value : -value; } + + + /** + * Alignment to the power of two + */ + template + static inline T _align_mask(T align) { + return ~((1 << align) - 1); } + + template + static inline T _align_offset(T align) { + return (1 << align) - 1; } + + template + static inline T align_addr(T addr, int align) { + return (addr + _align_offset(align)) & _align_mask(align); } + + + /** + * LOG2 + * + * Scan for most-significant set bit. + */ + template + static inline T log2(T value) + { + if (!value) return -1; + for (int i = 8 * sizeof(value) - 1; i >= 0; --i) + if ((1 << i) & value) return i; + + return -1; + } + + + /** + * Align value to next machine-word boundary + */ + template + inline T align_natural(T value) + { + T mask = sizeof(long) - 1; + return (value + mask) & ~mask; + } +} + +#endif /* _INCLUDE__UTIL__MISC_MATH_H_ */ diff --git a/base/include/util/string.h b/base/include/util/string.h new file mode 100644 index 000000000..cdb831579 --- /dev/null +++ b/base/include/util/string.h @@ -0,0 +1,417 @@ +/* + * \brief String utility functions + * \author Norman Feske + * \date 2006-05-10 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__STRING_H_ +#define _INCLUDE__UTIL__STRING_H_ + +#include +#include + +namespace Genode { + + /** + * Determine length of null-terminated string + */ + inline size_t strlen(const char *s) + { + size_t res = 0; + for (; s && *s; s++, res++); + return res; + } + + + /** + * Compare two strings + * + * \param len maximum number of characters to compare, + * default is unlimited + * + * \retval 0 strings are equal + * \retval >0 s1 is higher than s2 + * \retval <0 s1 is lower than s2 + */ + inline int strcmp(const char *s1, const char *s2, size_t len = ~0UL) + { + for (; *s1 && *s1 == *s2 && len; s1++, s2++, len--) ; + return len ? *s1 - *s2 : 0; + } + + + /** + * Copy memory block + * + * \param dst destination memory block + * \param src source memory block + * \param size number of bytes to copy + * + * \return pointer to destination memory block + */ + inline void *memcpy(void *dst, const void *src, size_t size) + { + char *d = (char *)dst, *s = (char *)src; + size_t i; + + if (s > d) + for (i = 0; i < size; i++, *d++ = *s++); + else + for (d += size, s += size, i = size; i-- > 0; *(--d) = *(--s)); + + return dst; + } + + + /** + * Memmove wrapper for sophisticated overlapping-aware memcpy + */ + inline void *memmove(void *dst, const void *src, size_t size) { + return memcpy(dst, src, size); } + + + /** + * Copy string + * + * \param dst destination buffer + * \param src buffer holding the null-terminated source string + * \param size maximum number of characters to copy + * \return pointer to destination string + * + * This function is not fully compatible to the C standard, in particular + * there is no zero-padding if the length of 'src' is smaller than 'size'. + * Furthermore, in contrast to the libc version, this function always + * produces a null-terminated string in the 'dst' buffer if the 'size' + * argument is greater than 0. + */ + inline char *strncpy(char *dst, const char *src, size_t size) + { + /* sanity check for corner case of a zero-size destination buffer */ + if (size == 0) return dst; + + /* backup original 'dst' for the use as return value */ + char *orig_dst = dst; + + /* + * Copy characters from 'src' to 'dst' respecting the 'size' limit. + * In each iteration, the 'size' variable holds the maximum remaining + * size. We have to leave at least one character free to add the null + * termination afterwards. + */ + while ((size-- > 1UL) && *src) + *dst++ = *src++; + + /* append null termination to the destination buffer */ + *dst = 0; + + return orig_dst; + } + + + /** + * Compare memory blocks + * + * \retval 0 memory blocks are equal + * \retval 1 memory blocks differ + * + * NOTE: This function is not fully compatible to the C standard. + */ + inline int memcmp(const void *p0, const void *p1, size_t size) + { + char *c0 = (char *)p0; + char *c1 = (char *)p1; + + size_t i; + for (i = 0; i < size; i++) + if (c0[i] != c1[i]) return 1; + + return 0; + } + + + /** + * Memset + */ + inline void *memset(void *dst, int i, size_t size) + { + while (size--) ((char *)dst)[size] = i; + return dst; + } + + + /** + * Convert ASCII character to digit + * + * \param hex consider hexadecimals + * \return digit or -1 on error + */ + inline int digit(char c, bool hex = false) + { + if (c >= '0' && c <= '9') return c - '0'; + if (hex && c >= 'a' && c <= 'f') return c - 'a' + 10; + if (hex && c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; + } + + + /** + * Return true if character is a letter + */ + inline bool is_letter(char c) + { + return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))); + } + + + /** + * Return true if character is a digit + */ + inline bool is_digit(char c, bool hex = false) + { + return (digit(c, hex) >= 0); + } + + + /** + * Return true if character is whitespace + */ + inline bool is_whitespace(char c) + { + return (c == '\t' || c == ' ' || c == '\n'); + } + + + /** + * Convert ASCII string to another type + * + * \param T destination type of conversion + * \param s null-terminated source string + * \param result destination pointer to conversion result + * \param base base, autodetected if set to 0 + * \return number of consumed characters + * + * Please note that 'base' and 's_max_len' are not evaluated by all + * template specializations. + */ + template + inline size_t ascii_to(const char *s, T *result, unsigned base = 0); + + + /** + * Read unsigned long value from string + */ + template <> + inline size_t ascii_to(const char *s, unsigned long *result, + unsigned base) + { + unsigned long i = 0, value = 0; + + if (!*s) return i; + + /* autodetect hexadecimal base, default is a base of 10 */ + if (base == 0) { + + /* read '0x' prefix */ + if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) { + s += 2; i += 2; + base = 16; + } else + base = 10; + } + + /* read number */ + for (int d; ; s++, i++) { + + /* read digit, stop when hitting a non-digit character */ + if ((d = digit(*s, base == 16)) < 0) break; + + /* append digit to integer value */ + value = value*base + d; + } + + *result = value; + return i; + } + + + /** + * Read unsigned int value from string + */ + template <> + inline size_t ascii_to(const char *s, unsigned int *result, + unsigned base) + { + unsigned long result_long = 0; + size_t ret = ascii_to(s, &result_long, base); + *result = result_long; + return ret; + } + + + /** + * Read signed long value from string + */ + template <> + inline size_t ascii_to(const char *s, long *result, unsigned base) + { + int i = 0; + + /* read sign */ + int sign = (*s == '-') ? -1 : 1; + + if (*s == '-' || *s == '+') { s++; i++; } + + int j = 0; + unsigned long value = 0; + + j = ascii_to(s, &value, base); + + if (!j) return i; + + *result = sign*value; + return i + j; + } + + + /** + * Wrapper of 'size_t' for selecting 'ascii_to' specialization + */ + class Number_of_bytes + { + size_t _n; + + public: + + /** + * Default constructor + */ + Number_of_bytes() : _n(0) { } + + /** + * Constructor, to be used implicitly via assignment operator + */ + Number_of_bytes(size_t n) : _n(n) { } + + /** + * Convert number of bytes to 'size_t' value + */ + operator size_t() const { return _n; } + }; + + + /** + * Read 'Number_of_bytes' value from string and handle the size suffixes + * + * This function scales the resulting size value according to the suffixes + * for G (2^30), M (2^20), and K (2^10) if present. + */ + template <> + inline size_t ascii_to(const char *s, Number_of_bytes *result, unsigned) + { + unsigned long res = 0; + + /* convert numeric part of string */ + int i = ascii_to(s, &res, 0); + + /* handle suffixes */ + if (i > 0) + switch (s[i]) { + case 'G': res *= 1024; + case 'M': res *= 1024; + case 'K': res *= 1024; i++; + default: break; + } + + *result = res; + return i; + } + + + /** + * Read double float value from string + */ + template <> + inline size_t ascii_to(const char *s, double *result, unsigned) + { + double v = 0.0; /* decimal part */ + double d = 0.1; /* power of fractional digit */ + bool neg = false; /* sign */ + int i = 0; /* character counter */ + + if (s[i] == '-') { + neg = true; + i++; + } + + /* parse decimal part of number */ + for (; s[i] && is_digit(s[i]); i++) + v = 10*v + digit(s[i], false); + + /* if no fractional part exists, return current value */ + if (s[i] != '.') { + *result = neg ? -v : v; + return i; + } + + /* skip comma */ + i++; + + /* parse fractional part of number */ + for (; s[i] && is_digit(s[i]); i++, d *= 0.1) + v += d*digit(s[i], false); + + *result = neg ? -v : v; + return i; + } + + + /** + * Check for end of quotation + * + * Checks if next character is non-backslashed quotation mark. + */ + inline bool end_of_quote(const char *s) { + return s[0] != '\\' && s[1] == '\"'; } + + + /** + * Unpack quoted string + * + * \param src source string including the quotation marks ("...") + * \param dst destination buffer + * + * \return number of characters or negative error code + */ + inline int unpack_string(const char *src, char *dst, int dst_len) + { + /* check if quoted string */ + if (*src != '"') return -1; + + src++; + + int i = 0; + for (; *src && !end_of_quote(src - 1) && (i < dst_len - 1); i++) { + + /* transform '\"' to '"' */ + if (src[0] == '\\' && src[1] == '\"') { + *dst++ = '"'; + src += 2; + } else + *dst++ = *src++; + } + + /* write terminating null */ + *dst = 0; + + return i; + } +} + +#endif /* _INCLUDE__UTIL__STRING_H_ */ diff --git a/base/include/util/token.h b/base/include/util/token.h new file mode 100644 index 000000000..a4d4bb905 --- /dev/null +++ b/base/include/util/token.h @@ -0,0 +1,210 @@ +/* + * \brief Tokenizer support + * \author Norman Feske + * \date 2006-05-19 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__TOKEN_H_ +#define _INCLUDE__UTIL__TOKEN_H_ + +#include + +namespace Genode { + + /** + * Scanner policy that accepts underline characters in identifiers + */ + struct Scanner_policy_identifier_with_underline + { + /** + * Return true if character belongs to a valid identifier + * + * \param c character + * \param i index of character in token + * \return true if character is a valid identifier character + * + * Letters and underline characters are allowed anywhere in an + * identifier, digits must not appear at the beginning. + */ + static bool identifier_char(char c, unsigned i) { + return is_letter(c) || (c == '_') || (i && is_digit(c)); } + }; + + /** + * Token + * + * This class is used to group characters of a string which belong + * to one syntactical token types number, identifier, string, + * whitespace or another single character. + * + * \param SCANNER_POLICY policy that defines the way of token scanning + * + * See 'Scanner_policy_identifier_with_underline' for an example scanner + * policy. + */ + template + class Token + { + public: + + enum Type { SINGLECHAR, NUMBER, IDENT, STRING, WHITESPACE, END }; + + /** + * Constructor + * + * \param s start of string to construct a token from + * \param max_len maximum token length + * + * The 'max_len' argument is useful for processing character arrays + * that are not null-terminated. + */ + Token(const char *s = 0, size_t max_len = ~0UL) + : _start(s), _max_len(max_len), _len(s ? _calc_len(max_len) : 0) { } + + /** + * Accessors + */ + char *start() const { return (char *)_start; } + size_t len() const { return _len; } + Type type() const { return _type(_len); } + + /** + * Return token as null-terminated string + */ + void string(char *dst, size_t max_len) const { + strncpy(dst, start(), min(len() + 1, max_len)); } + + /** + * Return true if token is valid + */ + operator bool () const { return _start && _len; } + + /** + * Access single characters of token + */ + char operator [] (int idx) + { + return ((idx >= 0) && ((unsigned)idx < _len)) ? _start[idx] : 0; + } + + /** + * Return next token + */ + Token next() const { return Token(_start + _len, _max_len - _len); } + + /** + * Return next non-whitespace token + */ + Token eat_whitespace() const { return (_type(_len) == WHITESPACE) ? next() : *this; } + + private: + + const char *_start; + size_t _max_len; + size_t _len; + + /** + * Return type of token + * + * \param max_len maximum token length + * + * This function is used during the construction of 'Token' + * objects, in particular for determining the value of the '_len' + * member. Therefore, we explicitely pass the 'max_len' to the + * function. For the public interface, there exists the 'type()' + * accessor, which relies on '_len' as implicit argument. + */ + Type _type(size_t max_len) const + { + if (!_start || max_len < 1 || !*_start) return END; + + /* determine the type based on the first character */ + char c = *_start; + if (SCANNER_POLICY::identifier_char(c, 0)) return IDENT; + if (is_digit(c)) return NUMBER; + if (is_whitespace(c)) return WHITESPACE; + + /* if string is incomplete, discard it (type END) */ + if (c == '"') + return _quoted_string_len(max_len) ? STRING : END; + + return SINGLECHAR; + } + + size_t _quoted_string_len(size_t max_len) const + { + unsigned i = 0; + + for (; !end_of_quote(&_start[i]) && i < max_len; i++) + + /* string ends without final quotation mark? too bad! */ + if (!_start[i]) return 0; + + /* exceeded maximum token length */ + if (i == max_len) return 0; + + /* + * We stopped our search at the character before the + * final quotation mark but we return the number of + * characters including the quotation marks. + */ + return i + 2; + } + + /** + * Return length of token + */ + int _calc_len(size_t max_len) const + { + switch (_type(max_len)) { + + case SINGLECHAR: + return 1; + + case NUMBER: + { + unsigned i = 0; + for (; i < max_len && is_digit(_start[i]); i++); + return i; + } + + case IDENT: + { + unsigned i = 0; + for (; i < max_len; i++) { + if (SCANNER_POLICY::identifier_char(_start[i], i)) + continue; + + /* stop if any other (invalid) character occurs */ + break; + } + return i; + } + + case STRING: + + return _quoted_string_len(max_len); + + case WHITESPACE: + { + unsigned i = 0; + for (; is_whitespace(_start[i]) && i < max_len; i++); + return i; + } + + case END: + default: + return 0; + } + } + }; +} + +#endif /* _INCLUDE__UTIL__TOKEN_H_ */ diff --git a/base/include/util/touch.h b/base/include/util/touch.h new file mode 100644 index 000000000..a76d58c97 --- /dev/null +++ b/base/include/util/touch.h @@ -0,0 +1,35 @@ +/* + * \brief Memory touch helpers + * \author Norman Feske + * \date 2007-04-29 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__TOUCH_H_ +#define _INCLUDE__UTIL__TOUCH_H_ + +#include + +namespace Genode { + + /** Touch one byte at address read only */ + inline void touch_read(unsigned char const volatile *addr) + { + (void)*addr; + } + + /** Touch one byte at address read/write */ + inline void touch_read_write(unsigned char volatile *addr) + { + unsigned char v = *addr; + *addr = v; + } +} + +#endif /* _INCLUDE__UTIL__TOUCH_H_ */ diff --git a/base/include/x86/cpu/atomic.h b/base/include/x86/cpu/atomic.h new file mode 100644 index 000000000..79000e090 --- /dev/null +++ b/base/include/x86/cpu/atomic.h @@ -0,0 +1,53 @@ +/* + * \brief Atomic operations for x86 + * \author Norman Feske + * \date 2006-07-26 + * + * Based on l4util/include/ARCH-x86/atomic_arch.h. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__X86__CPU__ATOMIC_H_ +#define _INCLUDE__X86__CPU__ATOMIC_H_ + +namespace Genode { + + /** + * Atomic compare and exchange + * + * This function compares the value at dest with cmp_val. + * If both values are equal, dest is set to new_val. If + * both values are different, the value at dest remains + * unchanged. + * + * \return 1 if the value was successfully changed to new_val, + * 0 if cmp_val and the value at dest differ. + */ + inline int cmpxchg(volatile int *dest, int cmp_val, int new_val) + { + int tmp; + + __asm__ __volatile__ + ( + "lock cmpxchgl %1, %3 \n\t" + : + "=a" (tmp) /* 0 EAX, return val */ + : + "r" (new_val), /* 1 reg, new value */ + "0" (cmp_val), /* 2 EAX, compare value */ + "m" (*dest) /* 3 mem, destination operand */ + : + "memory", "cc" + ); + + return tmp == cmp_val; + } +} + +#endif /* _INCLUDE__X86__CPU__ATOMIC_H_ */ diff --git a/base/include/x86/cpu/consts.h b/base/include/x86/cpu/consts.h new file mode 100644 index 000000000..251acafaf --- /dev/null +++ b/base/include/x86/cpu/consts.h @@ -0,0 +1,35 @@ +/* + * \brief Constants definitions for the x86 architecture. + * \author Stefan Kalkowski + * \date 2011-09-08 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__X86__CPU__CONSTS_H_ +#define _INCLUDE__X86__CPU__CONSTS_H_ + +namespace X86 { + + enum Eflags_masks { + CARRY = 1 << 0, + PARITY = 1 << 2, + ADJUST = 1 << 4, + ZERO = 1 << 6, + SIGN = 1 << 7, + TRAP = 1 << 8, + INT_ENABLE = 1 << 9, + DIRECTION = 1 << 10, + OVERFLOW = 1 << 11, + IOPL = 3 << 12, + NESTED_TASK = 1 << 14, + }; + +} + +#endif /* _INCLUDE__X86__CPU__CONSTS_H_ */ diff --git a/base/include/x86_32/cpu/cpu_state.h b/base/include/x86_32/cpu/cpu_state.h new file mode 100644 index 000000000..b872c71ea --- /dev/null +++ b/base/include/x86_32/cpu/cpu_state.h @@ -0,0 +1,48 @@ +/* + * \brief CPU state + * \author Christian Prochaska + * \date 2011-04-15 + * + * This file contains the x86_32-specific part of the CPU state. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__X86_32__CPU__CPU_STATE_H_ +#define _INCLUDE__X86_32__CPU__CPU_STATE_H_ + +#include + +namespace Genode { + + struct Cpu_state + { + addr_t ip; /* instruction pointer */ + addr_t sp; /* stack pointer */ + addr_t edi; + addr_t esi; + addr_t ebp; + addr_t ebx; + addr_t edx; + addr_t ecx; + addr_t eax; + addr_t gs; + addr_t fs; + addr_t eflags; + addr_t trapno; + + /** + * Constructor + */ + Cpu_state(): ip(0), sp(0), edi(0), esi(0), ebp(0), + ebx(0), edx(0), ecx(0), eax(0), gs(0), + fs(0), eflags(0), trapno(0) { } + }; +} + +#endif /* _INCLUDE__X86_32__CPU__CPU_STATE_H_ */ diff --git a/base/include/x86_64/cpu/cpu_state.h b/base/include/x86_64/cpu/cpu_state.h new file mode 100644 index 000000000..1ebdc801f --- /dev/null +++ b/base/include/x86_64/cpu/cpu_state.h @@ -0,0 +1,54 @@ +/* + * \brief CPU state + * \author Christian Prochaska + * \author Stefan Kalkowski + * \date 2011-04-15 + * + * This file contains the x86_64-specific part of the CPU state. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__X86_64__CPU__CPU_STATE_H_ +#define _INCLUDE__X86_64__CPU__CPU_STATE_H_ + +#include + +namespace Genode { + + struct Cpu_state + { + addr_t ip; + addr_t sp; + addr_t r8; + addr_t r9; + addr_t r10; + addr_t r11; + addr_t r12; + addr_t r13; + addr_t r14; + addr_t r15; + addr_t rax; + addr_t rbx; + addr_t rcx; + addr_t rdx; + addr_t rdi; + addr_t rsi; + addr_t rbp; + addr_t ss; + addr_t eflags; + addr_t trapno; + + Cpu_state() : ip(0), sp(0), r8(0), r9(0), r10(0), + r11(0), r12(0), r13(0), r14(0), r15(0), + rax(0), rbx(0), rcx(0), rdx(0), rdi(0), + rsi(0), rbp(0), ss(0), eflags(0), trapno(0) {} + }; +} + +#endif /* _INCLUDE__X86_64__CPU__CPU_STATE_H_ */ diff --git a/base/lib/README b/base/lib/README new file mode 100644 index 000000000..8b5b8affc --- /dev/null +++ b/base/lib/README @@ -0,0 +1 @@ +This directory holds library-description files. diff --git a/base/lib/import/import-stdcxx.mk b/base/lib/import/import-stdcxx.mk new file mode 100644 index 000000000..0e343f69f --- /dev/null +++ b/base/lib/import/import-stdcxx.mk @@ -0,0 +1,21 @@ +# +# Support for using standard C++ headers for Genode programs +# + +# +# Add the location of the compiler's C++ headers to search path +# +# We add all header locations that have "c++" or "include-fixed" to the search +# path. The 'c++' subdirectory contains the actual standard C++ headers. +# However, for using them together with Boost, we need to access 'limits.h' as +# provided within the 'include-fixed' location. +# +INC_DIR += $(shell echo "int main() {return 0;}" |\ + LANG=C $(CXX) -x c++ -v -E - 2>&1 |\ + sed '/^\#include <\.\.\.> search starts here:/,/^End of search list/!d' |\ + grep "c++") + +# +# Link libstdc++ that comes with the tool chain +# +EXT_OBJECTS += $(shell $(CUSTOM_CXX_LIB) $(CC_MARCH) -print-file-name=libstdc++.a) diff --git a/base/lib/mk/README b/base/lib/mk/README new file mode 100644 index 000000000..6e3381812 --- /dev/null +++ b/base/lib/mk/README @@ -0,0 +1,24 @@ +This directory contains library description files. Each '.mk' file +holds the instruction for building the library ''. These makefiles are +never used directly but they are called from the build system when required. +When called, the build system passes the following variables: + +:'BASE_DIR': This is the base directory of the source tree. + +Source codes are specified by setting the 'SRC_CC' and 'SRC_C' variables. +The source code locations must be specified via 'vpath'. +A library can include other libraries by setting the 'LIBS' +variable. + +Each '.mk' file must include the 'lib.mk' role file: + +! include $(BASE_DIR)/mk/lib.mk + +Libraries implementing one and the same library interface may have specific +implementations for different platforms. Such platform-specific '.mk' +files should be placed into corresponding subdirectories. For example, the +'linux'-specific implementation of the 'server' library resides in the 'linux/' +subdirectory. The build system automatically searches the right '.mk' +file by evaluating the 'SPECS' configuration variable. If 'SPECS' is set to +'host linux', the build system will look into the directories './', './host', +and './linux'. diff --git a/base/lib/mk/allocator_avl.mk b/base/lib/mk/allocator_avl.mk new file mode 100644 index 000000000..2d339fc66 --- /dev/null +++ b/base/lib/mk/allocator_avl.mk @@ -0,0 +1,4 @@ +SRC_CC = allocator_avl.cc +LIBS = slab avl_tree + +vpath % $(REP_DIR)/src/base/allocator diff --git a/base/lib/mk/avl_tree.mk b/base/lib/mk/avl_tree.mk new file mode 100644 index 000000000..bc0f3037e --- /dev/null +++ b/base/lib/mk/avl_tree.mk @@ -0,0 +1,3 @@ +SRC_CC = avl_tree.cc + +vpath %.cc $(REP_DIR)/src/base/avl_tree diff --git a/base/lib/mk/console.mk b/base/lib/mk/console.mk new file mode 100644 index 000000000..3df277d4b --- /dev/null +++ b/base/lib/mk/console.mk @@ -0,0 +1,3 @@ +SRC_CC = console.cc + +vpath %.cc $(REP_DIR)/src/base/console diff --git a/base/lib/mk/cxx.mk b/base/lib/mk/cxx.mk new file mode 100644 index 000000000..ca1f9d171 --- /dev/null +++ b/base/lib/mk/cxx.mk @@ -0,0 +1,68 @@ +LIBS = allocator_avl +CXX_SRC_CC += misc.cc new_delete.cc malloc_free.cc exception.cc guard.cc unwind.cc + +vpath %.cc $(BASE_DIR)/src/base/cxx + +# +# Here we define all symbols we want to hide in libsupc++ and libgcc_eh +# +LIBC_SYMBOLS += malloc free calloc realloc \ + abort fputc fputs fwrite \ + stderr strcat strcpy strlen \ + memcmp strncmp strcmp sprintf \ + __stderrp + +# +# Symbols we wrap (see unwind.cc) +# +EH_SYMBOLS = _Unwind_Resume + +# +# Additional functions for ARM +# +EH_SYMBOLS += __aeabi_unwind_cpp_pr0 __aeabi_unwind_cpp_pr1 + +# +# Take the right system libraries +# +# Normally, we never include build-system-internal files from library- +# description files. For building the 'cxx' library, however, we need the +# information about the used 'gcc' for resolving the location of the C++ +# support libraries. This definition is performed by 'mk/lib.mk' after +# including this library description file. Hence, we need to manually +# include 'global.mk' here. +# +include $(BASE_DIR)/mk/global.mk + +LIBCXX_GCC = $(shell $(CUSTOM_CXX_LIB) $(CC_MARCH) -print-file-name=libsupc++.a) \ + $(shell $(CUSTOM_CXX_LIB) $(CC_MARCH) -print-file-name=libgcc_eh.a || true) + +# +# Dummy target used by the build system +# +SRC_S = supc++.o +CXX_SRC = $(sort $(CXX_SRC_CC)) +CXX_OBJECTS = $(addsuffix .o,$(basename $(CXX_SRC))) +LOCAL_SYMBOLS = $(patsubst %,--localize-symbol=%,$(LIBC_SYMBOLS)) +REDEF_SYMBOLS = $(foreach S, $(EH_SYMBOLS), --redefine-sym $(S)=_cxx_$(S) --redefine-sym __cxx_$(S)=$(S)) + +# +# Prevent symbols of the gcc support libs from being discarded during 'ld -r' +# +KEEP_SYMBOLS += __cxa_guard_acquire +KEEP_SYMBOLS += __dynamic_cast +KEEP_SYMBOLS += _ZTVN10__cxxabiv116__enum_type_infoE +KEEP_SYMBOLS += _ZN10__cxxabiv121__vmi_class_type_infoD0Ev +KEEP_SYMBOLS += _ZTVN10__cxxabiv119__pointer_type_infoE +KEEP_SYMBOLS += _ZTSN10__cxxabiv120__function_type_infoE + +# +# Rule to link all libc definitions and libsupc++ libraries +# and to hide after that the exported libc symbols +# +$(SRC_S): $(CXX_OBJECTS) + $(MSG_MERGE)$@ + $(VERBOSE)$(LD) $(LD_MARCH) $(addprefix -u ,$(KEEP_SYMBOLS)) -r $(CXX_OBJECTS) $(LIBCXX_GCC) -o $@.tmp + $(MSG_CONVERT)$@ + $(VERBOSE)$(OBJCOPY) $(LOCAL_SYMBOLS) $(REDEF_SYMBOLS) $@.tmp $@ + $(VERBOSE)$(RM) $@.tmp diff --git a/base/lib/mk/elf.mk b/base/lib/mk/elf.mk new file mode 100644 index 000000000..0d6f828b8 --- /dev/null +++ b/base/lib/mk/elf.mk @@ -0,0 +1,3 @@ +SRC_CC = elf_binary.cc + +vpath % $(REP_DIR)/src/base/elf diff --git a/base/lib/mk/env.mk b/base/lib/mk/env.mk new file mode 100644 index 000000000..651e430ce --- /dev/null +++ b/base/lib/mk/env.mk @@ -0,0 +1,5 @@ +SRC_CC = env.cc context_area.cc +LIBS = ipc heap log_console lock + +vpath env.cc $(REP_DIR)/src/base/env +vpath context_area.cc $(BASE_DIR)/src/base/env diff --git a/base/lib/mk/heap.mk b/base/lib/mk/heap.mk new file mode 100644 index 000000000..bfb490471 --- /dev/null +++ b/base/lib/mk/heap.mk @@ -0,0 +1,4 @@ +SRC_CC = heap.cc sliced_heap.cc +LIBS = allocator_avl + +vpath %.cc $(REP_DIR)/src/base/heap diff --git a/base/lib/mk/host/cxx.mk b/base/lib/mk/host/cxx.mk new file mode 100644 index 000000000..4a410956a --- /dev/null +++ b/base/lib/mk/host/cxx.mk @@ -0,0 +1,3 @@ +SRC_CC = new_delete.cc + +vpath new_delete.cc $(REP_DIR)/src/base/cxx diff --git a/base/lib/mk/log_console.mk b/base/lib/mk/log_console.mk new file mode 100644 index 000000000..9cd2ea242 --- /dev/null +++ b/base/lib/mk/log_console.mk @@ -0,0 +1,4 @@ +SRC_CC = log_console.cc +LIBS = console + +vpath log_console.cc $(REP_DIR)/src/base/console diff --git a/base/lib/mk/platform.mk b/base/lib/mk/platform.mk new file mode 100644 index 000000000..e69de29bb diff --git a/base/lib/mk/process.mk b/base/lib/mk/process.mk new file mode 100644 index 000000000..e51576aa4 --- /dev/null +++ b/base/lib/mk/process.mk @@ -0,0 +1,4 @@ +SRC_CC = process.cc +LIBS = elf + +vpath process.cc $(REP_DIR)/src/base/process diff --git a/base/lib/mk/raw_server.mk b/base/lib/mk/raw_server.mk new file mode 100644 index 000000000..744098113 --- /dev/null +++ b/base/lib/mk/raw_server.mk @@ -0,0 +1,3 @@ +SRC_CC = server.cc common.cc + +vpath %.cc $(REP_DIR)/src/base/server diff --git a/base/lib/mk/raw_signal.mk b/base/lib/mk/raw_signal.mk new file mode 100644 index 000000000..6e08dee0f --- /dev/null +++ b/base/lib/mk/raw_signal.mk @@ -0,0 +1,3 @@ +SRC_CC = signal.cc + +vpath signal.cc $(REP_DIR)/src/base/signal diff --git a/base/lib/mk/server.mk b/base/lib/mk/server.mk new file mode 100644 index 000000000..de155386e --- /dev/null +++ b/base/lib/mk/server.mk @@ -0,0 +1,3 @@ +LIBS = thread + +include $(REP_DIR)/lib/mk/raw_server.mk diff --git a/base/lib/mk/signal.mk b/base/lib/mk/signal.mk new file mode 100644 index 000000000..b778fcbf3 --- /dev/null +++ b/base/lib/mk/signal.mk @@ -0,0 +1,3 @@ +include $(BASE_DIR)/lib/mk/raw_signal.mk + +LIBS += thread diff --git a/base/lib/mk/slab.mk b/base/lib/mk/slab.mk new file mode 100644 index 000000000..c49922a2d --- /dev/null +++ b/base/lib/mk/slab.mk @@ -0,0 +1,3 @@ +SRC_CC = slab.cc + +vpath % $(REP_DIR)/src/base/allocator diff --git a/base/lib/mk/stdcxx.mk b/base/lib/mk/stdcxx.mk new file mode 100644 index 000000000..67dbe553b --- /dev/null +++ b/base/lib/mk/stdcxx.mk @@ -0,0 +1,7 @@ +# +# This is a pseudo library for letting programs use stdc++ headers by adding +# 'stdcxx' to the 'LIBS' declaration. The actual support for incorporating +# the C++ standard library resides in 'lib/import/import-stdcxx.mk'. This +# file merely exists to resolve the build dependency to the 'stdcxx' library +# description file. +# diff --git a/base/lib/mk/thread.mk b/base/lib/mk/thread.mk new file mode 100644 index 000000000..0dfb937d6 --- /dev/null +++ b/base/lib/mk/thread.mk @@ -0,0 +1,3 @@ +SRC_CC = thread.cc thread_start.cc thread_bootstrap.cc + +vpath %.cc $(BASE_DIR)/src/base/thread diff --git a/base/mk/README b/base/mk/README new file mode 100644 index 000000000..d4220b8d0 --- /dev/null +++ b/base/mk/README @@ -0,0 +1,14 @@ +This directory contains the build system. In consists mainly of makefile +templates for different directory roles. + +:'global.mk': This file contains global variables, for example the + definitions of the tools to use. + +:'generic.mk': Generic rules for creating file types from others. + +:'prg.mk': This file represents the target binary role of a directory. + It must be included by all makefiles that build programs. + +:'lib.mk': This file represents a library role. It is never used from + within the 'src/' directory but only from the .mk files + in 'lib/mk/'. diff --git a/base/mk/base-libs.mk b/base/mk/base-libs.mk new file mode 100644 index 000000000..33f2e1449 --- /dev/null +++ b/base/mk/base-libs.mk @@ -0,0 +1,14 @@ +# +# Genode base libaries +# +# These linked against 'ldso' and filtered out for dynamically +# linked binaries +# +BASE_LIBS = alarm allocator_avl avl_tree cxx env heap \ + ipc lock slab timed_semaphore thread signal \ + log_console slab + +# +# Name of Genode's dynamic linker +# +DYNAMIC_LINKER = ld diff --git a/base/mk/dep_lib.mk b/base/mk/dep_lib.mk new file mode 100644 index 000000000..e72e8ac08 --- /dev/null +++ b/base/mk/dep_lib.mk @@ -0,0 +1,146 @@ +# +# This file determines dependencies of a library from other libraries +# +# The following variables must be defined by the caller: +# +# VERBOSE - controls the make verboseness +# VERBOSE_DIR - verboseness of directory change messages +# VERBOSE_MK - verboseness of make calls +# REPOSITORIES - source code repositories to use +# BASE_DIR - base directory of build system repository +# TARGET_DIR - target build directory +# BUILD_BASE_DIR - build directory with build config +# LIB_CACHE_DIR - destination directory for object files +# LIB_PROGRESS_LOG - library build log file +# BUILD_LIBS - list of libraries to build (without .lib.a or .lib.so suffix) +# INSTALL_DIR - destination directory for installing shared libraries +# + +# +# Generate dependencies only for those libs that are +# not already contained in the library build log +# +include $(LIB_PROGRESS_LOG) +ifneq ($(filter $(LIB),$(LIBS_READY)),) +already_visited: + @true +else +all: append_lib_to_progress_log +endif + +append_lib_to_progress_log: + @echo "LIBS_READY += $(LIB)" >> $(LIB_PROGRESS_LOG) + +LIB_MK_DIRS = $(foreach REP,$(REPOSITORIES),$(addprefix $(REP)/lib/mk/,$(SPECS)) $(REP)/lib/mk) + +# +# Of all possible file locations, use the (first) one that actually exist. +# +LIB_MK = $(firstword $(wildcard $(addsuffix /$(LIB).mk,$(LIB_MK_DIRS)))) + +# +# Sanity check to detect missing library description file +# +ifeq ($(LIB_MK),) +all: warn_missing_lib_mk +else +all: check_unsatisfied_requirements +endif + +warn_missing_lib_mk: generate_lib_rule_for_defect_library + @$(ECHO) "Library-description file $(DARK_COL)$(LIB).mk$(DEFAULT_COL) is missing" + +# +# Determine the repository base directory from the absolute pathname +# of the choosen libname.mk file. We need to specify the override +# command because REP_DIR was first set by prg.mk when building a +# program target. The repository of a library could be a different +# one. +# +# Finally, we strip the trailing slash from the path. The slash was +# used to match the whole directory in 'findstring'. +# +override REP_DIR := $(firstword $(foreach REP,$(REPOSITORIES),$(findstring $(REP)/,$(LIB_MK)))) +override REP_DIR := $(REP_DIR:/=) + +include $(LIB_MK) +include $(BASE_DIR)/mk/base-libs.mk +# +# Libraries from the library depends on +# +# For shared libraries, we have to make sure to build ldso support before +# building a shared library. +# +ifdef SHARED_LIB +LIBS += ldso-startup + +ifeq ($(LIB),$(DYNAMIC_LINKER)) +LIBS += $(BASE_LIBS) +else +LIBS := $(filter-out $(BASE_LIBS),$(LIBS)) +LIBS += $(DYNAMIC_LINKER) +endif + + +DEP_VAR_NAME := DEP_$(LIB).lib.so +else +DEP_VAR_NAME := DEP_$(LIB).lib +endif + +# +# Add platform preparation dependency +# +# We make each leaf library depend on a library called 'platform'. This way, +# the 'platform' library becomes a prerequisite of all other libraries. The +# 'platform' library is supposed to take precautions for setting up +# platform-specific build environments, e.g., preparing kernel API headers. +# +ifeq ($(LIBS),) +ifneq ($(LIB),platform) +LIBS += platform +endif +endif + +# +# Check if the requirements of the target are satisfied +# +UNSATISFIED_REQUIREMENTS = $(filter-out $(SPECS),$(REQUIRES)) +ifneq ($(UNSATISFIED_REQUIREMENTS),) +check_unsatisfied_requirements: warn_unsatisfied_requirements +else +check_unsatisfied_requirements: generate_lib_rule +endif + +warn_unsatisfied_requirements: generate_lib_rule_for_defect_library + @$(ECHO) "Skip library $(LIB) because it requires $(DARK_COL)$(UNSATISFIED_REQUIREMENTS)$(DEFAULT_COL)" + +generate_lib_rule_for_defect_library: + @echo "INVALID_DEPS += $(LIB)" >> $(LIB_DEP_FILE) + @echo "" >> $(LIB_DEP_FILE) + +LIBS_TO_VISIT = $(filter-out $(LIBS_READY),$(LIBS)) + +generate_lib_rule: +ifneq ($(LIBS),) + @(echo "$(DEP_VAR_NAME) = $(foreach l,$(LIBS),$l.lib \$$(DEP_$l.lib))"; \ + echo "") >> $(LIB_DEP_FILE) +endif + @(echo "$(LIB).lib: $(addsuffix .lib,$(LIBS))"; \ + echo " @\$$(MKDIR) -p \$$(LIB_CACHE_DIR)/$(LIB)"; \ + echo " \$$(VERBOSE_MK)\$$(MAKE) $(VERBOSE_DIR) -C \$$(LIB_CACHE_DIR)/$(LIB) -f \$$(BASE_DIR)/mk/lib.mk \\"; \ + echo " REP_DIR=$(REP_DIR) \\"; \ + echo " LIB_MK=$(LIB_MK) \\"; \ + echo " LIB=$(LIB) \\"; \ + echo " DEPS=\"\$$($(DEP_VAR_NAME))\" \\"; \ + echo " BUILD_BASE_DIR=$(BUILD_BASE_DIR) \\"; \ + echo " SHELL=$(SHELL) \\"; \ + echo " SHARED_LIBS=\"\$$(SHARED_LIBS)\"\\"; \ + echo " INSTALL_DIR=\$$(INSTALL_DIR)"; \ + echo "") >> $(LIB_DEP_FILE) + @for i in $(LIBS_TO_VISIT); do \ + $(MAKE) $(VERBOSE_DIR) -f $(BASE_DIR)/mk/dep_lib.mk REP_DIR=$(REP_DIR) LIB=$$i; done +ifdef SHARED_LIB + @(echo "SHARED_LIBS += $(LIB)"; \ + echo "") >> $(LIB_DEP_FILE) +endif + diff --git a/base/mk/dep_prg.mk b/base/mk/dep_prg.mk new file mode 100644 index 000000000..a6184ef9c --- /dev/null +++ b/base/mk/dep_prg.mk @@ -0,0 +1,71 @@ +# +# Prevent execution of any rule contained in $(TARGET_MK) as default rule +# +all: + +# +# Utility for selecting files from the list of repositories +# +select_from_repositories = $(firstword $(foreach REP,$(REPOSITORIES),$(wildcard $(REP)/$(1)))) + +# +# Include target build instructions to aquire library dependecies +# +PRG_DIR := $(dir $(TARGET_MK)) +include $(TARGET_MK) + +# +# Include lib-import description files +# +include $(foreach LIB,$(LIBS),$(call select_from_repositories,lib/import/import-$(LIB).mk)) + +# +# Add globally defined library supplements +# +include $(SPEC_FILES) +LIBS += $(PRG_LIBS) + +# +# Determine location of $(TARGET_MK) within 'src/', remove trailing slash +# +PRG_REL_DIR := $(subst $(REP_DIR)/src/,,$(PRG_DIR)) +PRG_REL_DIR := $(PRG_REL_DIR:/=) + +# +# Prevent generation of program rule if requirements are unsatisfied +# +UNSATISFIED_REQUIREMENTS = $(filter-out $(SPECS),$(REQUIRES)) +ifneq ($(UNSATISFIED_REQUIREMENTS),) +all: + @$(ECHO) "Skip target $(PRG_REL_DIR) because it requires $(DARK_COL)$(UNSATISFIED_REQUIREMENTS)$(DEFAULT_COL)" +else +all: gen_prg_rule +endif + +include $(LIB_PROGRESS_LOG) +LIBS_TO_VISIT = $(filter-out $(LIBS_READY),$(LIBS)) + +# +# Generate program rule +# +gen_prg_rule: +ifneq ($(LIBS),) + @(echo "DEP_$(TARGET).prg = $(foreach l,$(LIBS),$l.lib \$$(DEP_$l.lib))"; \ + echo "") >> $(LIB_DEP_FILE) +endif + @(echo "$(TARGET).prg: $(addsuffix .lib,$(LIBS))"; \ + echo " @\$$(MKDIR) -p $(PRG_REL_DIR)"; \ + echo " \$$(VERBOSE_MK)\$$(MAKE) $(VERBOSE_DIR) -C $(PRG_REL_DIR) -f \$$(BASE_DIR)/mk/prg.mk \\"; \ + echo " REP_DIR=$(REP_DIR) \\"; \ + echo " PRG_REL_DIR=$(PRG_REL_DIR) \\"; \ + echo " BUILD_BASE_DIR=$(BUILD_BASE_DIR) \\"; \ + echo " DEPS=\"\$$(DEP_$(TARGET).prg)\" \\"; \ + echo " SHELL=$(SHELL) \\"; \ + echo " INSTALL_DIR=\"\$$(INSTALL_DIR)\""; \ + echo "") >> $(LIB_DEP_FILE) + @for i in $(LIBS_TO_VISIT); do \ + $(MAKE) $(VERBOSE_DIR) -f $(BASE_DIR)/mk/dep_lib.mk REP_DIR=$(REP_DIR) LIB=$$i; done + @(echo ""; \ + echo "ifeq (\$$(filter \$$(DEP_$(TARGET).prg:.lib=),\$$(INVALID_DEPS)),)"; \ + echo "all: $(TARGET).prg"; \ + echo "endif") >> $(LIB_DEP_FILE) diff --git a/base/mk/generic.mk b/base/mk/generic.mk new file mode 100644 index 000000000..93a7f0b5c --- /dev/null +++ b/base/mk/generic.mk @@ -0,0 +1,85 @@ +# +# Generic rules to build file types from other file types and other +# common functionaly that is needed to build library or program targets. +# + +# +# Collect object files and avoid duplicates (by using 'sort') +# +SRC_O += $(addprefix binary_,$(addsuffix .o,$(notdir $(SRC_BIN)))) +SRC = $(sort $(SRC_C) $(SRC_CC) $(SRC_ADA) $(SRC_S) $(SRC_O)) +OBJECTS = $(addsuffix .o,$(basename $(SRC))) + +# +# Create sub directories for objects files corresponding to the sub directories +# of their respective source files +# +SUB_DIRS = $(sort $(dir $(OBJECTS))) +ifneq ($(SUB_DIRS),./) +$(OBJECTS): $(filter-out $(wildcard $(SUB_DIRS)), $(SUB_DIRS)) +endif + +.PHONY: $(SUB_DIRS) +$(SUB_DIRS): + $(VERBOSE)mkdir -p $@ + +# +# Make sure, that we rebuild object files after Makefile changes +# +$(wildcard $(OBJECTS)): $(filter-out $(LIB_PROGRESS_LOG),$(MAKEFILE_LIST)) + +INCLUDES := $(addprefix -I,$(wildcard $(ALL_INC_DIR))) + +# +# Include dependency files for the corresponding object files except +# when cleaning +# +ifneq ($(filter-out $(MAKECMDGOALS),clean),) +-include $(OBJECTS:.o=.d) +endif + +%.o: %.c + $(MSG_COMP)$@ + $(VERBOSE)$(CC) $(CC_DEF) $(CC_C_OPT) $(INCLUDES) -c $< -o $@ + +%.o: %.cc + $(MSG_COMP)$@ + $(VERBOSE)$(CXX) $(CXX_DEF) $(CC_CXX_OPT) $(INCLUDES) -c $< -o $@ + +%.o: %.cpp + $(MSG_COMP)$@ + $(VERBOSE)$(CXX) $(CXX_DEF) $(CC_CXX_OPT) $(INCLUDES) -c $< -o $@ + +%.o: %.s + $(MSG_ASSEM)$@ + $(VERBOSE)$(AS) $(AS_OPT) $(INCLUDES) $< -o $@ + +# +# Compiling Ada source codes +# +%.o: %.adb + $(MSG_COMP)$@ + $(VERBOSE)gnatmake -q -c $(CC_ADA_OPT) $(INCLUDES) $< + +# +# Assembler files that must be preprocessed are fed to the C compiler. +# +%.o: %.S + $(MSG_COMP)$@ + $(VERBOSE)$(CC) $(CC_DEF) $(CC_OPT) -D__ASSEMBLY__ $(INCLUDES) -c $< -o $@ + +# +# Link binary data +# +# We transform binary data into an object file by using the 'incbin' directive +# of the GNU assembler. This enables us to choose a any label for the binary +# data (in contrast to 'ld -r -oformat default -b binary', which generates the +# label from the input path name) and to align the binary data as required on +# some architectures (e.g., ARM). +# +symbol_name = _binary_$(subst -,_,$(subst .,_,$(subst binary_,,$(subst .o,,$(notdir $@))))) + +binary_%.o: % + $(MSG_CONVERT)$@ + $(VERBOSE)echo ".global $(symbol_name)_start, $(symbol_name)_end; .data; .align 4; $(symbol_name)_start:; .incbin \"$<\"; $(symbol_name)_end:" |\ + $(AS) $(AS_OPT) -f -o $@ - diff --git a/base/mk/global.mk b/base/mk/global.mk new file mode 100644 index 000000000..b6b0d5e95 --- /dev/null +++ b/base/mk/global.mk @@ -0,0 +1,175 @@ +# +# Global build configuration variables +# + +# +# Read user-provided tools configuration +# +-include $(call select_from_repositories,etc/tools.conf) +-include $(BUILD_BASE_DIR)/etc/tools.conf + +# +# Set undefined CUSTOM_ tools to their default values +# +CUSTOM_CC ?= $(CROSS_DEV_PREFIX)gcc +CUSTOM_CXX ?= $(CROSS_DEV_PREFIX)g++ +CUSTOM_CXX_LIB ?= $(CUSTOM_CXX) +CUSTOM_LD ?= $(CROSS_DEV_PREFIX)ld +CUSTOM_AS ?= $(CROSS_DEV_PREFIX)as +CUSTOM_AR ?= $(CROSS_DEV_PREFIX)ar +CUSTOM_NM ?= $(CROSS_DEV_PREFIX)nm +CUSTOM_OBJCOPY ?= $(CROSS_DEV_PREFIX)objcopy + +# +# GNU utilities +# +# Non-Linux operating systems may have to install 'findutils' +# to get the GNU versions of xargs and find. +# +TAC ?= tac +GNU_FIND ?= find +GNU_XARGS ?= xargs +ECHO ?= echo -e + +# +# Build tools +# +CC = $(CUSTOM_CC) +CXX = $(CUSTOM_CXX) +LD = $(CUSTOM_LD) +AS = $(CUSTOM_AS) +AR = $(CUSTOM_AR) +NM = $(CUSTOM_NM) +OBJCOPY = $(CUSTOM_OBJCOPY) + +# +# Compiler and Linker options +# + +# +# Options for automatically generating dependency files +# +# We specify the target for the generated dependency file explicitly via +# the -MT option. Unfortunately, this option is handled differently by +# different gcc versions. Older versions used to always append the object +# file to the target. However, gcc-4.3.2 takes the -MT argument literally. +# So we have to specify both the .o file and the .d file. On older gcc +# versions, this results in the .o file to appear twice in the target +# but that is no problem. +# +CC_OPT_DEP = -MMD -MP -MT '$@ $(@:.o=.d)' + +# +# Always compile with '-ffunction-sections' to enable the use of the +# linker option '-gc-sections' +# +CC_OPT += -ffunction-sections + +# +# Prevent the compiler from optimizations related to strict aliasing +# +CC_OPT += -fno-strict-aliasing + +# +# Do not compile/link with standard includes and standard libraries per +# default. +# +ifneq ($(STDINC),yes) +CC_OPT_NOSTDINC := -nostdinc +endif +ifneq ($(STDLIB),yes) +LD_OPT_NOSTDLIB := -nostdlib -Wl,-nostdlib +endif + +# +# Default optimization and warning levels +# +CC_OLEVEL ?= -O2 +CC_WARN ?= -Wall + +# +# Aggregate compiler options that are common for C and C++ +# +CC_OPT += $(CC_OPT_NOSTDINC) -g $(CC_MARCH) $(CC_OLEVEL) $(CC_OPT_DEP) $(CC_WARN) $(CC_OPT_CHECKCC) + +# +# Incorporate source-file-specific compiler options +# +# The make variable $* refers to the currently processed compilation +# unit when 'CC_OPT' gets implicitly expanded by the rules '%.o: %.c' +# and '%.o: %.cc' of 'generic.mk'. +# +# We substitute '.' characters by '_' to allow source-file-specific +# compiler options for files with more than one dot in their name. +# +CC_OPT += $(CC_OPT_$(subst .,_,$*)) + +# +# Predefine C and C++ specific compiler options with their common values +# +CC_CXX_OPT += $(CC_OPT) +CC_C_OPT += $(CC_OPT) +CC_ADA_OPT += $(CC_OLEVEL) $(CC_WARN) + +# +# Linker options +# +# Use '-gc-sections' by default but allow a platform to disable this feature by +# defining 'LD_GC_SECTIONS' empty. This is needed for the microblaze tool chain +# (gcc version 4.11 and binutils version 2.16), which happens to produce broken +# code when '-gc-sections' is enabled. +# +LD_OPT_GC_SECTIONS ?= -gc-sections +LD_OPT_PREFIX := -Wl, +LD_OPT += $(LD_MARCH) $(LD_OPT_GC_SECTIONS) +CXX_LINK_OPT += $(addprefix $(LD_OPT_PREFIX),$(LD_OPT)) +CXX_LINK_OPT += $(LD_OPT_NOSTDLIB) + +# +# Linker script for dynamically linked programs +# +LD_SCRIPT_DYN = $(call select_from_repositories,src/platform/genode_dyn.ld) + +# +# Linker script for shared libraries +# +LD_SCRIPT_SO = $(call select_from_repositories,src/platform/genode_rel.ld) + +# +# Assembler options +# +AS_OPT += $(AS_MARCH) + +# +# Control sequences for color terminals +# +# To disable colored output, define these variable empty in your +# build-local 'etc/tools.conf' file. +# +BRIGHT_COL ?= \033[01;33m +DARK_COL ?= \033[00;33m +DEFAULT_COL ?= \033[0m + +ALL_INC_DIR := . +ALL_INC_DIR += $(INC_DIR) +ALL_INC_DIR += $(foreach DIR,$(REP_INC_DIR), $(foreach REP,$(REPOSITORIES),$(REP)/$(DIR))) +ALL_INC_DIR += $(foreach REP,$(REPOSITORIES),$(REP)/include) +ALL_INC_DIR += $(LIBGCC_INC_DIR) + +INSTALL_DIR ?= + +VERBOSE ?= @ +VERBOSE_DIR ?= --no-print-directory + +MSG_LINK = @$(ECHO) " LINK " +MSG_COMP = @$(ECHO) " COMPILE " +MSG_BUILD = @$(ECHO) " BUILD " +MSG_MERGE = @$(ECHO) " MERGE " +MSG_CONVERT = @$(ECHO) " CONVERT " +MSG_CONFIG = @$(ECHO) " CONFIG " +MSG_CLEAN = @$(ECHO) " CLEAN " +MSG_ASSEM = @$(ECHO) " ASSEMBLE " +MSG_INST = @$(ECHO) " INSTALL " +MSG_PRG = @$(ECHO) "$(BRIGHT_COL) Program $(DEFAULT_COL)" +MSG_LIB = @$(ECHO) "$(DARK_COL) Library $(DEFAULT_COL)" + diff --git a/base/mk/lib.mk b/base/mk/lib.mk new file mode 100644 index 000000000..bbcd34b42 --- /dev/null +++ b/base/mk/lib.mk @@ -0,0 +1,178 @@ +## +## Rules for building a library target +## +## The following variables must be passed when calling this file: +## +## BASE_DIR - base directory of the build system +## REPOSITORIES - repositories providing libs and headers +## VERBOSE - build verboseness modifier +## VERBOSE_DIR - verboseness modifier for changing directories +## VERBOSE_MK - verboseness of make calls +## BUILD_BASE_DIR - base of build directory tree +## LIB_CACHE_DIR - library build cache location +## INSTALL_DIR - program target build directory +## DEPS - library dependencies +## REP_DIR - repository where the library resides +## + +# +# Prevent .mk rules to be executed as default rule +# +all: + +# +# Function that searches for files in all +# repositories and returns the first match. +# +select_from_repositories = $(firstword $(foreach REP,$(REPOSITORIES),$(wildcard $(REP)/$(1)))) + +# +# Include specifics, for example platform, kernel-api etc. +# +include $(SPEC_FILES) + +# +# Include library build instructions +# +# We set the 'called_from_lib_mk' variable to allow the library decription file +# to respond to the build pass. +# +BACKUP_INC_DIR := $(INC_DIR) +called_from_lib_mk = yes +include $(LIB_MK) + +# +# Sanity check for INC_DIR overrides +# +ifneq ($(filter-out $(INC_DIR),$(BACKUP_INC_DIR)),) +all: error_inc_dir_override +endif + +error_inc_dir_override: + @$(ECHO) "Error: $(LIB_MK) overrides INC_DIR instead of appending" ; false + +# +# Include lib-import descriptions of all used libraries and the target library +# +include $(foreach LIB,$(LIBS),$(call select_from_repositories,lib/import/import-$(LIB).mk)) + +# +# Include global definitions +# +include $(BASE_DIR)/mk/global.mk + +# +# Name of .lib.a or .lib.so file to create +# +ifndef SHARED_LIB +LIB_A := $(addsuffix .lib.a,$(LIB)) +LIB_FILENAME := $(LIB_A) +else +LIB_SO := $(addsuffix .lib.so,$(LIB)) +INSTALL_SO := $(INSTALL_DIR)/$(LIB_SO) +LIB_FILENAME := $(LIB_SO) +endif +LIB_TAG := $(addsuffix .lib.tag,$(LIB)) + +# +# Link libgcc to shared libraries +# +# For static libraries, libgcc is not needed because it will be linked +# against the final target. +# +ifdef SHARED_LIB +LIBGCC = $(shell $(CC) $(CC_MARCH) -print-libgcc-file-name) +endif + +# +# Build libraries position-independent +# +# This option is required for building shared objects but also for static +# libraries that are (potentially) linked against shared objects. Hence, +# we build all libraries with '-fPIC'. +# +CC_OPT_PIC ?= -fPIC +CC_OPT += $(CC_OPT_PIC) + +# +# Print message for the currently built library +# +all: message + +message: + $(MSG_LIB)$(LIB) + +# +# Trigger the creation of the .lib.a or .lib.so file +# +all: $(LIB_TAG) + +$(LIB_TAG): $(LIB_A) $(LIB_SO) $(INSTALL_SO) + @touch $@ + +include $(BASE_DIR)/mk/generic.mk + +# +# Rule to build the .lib.a file +# +# Use $(OBJECTS) instead of $^ for specifying the list of objects to include +# in the archive because $^ may also contain non-object phony targets, e.g., +# used by the integration of Qt4's meta-object compiler into the Genode +# build system. +# +$(LIB_A): $(OBJECTS) + $(MSG_MERGE)$(LIB_A) + $(VERBOSE)$(AR) -rc $@ $(OBJECTS) + +# +# The 'sort' is needed to ensure the same link order regardless +# of the find order, which uses to vary among different systems. +# +STATIC_LIBS := $(foreach l,$(DEPS:.lib=),$(LIB_CACHE_DIR)/$l/$l.lib.a) +STATIC_LIBS := $(sort $(wildcard $(STATIC_LIBS))) +STATIC_LIBS_BRIEF := $(subst $(LIB_CACHE_DIR),$$libs,$(STATIC_LIBS)) + +# +# Rule to build the .lib.so file +# +# The 'LIBS' variable may contain static and shared sub libraries. When linking +# the shared library, we have to link all shared sub libraries to the library +# to store the library-dependency information in the library. Because we do not +# know which sub libraries are static or shared prior calling 'build_libs.mk', +# we use an explicit call to the 'lib_so_wildcard' macro to determine the subset +# of libraries that are shared. +# +# The 'ldso-startup/startup.o' object file, which contains the support code for +# constructing static objects must be specified as object file to prevent the +# linker from garbage-collecting it. +# + +USED_SHARED_LIBS := $(filter $(DEPS:.lib=),$(SHARED_LIBS)) +USED_SO_FILES := $(foreach s,$(USED_SHARED_LIBS),$(LIB_CACHE_DIR)/$s/$s.lib.so) + +# +# Don't link ld libary against shared objects +# +USED_SO_FILES := $(filter-out %ld.lib.so,$(USED_SO_FILES)) + +# +# Default entry point of shared libraries +# +ENTRY_POINT ?= 0x0 + +$(LIB_SO): $(STATIC_LIBS) $(OBJECTS) $(wildcard $(LD_SCRIPT_SO)) + $(MSG_MERGE)$(LIB_SO) + $(VERBOSE)libs=$(LIB_CACHE_DIR); $(LD) -o $(LIB_SO) -shared --eh-frame-hdr \ + $(LD_OPT) \ + -T $(LD_SCRIPT_SO) \ + --entry=$(ENTRY_POINT) \ + --whole-archive \ + --start-group \ + $(USED_SO_FILES) $(STATIC_LIBS_BRIEF) $(OBJECTS) \ + --end-group \ + --no-whole-archive \ + $(LIBGCC) + +$(INSTALL_SO): + $(VERBOSE)ln -sf `pwd`/$(LIB_SO) $@ + diff --git a/base/mk/prg.mk b/base/mk/prg.mk new file mode 100644 index 000000000..570e210a2 --- /dev/null +++ b/base/mk/prg.mk @@ -0,0 +1,183 @@ +## +## Rules for building a program target +## +## The following variables must be passed when calling this file: +## +## BASE_DIR - base directory of the build system +## REP_DIR - source repository of the program +## PRG_REL_DIR - directory of the program relative to 'src/' +## REPOSITORIES - repositories providing libs and headers +## INSTALL_DIR - final install location +## VERBOSE - build verboseness modifier +## VERBOSE_DIR - verboseness modifier for changing directories +## VERBOSE_MK - verboseness of make calls +## LIB_CACHE_DIR - library build cache location +## + +# +# Prevent target.mk rules to be executed as default rule +# +all: + +# +# Function that searches for files in all repositories and returns the first match +# +select_from_repositories = $(firstword $(foreach REP,$(REPOSITORIES),$(wildcard $(REP)/$(1)))) + +# +# Include target build instructions +# +PRG_DIR := $(REP_DIR)/src/$(PRG_REL_DIR) +include $(PRG_DIR)/target.mk + +# +# Include lib-import description files +# +include $(foreach LIB,$(LIBS),$(call select_from_repositories,lib/import/import-$(LIB).mk)) + +# +# Include specifics for platforms, kernel APIs, etc. +# +include $(SPEC_FILES) + +vpath % $(PRG_DIR) + +include $(BASE_DIR)/mk/global.mk + +# +# Assemble linker options for static and dynamic linkage +# +ifneq ($(LD_TEXT_ADDR),) +CXX_LINK_OPT += -Wl,-Ttext=$(LD_TEXT_ADDR) +endif + +# +# Supply machine-dependent arguments to the linker +# +CXX_LINK_OPT += $(CC_MARCH) + +# +# Generic linker script for statically linked binaries +# +LD_SCRIPT_STATIC ?= $(call select_from_repositories,src/platform/genode.ld) + +include $(BASE_DIR)/mk/generic.mk +include $(BASE_DIR)/mk/base-libs.mk + +ifeq ($(INSTALL_DIR),) +all: message $(TARGET) +else +all: message $(INSTALL_DIR)/$(TARGET) +endif + @true # prevent nothing-to-be-done message + +.PHONY: message +message: + $(MSG_PRG)$(PRG_REL_DIR)/$(TARGET) + +# +# Enforce unconditional call of gnatmake rule when compiling Ada sources +# +# Citation from texinfo manual for make: +# +# If a rule has no prerequisites or commands, and the target of the rule +# is a nonexistent file, then `make' imagines this target to have been +# updated whenever its rule is run. This implies that all targets +# depending on this one will always have their commands run. +# +FORCE: +$(SRC_ADA:.adb=.o): FORCE + +# +# The 'sort' is needed to ensure the same link order regardless +# of the find order, which uses to vary among different systems. +# +SHARED_LIBS := $(foreach l,$(DEPS:.lib=),$(LIB_CACHE_DIR)/$l/$l.lib.so) +SHARED_LIBS := $(sort $(wildcard $(SHARED_LIBS))) + +# +# Use CXX for linking +# +LD_CMD := $(CXX) $(CXX_LINK_OPT) + +ifeq ($(SHARED_LIBS),) +LD_SCRIPTS := $(LD_SCRIPT_STATIC) +FILTER_DEPS := $(DEPS:.lib=) +else +LD_SCRIPTS := $(LD_SCRIPT_DYN) +LD_CMD += -Wl,--dynamic-linker=$(DYNAMIC_LINKER).lib.so \ + -Wl,--eh-frame-hdr + +# +# Filter out the base libraries since they will be provided by the ldso.library +# +FILTER_DEPS := $(filter-out $(BASE_LIBS),$(DEPS:.lib=)) +SHARED_LIBS += $(LIB_CACHE_DIR)/$(DYNAMIC_LINKER)/$(DYNAMIC_LINKER).lib.so +endif + +# +# LD_SCRIPT_PREFIX is needed as 'addprefix' chokes on prefixes containing +# commas othwerwise. For compatibilty with older tool chains, we use two -Wl +# parameters for both components of the linker command line. +# +LD_SCRIPT_PREFIX = -Wl,-T -Wl, + +# +# LD_SCRIPTS may be a list of linker scripts (e.g., in base-linux). Further, +# we use the default linker script if none was specified - 'addprefix' expands +# to empty string on empty input. +# +LD_CMD += $(addprefix $(LD_SCRIPT_PREFIX), $(LD_SCRIPTS)) + +STATIC_LIBS := $(foreach l,$(FILTER_DEPS),$(LIB_CACHE_DIR)/$l/$l.lib.a) +STATIC_LIBS := $(sort $(wildcard $(STATIC_LIBS))) + +# +# We need the linker option '--whole-archive' to make sure that all library +# constructors marked with '__attribute__((constructor))' end up int the +# binary. When not using this option, the linker goes through all libraries +# to resolve a symbol and, if it finds the symbol, stops searching. This way, +# object files that are never explicitly referenced (such as library +# constructors) would not be visited at all. +# +# Furthermore, the '--whole-archive' option reveals symbol ambiguities, which +# would go undetected if the search stops after the first match. +# +LINK_ITEMS := $(OBJECTS) $(STATIC_LIBS) $(SHARED_LIBS) +SHORT_LINK_ITEMS := $(subst $(LIB_CACHE_DIR),$$libs,$(LINK_ITEMS)) + +LD_CMD += -Wl,--whole-archive -Wl,--start-group +LD_CMD += $(SHORT_LINK_ITEMS) +LD_CMD += -Wl,--end-group -Wl,--no-whole-archive +LD_CMD += $(EXT_OBJECTS) + +# +# Link libgcc to each program +# +LD_LIBGCC ?= $(shell $(CC) $(CC_MARCH) -print-libgcc-file-name) +LD_CMD += $(LD_LIBGCC) + +# +# Skip final linking if no objects are involved, i.e. no 'SRC' files are +# specified in the 'target.mk' file. This applies for pseudo 'target.mk' +# files that invoke a 3rd-party build system by providing local rule for +# $(TARGET). +# +ifneq ($(OBJECTS),) +$(TARGET): $(LINK_ITEMS) $(wildcard $(LD_SCRIPTS)) + $(MSG_LINK)$(TARGET) + $(VERBOSE)libs=$(LIB_CACHE_DIR); $(LD_CMD) -o $@ + +$(INSTALL_DIR)/$(TARGET): $(TARGET) + $(VERBOSE)ln -sf `pwd`/$(TARGET) $@ +else +$(TARGET): +$(INSTALL_DIR)/$(TARGET): $(TARGET) +endif + +clean_prg_objects: + $(MSG_CLEAN)$(PRG_REL_DIR) + $(VERBOSE)$(RM) -f $(OBJECTS) $(OBJECTS:.o=.d) $(TARGET) + $(VERBOSE)$(RM) -f *.d *.i *.ii *.s *.ali + +clean: clean_prg_objects diff --git a/base/mk/spec-32bit.mk b/base/mk/spec-32bit.mk new file mode 100644 index 000000000..605b433d5 --- /dev/null +++ b/base/mk/spec-32bit.mk @@ -0,0 +1,4 @@ +# +# 32-bit-specific Genode headers +# +REP_INC_DIR += include/32bit diff --git a/base/mk/spec-64bit.mk b/base/mk/spec-64bit.mk new file mode 100644 index 000000000..7878d908f --- /dev/null +++ b/base/mk/spec-64bit.mk @@ -0,0 +1,4 @@ +# +# 64-bit-specific Genode headers +# +REP_INC_DIR += include/64bit diff --git a/base/mk/spec-arm.mk b/base/mk/spec-arm.mk new file mode 100644 index 000000000..d2490942f --- /dev/null +++ b/base/mk/spec-arm.mk @@ -0,0 +1,14 @@ +# +# ARM-specific Genode headers +# +REP_INC_DIR += include/arm + +SPECS += 32bit + +# +# Prevent compiler message +# "note: the mangling of 'va_list' has changed in GCC 4.4" +# +CC_OPT += -Wno-psabi + +include $(call select_from_repositories,mk/spec-32bit.mk) diff --git a/base/mk/spec-arm_v5.mk b/base/mk/spec-arm_v5.mk new file mode 100644 index 000000000..4ab575d26 --- /dev/null +++ b/base/mk/spec-arm_v5.mk @@ -0,0 +1,8 @@ +SPECS += arm + +# +# Configure target CPU +# +CC_OPT += -march=armv5 + +include $(call select_from_repositories,mk/spec-arm.mk) diff --git a/base/mk/spec-arm_v7a.mk b/base/mk/spec-arm_v7a.mk new file mode 100644 index 000000000..9ffb451ca --- /dev/null +++ b/base/mk/spec-arm_v7a.mk @@ -0,0 +1,8 @@ +SPECS += arm + +# +# Configure target CPU +# +CC_OPT += -march=armv7-a + +include $(call select_from_repositories,mk/spec-arm.mk) diff --git a/base/mk/spec-experimental.mk b/base/mk/spec-experimental.mk new file mode 100644 index 000000000..a050bc07b --- /dev/null +++ b/base/mk/spec-experimental.mk @@ -0,0 +1 @@ +CC_OPT += -DEXPERIMENTAL diff --git a/base/mk/spec-host.mk b/base/mk/spec-host.mk new file mode 100644 index 000000000..cbc51d4e9 --- /dev/null +++ b/base/mk/spec-host.mk @@ -0,0 +1,8 @@ +# +# When building for the host, we use the host's standard C environment +# +STDINC = yes +STDLIB = yes +LD_SCRIPT_STATIC = + +REP_INC_DIR = include diff --git a/base/mk/spec-platform_pbxa9.mk b/base/mk/spec-platform_pbxa9.mk new file mode 100644 index 000000000..87d23c16f --- /dev/null +++ b/base/mk/spec-platform_pbxa9.mk @@ -0,0 +1,16 @@ +# +# Enable peripherals of the platform +# +SPECS += pl050 pl11x ps2 pl180 lan9118 pl011 + +# +# Pull in CPU specifics +# +SPECS += arm_v7a + +# +# Add device parameters to include search path +# +REP_INC_DIR += include/platform/pbxa9 + +include $(call select_from_repositories,mk/spec-arm_v7a.mk) diff --git a/base/mk/spec-platform_vea9x4.mk b/base/mk/spec-platform_vea9x4.mk new file mode 100644 index 000000000..6551b7cb4 --- /dev/null +++ b/base/mk/spec-platform_vea9x4.mk @@ -0,0 +1,16 @@ +# +# Enable peripherals of the platform +# +SPECS += pl050 pl11x ps2 pl180 lan9118 pl011 + +# +# Pull in CPU specifics +# +SPECS += arm_v7a + +# +# Add device parameters to include search path +# +REP_INC_DIR += include/platform/vea9x4 + +include $(call select_from_repositories,mk/spec-arm_v7a.mk) diff --git a/base/mk/spec-platform_vpb926.mk b/base/mk/spec-platform_vpb926.mk new file mode 100644 index 000000000..09d23092c --- /dev/null +++ b/base/mk/spec-platform_vpb926.mk @@ -0,0 +1,18 @@ +REP_INC_DIR += include/platform/vpb926 + +# +# Enable peripherals of the platform +# +SPECS += pl050 pl11x pl011 ps2 + +# +# Pull in CPU specifics +# +SPECS += arm_v5 + +# +# Add device parameters to include search path +# +REP_INC_DIR += include/platform/vpb926 + +include $(call select_from_repositories,mk/spec-arm_v5.mk) diff --git a/base/mk/spec-release.mk b/base/mk/spec-release.mk new file mode 100644 index 000000000..63b1a66bb --- /dev/null +++ b/base/mk/spec-release.mk @@ -0,0 +1 @@ +CC_OPT += -DGENODE_RELEASE diff --git a/base/mk/spec-x86_32.mk b/base/mk/spec-x86_32.mk new file mode 100644 index 000000000..f93ba98b8 --- /dev/null +++ b/base/mk/spec-x86_32.mk @@ -0,0 +1,19 @@ +# +# Specifics for 32-bit x86 +# +SPECS += x86 32bit + +# +# x86-specific Genode headers +# +REP_INC_DIR += include/x86 +REP_INC_DIR += include/x86_32 + +# +# x86-specific flags +# +CC_MARCH ?= -march=i686 -m32 +LD_MARCH ?= -melf_i386 +AS_MARCH ?= -march=i686 --32 + +include $(call select_from_repositories,mk/spec-32bit.mk) diff --git a/base/mk/spec-x86_64.mk b/base/mk/spec-x86_64.mk new file mode 100644 index 000000000..824e173cc --- /dev/null +++ b/base/mk/spec-x86_64.mk @@ -0,0 +1,12 @@ +# +# Specifics for 64-bit x86 +# +SPECS += x86 64bit + +# +# x86-specific Genode headers +# +REP_INC_DIR += include/x86 +REP_INC_DIR += include/x86_64 + +include $(call select_from_repositories,mk/spec-64bit.mk) diff --git a/base/run/rm_fault.run b/base/run/rm_fault.run new file mode 100644 index 000000000..476297ea1 --- /dev/null +++ b/base/run/rm_fault.run @@ -0,0 +1,35 @@ +if {[have_spec linux] || [have_spec nova] || [have_spec pistachio]} { + puts "Platform doea not support managed dataspaces"; exit } + +build "core init test/rm_fault" + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + +} + +build_boot_image "core init test-rmfault" + +append qemu_args "-nographic -m 64" + +run_genode_until {child exited with exit value 0.*} 10 + +puts "Test succeeded" diff --git a/base/run/sub_rm.run b/base/run/sub_rm.run new file mode 100644 index 000000000..90791a2da --- /dev/null +++ b/base/run/sub_rm.run @@ -0,0 +1,63 @@ +build "core init test/sub_rm" + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + +} + +build_boot_image "core init test-sub_rm" + +append qemu_args "-nographic -m 64" + +run_genode_until {.*--- end of sub-rm test ---.*} 10 + +if [have_spec linux_x86_32] { + set maps [exec cat /proc/[exec pidof test-sub_rm]/maps] + + puts "\nmemory map after test completion follows:\n" + puts "$maps\n" + + # + # Validate some properties of the final mmap + # + if {![regexp {60000000-60040000 ---p} $maps]} { + puts "Error: detaching from sub RM session failed" + exit -1 + } + if {![regexp {60040000-60044000 rwxs} $maps]} { + puts "Error: populating already attached sub RM session failed" + exit -1 + } + if {![regexp {60080000-60083000 rwxs 00001000} $maps]} { + puts "Error: using offset parameter to sub RM attach did not work" + exit -1 + } + if {![regexp {600c0000-600c2000 rwxs 00001000} $maps]} { + puts "Error: using offset and size parameters to sub RM attach did not work" + exit -1 + } + if {![regexp -- {-60100000 ---p} $maps]} { + puts "Error: attached sub RM session exceeds region boundary" + exit -1 + } +} + +puts "Test succeeded" diff --git a/base/src/README b/base/src/README new file mode 100644 index 000000000..3dbaa0f09 --- /dev/null +++ b/base/src/README @@ -0,0 +1 @@ +This directory contains all source codes. diff --git a/base/src/base/README b/base/src/base/README new file mode 100644 index 000000000..e7f1aba52 --- /dev/null +++ b/base/src/base/README @@ -0,0 +1,7 @@ +This directory contains the mandatory Genode infrastructure +on which all Genode components rely. Each subdirectory +corresponds to a library. + +:Note: Do not mistake the name of this directory with the + make variable $(BASE_DIR). The make variable refers to + the top-level directory of the whole Genode source tree. diff --git a/base/src/base/allocator/README b/base/src/base/allocator/README new file mode 100644 index 000000000..d51637565 --- /dev/null +++ b/base/src/base/allocator/README @@ -0,0 +1,5 @@ +This directory contains a memory-allocator implementation. The +allocator supports storing its meta-data independently from the +managed memory area. This is useful if the allocator is used +to manage memory that is not accessible by the allocator itself +(e.g., virtual memory of another protection domain). diff --git a/base/src/base/allocator/allocator_avl.cc b/base/src/base/allocator/allocator_avl.cc new file mode 100644 index 000000000..61ce8722b --- /dev/null +++ b/base/src/base/allocator/allocator_avl.cc @@ -0,0 +1,357 @@ +/* + * \brief AVL-tree-based memory allocator implementation + * \author Norman Feske + * \date 2006-04-18 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + + +/** + * Placement operator - tool for directly calling a constructor + */ +inline void *operator new(size_t, void *at) { return at; } + + +/************************** + ** Block Implementation ** + **************************/ + +Allocator_avl_base::Block * +Allocator_avl_base::Block::find_best_fit(size_t size, unsigned align) +{ + /* find child with lowest max_avail value */ + bool side = _child_max_avail(1) < _child_max_avail(0); + + /* try to find best fitting block in both subtrees */ + for (int i = 0; i < 2; i++, side = !side) { + + if (_child_max_avail(side) < size) + continue; + + Block *res = child(side) ? child(side)->find_best_fit(size, align) : 0; + + if (res) + return (_fits(size, align) && size < res->size()) ? this : res; + } + + return (_fits(size, align)) ? this : 0; +} + + +Allocator_avl_base::Block * +Allocator_avl_base::Block::find_by_address(addr_t find_addr, size_t find_size, + bool check_overlap) +{ + /* the following checks do not work for size==0 */ + find_size = find_size ? find_size : 1; + + /* check for overlapping */ + if (check_overlap + && (find_addr + find_size - 1 >= addr()) + && (addr() + size() - 1 >= find_addr)) + return this; + + /* check for containment */ + if ((find_addr >= addr()) + && (find_addr + find_size - 1 <= addr() + size() - 1)) + return this; + + /* walk into subtree (right if search addr is higher than current */ + Block *c = child(find_addr >= addr()); + + /* if such a subtree exists, follow it */ + return c ? c->find_by_address(find_addr, find_size, check_overlap) : 0; +} + + +size_t Allocator_avl_base::Block::avail_in_subtree(void) +{ + size_t ret = avail(); + + /* accumulate subtrees of children */ + for (int i = 0; i < 2; i++) + if (child(i)) + ret += child(i)->avail_in_subtree(); + + return ret; +} + + +void Allocator_avl_base::Block::recompute() +{ + _max_avail = max(_child_max_avail(0), _child_max_avail(1)); + _max_avail = max(avail(), _max_avail); +} + + +/********************************** + ** Allocator_avl implementation ** + **********************************/ + +Allocator_avl_base::Block *Allocator_avl_base::_alloc_block_metadata() +{ + void *b = 0; + if (_md_alloc->alloc(sizeof(Block), &b)) + + /* call constructor by using the placement new operator */ + return new((Block *)b) Block(0, 0, 0); + + return 0; +} + + +bool Allocator_avl_base::_alloc_two_blocks_metadata(Block **dst1, Block **dst2) +{ + *dst1 = _alloc_block_metadata(); + *dst2 = _alloc_block_metadata(); + + if (!*dst1 && *dst2) _md_alloc->free(*dst2, sizeof(Block)); + if (!*dst2 && *dst1) _md_alloc->free(*dst1, sizeof(Block)); + + return (*dst1 && *dst2); +} + + +int Allocator_avl_base::_add_block(Block *block_metadata, + addr_t base, size_t size, bool used) +{ + if (!block_metadata) + return -1; + + /* call constructor for new block */ + new (block_metadata) Block(base, size, used); + + /* insert block into avl tree */ + _addr_tree.insert(block_metadata); + + return 0; +} + + +void Allocator_avl_base::_destroy_block(Block *b) +{ + if (!b) return; + + /* remove block from both avl trees */ + _addr_tree.remove(b); + _md_alloc->free(b, _md_entry_size); +} + + +void Allocator_avl_base::_cut_from_block(Block *b, addr_t addr, size_t size, + Block *dst1, Block *dst2) +{ + size_t padding = addr > b->addr() ? addr - b->addr() : 0; + size_t remaining = b->size() > (size + padding) ? b->size() - size - padding : 0; + addr_t orig_addr = b->addr(); + + _destroy_block(b); + + /* create free block containing the alignment padding */ + if (padding > 0) + _add_block(dst1, orig_addr, padding, Block::FREE); + else + _md_alloc->free(dst1, sizeof(Block)); + + /* create free block for remaining space of original block */ + if (remaining > 0) + _add_block(dst2, addr + size, remaining, Block::FREE); + else + _md_alloc->free(dst2, sizeof(Block)); +} + + +int Allocator_avl_base::add_range(addr_t new_addr, size_t new_size) +{ + Block *b; + + /* sanity check for insane users ;-) */ + if (!new_size) return -2; + + /* check for conflicts with existing blocks */ + if (_find_by_address(new_addr, new_size, true)) + return -3; + + Block *new_block = _alloc_block_metadata(); + if (!new_block) return -4; + + /* merge with predecessor */ + if ((b = _find_by_address(new_addr - 1)) && !b->used()) { + + new_size += b->size(); + new_addr = b->addr(); + + _destroy_block(b); + } + + /* merge with successor */ + if ((b = _find_by_address(new_addr + new_size)) && !b->used()) { + + new_size += b->size(); + + _destroy_block(b); + } + + /* create new block that spans over all merged blocks */ + return _add_block(new_block, new_addr, new_size, Block::FREE); +} + + +int Allocator_avl_base::remove_range(addr_t base, size_t size) +{ + /* sanity check for insane users ;-) */ + if (!size) return -1; + + Block *dst1, *dst2; + if (!_alloc_two_blocks_metadata(&dst1, &dst2)) + return -2; + + /* FIXME removing ranges from allocators with used blocks is not safe! */ + while (1) { + + /* find block overlapping the specified range */ + Block *b = _addr_tree.first(); + b = b ? b->find_by_address(base, size, 1) : 0; + + /* + * If there are no overlappings with any existing blocks (b == 0), we + * are done. If however, the overlapping block is in use, we have a + * problem. In both cases, return. + */ + if (!b || !b->avail()) { + _md_alloc->free(dst1, sizeof(Block)); + _md_alloc->free(dst2, sizeof(Block)); + return !b ? 0 : -3; + } + + + /* cut intersecting address range */ + addr_t intersect_beg = max(base, b->addr()); + size_t intersect_end = min(base + size - 1, b->addr() + b->size() - 1); + + _cut_from_block(b, intersect_beg, intersect_end - intersect_beg + 1, dst1, dst2); + if (!_alloc_two_blocks_metadata(&dst1, &dst2)) + return -4; + }; +} + + +bool Allocator_avl_base::alloc_aligned(size_t size, void **out_addr, int align) +{ + Block *dst1, *dst2; + if (!_alloc_two_blocks_metadata(&dst1, &dst2)) + return false; + + /* find best fitting block */ + Block *b = _addr_tree.first(); + b = b ? b->find_best_fit(size, align) : 0; + + if (!b) { + _md_alloc->free(dst1, sizeof(Block)); + _md_alloc->free(dst2, sizeof(Block)); + return false; + } + + /* calculate address of new (aligned) block */ + addr_t new_addr = align_addr(b->addr(), align); + + /* remove new block from containing block */ + _cut_from_block(b, new_addr, size, dst1, dst2); + + /* create allocated block */ + Block *new_block = _alloc_block_metadata(); + if (!new_block) { + _md_alloc->free(new_block, sizeof(Block)); + return false; + } + _add_block(new_block, new_addr, size, Block::USED); + + *out_addr = reinterpret_cast(new_addr); + return true; +} + + +Range_allocator::Alloc_return Allocator_avl_base::alloc_addr(size_t size, addr_t addr) +{ + /* sanity check */ + if (!_sum_in_range(addr, size)) + return Range_allocator::RANGE_CONFLICT; + + Block *dst1, *dst2; + if (!_alloc_two_blocks_metadata(&dst1, &dst2)) + return Range_allocator::OUT_OF_METADATA; + + /* find block at specified address */ + Block *b = _addr_tree.first(); + b = b ? b->find_by_address(addr, size) : 0; + + /* skip if there's no block or block is used */ + if (!b || b->used()) + { + _md_alloc->free(dst1, sizeof(Block)); + _md_alloc->free(dst2, sizeof(Block)); + return Range_allocator::RANGE_CONFLICT; + } + + /* remove new block from containing block */ + _cut_from_block(b, addr, size, dst1, dst2); + + /* create allocated block */ + Block *new_block = _alloc_block_metadata(); + if (!new_block) { + _md_alloc->free(new_block, sizeof(Block)); + return Range_allocator::OUT_OF_METADATA; + } + _add_block(new_block, addr, size, Block::USED); + + return Range_allocator::ALLOC_OK; +} + + +void Allocator_avl_base::free(void *addr) +{ + /* lookup corresponding block */ + Block *b = _find_by_address(reinterpret_cast(addr)); + + if (!b || !(b->used())) return; + + addr_t new_addr = b->addr(); + size_t new_size = b->size(); + + _destroy_block(b); + + add_range(new_addr, new_size); +} + + +bool Allocator_avl_base::any_block_addr(addr_t *out_addr) +{ + Block *b = _addr_tree.first(); + + *out_addr = b ? b->addr() : 0; + return (b != 0); +} + + +size_t Allocator_avl_base::avail() +{ + Block *b = static_cast(_addr_tree.first()); + return b ? b->avail_in_subtree() : 0; +} + + +bool Allocator_avl_base::valid_addr(addr_t addr) +{ + Block *b = _find_by_address(addr); + return b ? true : false; +} diff --git a/base/src/base/allocator/slab.cc b/base/src/base/allocator/slab.cc new file mode 100644 index 000000000..2d28ef184 --- /dev/null +++ b/base/src/base/allocator/slab.cc @@ -0,0 +1,296 @@ +/* + * \brief Slab allocator implementation + * \author Norman Feske + * \date 2006-05-16 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + + +/**************** + ** Slab block ** + ****************/ + +/** + * Placement operator - tool for directly calling a constructor + */ +inline void *operator new(size_t, void *at) { return at; } + + +void Slab_block::slab(Slab *slab) +{ + _slab = slab; + _avail = _slab->num_elem(); + next = prev = 0; + + for (unsigned i = 0; i < _avail; i++) + state(i, FREE); +} + + +Slab_entry *Slab_block::slab_entry(int idx) +{ + /* + * The slab slots start after the state array that consists + * of 'num_elem' bytes. We align the first slot to a four-aligned + * address. + */ + return (Slab_entry *)&_data[align_addr(_slab->num_elem(), 2) + + _slab->entry_size()*idx]; +} + + +int Slab_block::slab_entry_idx(Slab_entry *e) { + return ((addr_t)e - (addr_t)slab_entry(0))/_slab->entry_size(); } + + +void *Slab_block::alloc() +{ + size_t num_elem = _slab->num_elem(); + for (unsigned i = 0; i < num_elem; i++) + if (state(i) == FREE) { + state(i, USED); + Slab_entry *e = slab_entry(i); + e->occupy(this); + return e->addr(); + } + return 0; +} + + +Slab_entry *Slab_block::first_used_entry() +{ + size_t num_elem = _slab->num_elem(); + for (unsigned i = 0; i < num_elem; i++) + if (state(i) == USED) + return slab_entry(i); + return 0; +} + + +void Slab_block::inc_avail(Slab_entry *e) +{ + /* mark slab entry as free */ + int idx = slab_entry_idx(e); + state(idx, FREE); + _avail++; + + /* search previous block with higher avail value than this' */ + Slab_block *at = prev; + + while (at && (at->avail() < _avail)) + at = at->prev; + + /* + * If we already are the first block or our avail value is lower than the + * previous block, do not reposition the block in the list. + */ + if (prev == 0 || at == prev) + return; + + /* reposition block in list after block with higher avail value */ + _slab->remove_sb(this); + _slab->insert_sb(this, at); +} + + +void Slab_block::dec_avail() +{ + _avail--; + + /* search subsequent block with lower avail value than this' */ + Slab_block *at = this; + + while (at->next && at->next->avail() > _avail) + at = at->next; + + if (at == this) return; + + _slab->remove_sb(this); + _slab->insert_sb(this, at); +} + + +/********** + ** Slab ** + **********/ + +Slab::Slab(size_t slab_size, size_t block_size, Slab_block *initial_sb, + Allocator *backing_store) +: _slab_size(slab_size), + _block_size(block_size), + _first_sb(initial_sb), + _initial_sb(initial_sb), + _alloc_state(false), + _backing_store(backing_store) +{ + /* + * Calculate number of entries per slab block. + * + * The 'sizeof(umword_t)' is for the alignment of the first slab entry. + * The 1 is for one byte state entry. + */ + _num_elem = (_block_size - sizeof(Slab_block) - sizeof(umword_t)) + / (entry_size() + 1); + + /* if no initial slab block was specified, try to get one */ + if (!_first_sb && _backing_store) + _first_sb = _new_slab_block(); + + /* init first slab block */ + if (_first_sb) + _first_sb->slab(this); +} + + +Slab::~Slab() +{ + /* free backing store */ + while (_first_sb) { + Slab_block *sb = _first_sb; + remove_sb(_first_sb); + + /* + * Only free slab blocks that we allocated. This is not the case for + * the '_initial_sb' that we got as constructor argument. + */ + if (_backing_store && (sb != _initial_sb)) + _backing_store->free(sb, _block_size); + } +} + + +Slab_block *Slab::_new_slab_block() +{ + void *sb = 0; + if (!_backing_store || !_backing_store->alloc(_block_size, &sb)) + return 0; + + /* call constructor by using the placement new operator */ + return new(sb) Slab_block(this); +} + + +void Slab::remove_sb(Slab_block *sb) +{ + Slab_block *prev = sb->prev; + Slab_block *next = sb->next; + + if (prev) prev->next = next; + if (next) next->prev = prev; + + if (_first_sb == sb) + _first_sb = next; + + sb->prev = sb->next = 0; +} + + +void Slab::insert_sb(Slab_block *sb, Slab_block *at) +{ + /* determine next-pointer where to assign the current sb */ + Slab_block **nextptr_to_sb = at ? &at->next : &_first_sb; + + /* insert current sb */ + sb->next = *nextptr_to_sb; + *nextptr_to_sb = sb; + + /* update prev-pointer or succeeding block */ + if (sb->next) + sb->next->prev = sb; + + sb->prev = at; +} + + +bool Slab::num_free_entries_higher_than(int n) +{ + int cnt = 0; + + for (Slab_block *b = _first_sb; b && b->avail() > 0; b = b->next) { + cnt += b->avail(); + if (cnt > n) + return true; + } + return false; +} + + +bool Slab::alloc(size_t size, void **out_addr) +{ + /* sanity check if first slab block is gone */ + if (!_first_sb) return false; + + /* + * If we run out of slab, we need to allocate a new slab block. For the + * special case that this block is allocated using the allocator that by + * itself uses the slab allocator, such an allocation could cause up to + * three new slab_entry allocations. So we need to ensure to allocate the + * new slab block early enough - that is if there are only three free slab + * entries left. + */ + if (_backing_store && !num_free_entries_higher_than(3) && !_alloc_state) { + + /* allocate new block for slab */ + _alloc_state = true; + Slab_block *sb = _new_slab_block(); + _alloc_state = false; + + if (!sb) return false; + + /* + * The new block has the maximum number of available slots and + * so we can insert it at the beginning of the sorted block + * list. + */ + insert_sb(sb); + } + + *out_addr = _first_sb->alloc(); + return *out_addr == 0 ? false : true; +} + + +void Slab::free(void *addr) +{ + Slab_entry *e = addr ? Slab_entry::slab_entry(addr) : 0; + + if (e) e->free(); +} + + +void *Slab::first_used_elem() +{ + for (Slab_block *b = _first_sb; b; b = b->next) { + + /* skip completely free slab blocks */ + if (b->avail() == _num_elem) + continue; + + /* found a block with used elements - return address of the first one */ + Slab_entry *e = b->first_used_entry(); + if (e) return e->addr(); + } + return 0; +} + + +size_t Slab::consumed() +{ + /* count number of slab blocks */ + unsigned sb_cnt = 0; + for (Slab_block *sb = _first_sb; sb; sb = sb->next) + sb_cnt++; + + return sb_cnt * _block_size; +} diff --git a/base/src/base/avl_tree/avl_tree.cc b/base/src/base/avl_tree/avl_tree.cc new file mode 100644 index 000000000..5733443f7 --- /dev/null +++ b/base/src/base/avl_tree/avl_tree.cc @@ -0,0 +1,164 @@ +/* + * \brief AVL tree + * \author Norman Feske + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + + +inline void Avl_node_base::_recompute_depth(Policy &policy) +{ + unsigned char old_depth = _depth; + _depth = max(_child_depth(LEFT), _child_depth(RIGHT)) + 1; + + /* if our own value changes, update parent */ + if (_depth != old_depth && _parent) + _parent->_recompute_depth(policy); + + /* call recompute hook only for valid tree nodes */ + if (_parent) + policy.recompute(this); +} + + +void Avl_node_base::_adopt(Avl_node_base *node, Side i, Policy &policy) +{ + _child[i] = node; + if (node) + node->_parent = this; + + _recompute_depth(policy); +} + + +void Avl_node_base::_rotate_subtree(Avl_node_base *node, Side side, Policy &policy) +{ + int i = (node == _child[0]) ? LEFT : RIGHT; + + Avl_node_base *node_r = node->_child[!side]; + Avl_node_base *node_r_l = node_r->_child[side]; + + /* simple rotation */ + if (node_r->_bias() == !side) { + + node->_adopt(node_r_l, !side, policy); + node_r->_adopt(node, side, policy); + + _adopt(node_r, i, policy); + } + + /* double rotation */ + else if (node_r_l) { + + Avl_node_base *node_r_l_l = node_r_l->_child[side]; + Avl_node_base *node_r_l_r = node_r_l->_child[!side]; + + node->_adopt(node_r_l_l, !side, policy); + node_r->_adopt(node_r_l_r, side, policy); + + node_r_l->_adopt(node, side, policy); + node_r_l->_adopt(node_r, !side, policy); + + _adopt(node_r_l, i, policy); + } +} + + +void Avl_node_base::_rebalance_subtree(Avl_node_base *node, Policy &policy) +{ + int v = node->_child_depth(RIGHT) - node->_child_depth(LEFT); + + /* return if subtree is in balance */ + if (abs(v) < 2) return; + + _rotate_subtree(node, (v < 0), policy); +} + + +void Avl_node_base::insert(Avl_node_base *node, Policy &policy) +{ + if (node == this) { + PERR("Inserting element %p twice into avl tree!", node); + return; + } + + Side i = LEFT; + + /* for non-root nodes, decide for a branch */ + if (_parent) + i = policy.higher(this, node); + + if (_child[i]) + _child[i]->insert(node, policy); + else + _adopt(node, i, policy); + + /* the inserted node might have changed the depth of the subtree */ + _recompute_depth(policy); + + if (_parent) + _parent->_rebalance_subtree(this, policy); +} + + +void Avl_node_base::remove(Policy &policy) +{ + Avl_node_base *lp = 0; + Avl_node_base *l = _child[0]; + + if (l) { + + /* find right-most node in left sub tree (l) */ + while (l && l->_child[1]) + l = l->_child[1]; + + /* isolate right-most node in left sub tree */ + if (l == _child[0]) + _adopt(l->_child[0], LEFT, policy); + else + l->_parent->_adopt(l->_child[0], RIGHT, policy); + + /* consistent state */ + + /* remember for rebalancing */ + if (l->_parent != this) + lp = l->_parent; + + /* exchange this and l */ + for (int i = 0; i < 2; i++) + if (_parent->_child[i] == this) + _parent->_adopt(l, i, policy); + + l->_adopt(_child[0], LEFT, policy); + l->_adopt(_child[1], RIGHT, policy); + + } else { + + /* no left sub tree, attach our right sub tree to our parent */ + for (int i = 0; i < 2; i++) + if (_parent->_child[i] == this) + _parent->_adopt(_child[1], i, policy); + } + + /* walk the tree towards its root and rebalance sub trees */ + while (lp && lp->_parent) { + Avl_node_base *lpp = lp->_parent; + lpp->_rebalance_subtree(lp, policy); + lp = lpp; + } +} + + +Avl_node_base::Avl_node_base() : _parent(0), _depth(1) { + _child[LEFT] = _child[RIGHT] = 0; } diff --git a/base/src/base/console/console.cc b/base/src/base/console/console.cc new file mode 100644 index 000000000..c22418408 --- /dev/null +++ b/base/src/base/console/console.cc @@ -0,0 +1,335 @@ +/* + * \brief Output of format strings + * \author Norman Feske + * \date 2006-04-07 + * + * NOTE: Support for long long ints is not required by Core. + * Hence, this functionality and further features such + * as floating point numbers should better be placed + * in another 'rich_conole' lib outside of the Genode's + * base repository. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + + +/** + * Format string command representation + */ +class Format_command +{ + public: + + enum Type { INT, UINT, STRING, CHAR, PTR, PERCENT, INVALID }; + enum Length { DEFAULT, LONG, SIZE_T, LONG_LONG }; + + private: + + /** + * Read decimal value from string + */ + int decode_decimal(const char *str, int *consumed) + { + int res = 0; + while (1) { + char c = str[*consumed]; + + if (!c || c < '0' || c > '0' + 9) + return res; + + res = (res * 10) + c - '0'; + (*consumed)++; + } + } + + public: + + Type type; /* format argument type */ + Length length; /* format argument length */ + int padding; /* min number of characters to print */ + int base; /* base of numeric arguments */ + int zeropad : 1; /* pad with zero instead of space */ + int uppercase : 1; /* use upper case for hex numbers */ + int consumed; /* nb of consumed format string characters */ + + /** + * Constructor + * + * \param format begin of command in format string + */ + explicit Format_command(const char *format) + { + consumed = 0; + uppercase = 0; + base = 10; + type = INVALID; + length = DEFAULT; + + /* check for command begin and eat the character */ + if (format[consumed] != '%') return; + if (!format[++consumed]) return; + + /* heading zero indicates zero-padding */ + zeropad = (format[consumed] == '0'); + + /* read decimal padding value */ + padding = decode_decimal(format, &consumed); + if (!format[consumed]) return; + + /* decode length */ + switch (format[consumed]) { + + case 'l': + { + /* long long ints are marked by a subsequenting 'l' character */ + bool is_long_long = (format[consumed + 1] == 'l'); + + length = is_long_long ? LONG_LONG : LONG; + consumed += is_long_long ? 2 : 1; + break; + } + + case 'z': + + length = SIZE_T; + consumed++; + break; + + case 'p': + + length = LONG; + break; + + default: break; + } + + if (!format[consumed]) return; + + /* decode type */ + switch (format[consumed]) { + + case 'd': + case 'i': type = INT; base = 10; break; + case 'o': type = UINT; base = 8; break; + case 'u': type = UINT; base = 10; break; + case 'x': type = UINT; base = 16; break; + case 'X': type = UINT; base = 16; uppercase = 1; break; + case 'p': type = PTR; base = 16; break; + case 'b': type = UINT; base = 2; break; + case 'c': type = CHAR; break; + case 's': type = STRING; break; + case '%': type = PERCENT; break; + + case 0: return; + default: break; + } + + /* eat type character */ + consumed++; + } + + int numeric() + { + return (type == INT || type == UINT || type == PTR); + } +}; + + +/** + * Convert digit to ASCII value + */ +static char ascii(int digit, int uppercase = 0) +{ + if (digit > 9) + return digit + (uppercase ? 'A' : 'a') - 10; + + return digit + '0'; +} + + +/** + * Output signed value with the specified base + */ +template +void Console::_out_signed(T value, unsigned base) +{ + /* for base 2, the number of digits is the number of value bits */ + char buf[sizeof(value)*8]; + + /* set flag if value is negative */ + int neg = value < 0 ? 1 : 0; + + /* get absolute value */ + value = value < 0 ? -value : value; + + int i = 0; + + /* handle zero as special case */ + if (value == 0) + buf[i++] = ascii(0); + + /* fill buffer starting with the least significant digits */ + else + for (; value > 0; value /= base) + buf[i++] = ascii(value % base); + + /* add sign to buffer for negative values */ + if (neg) + buf[i++] = '-'; + + /* output buffer in reverse order */ + for (; i--; ) + _out_char(buf[i]); +} + + +/** + * Output unsigned value with the specified base and padding + */ +template +void Console::_out_unsigned(T value, unsigned base, int pad) +{ + /* for base 2, the number of digits is the number of value bits */ + char buf[sizeof(value)*8]; + + int i = 0; + + /* handle zero as special case */ + if (value == 0) { + buf[i++] = ascii(0); + pad--; + } + + /* fill buffer starting with the least significant digits */ + for (; value > 0; value /= base, pad--) + buf[i++] = ascii(value % base); + + /* add padding zeros */ + for (; pad-- > 0; ) + buf[i++] = ascii(0); + + /* output buffer in reverse order */ + for (; i--; ) + _out_char(buf[i]); +} + + +void Console::_out_string(const char *str) +{ + if (!str) + _out_string(""); + else + while (*str) _out_char(*str++); +} + + +void Console::printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + vprintf(format, list); + va_end(list); +} + + +void Console::vprintf(const char *format, va_list list) +{ + + while (*format) { + + /* eat and output plain characters */ + if (*format != '%') { + _out_char(*format++); + continue; + } + + /* parse format argument descriptor */ + Format_command cmd(format); + + /* read numeric argument from va_list */ + long long numeric_arg = 0; + if (cmd.numeric()) { + switch (cmd.length) { + + case Format_command::LONG_LONG: + + numeric_arg = va_arg(list, long long); + break; + + case Format_command::LONG: + + numeric_arg = va_arg(list, long); + break; + + case Format_command::SIZE_T: + + numeric_arg = va_arg(list, size_t); + break; + + case Format_command::DEFAULT: + + numeric_arg = va_arg(list, int); + break; + } + } + + /* call type-specific output routines */ + switch (cmd.type) { + + case Format_command::INT: + + if (cmd.length == Format_command::LONG_LONG) + _out_signed(numeric_arg, cmd.base); + else + _out_signed(numeric_arg, cmd.base); + break; + + case Format_command::UINT: + + if (cmd.length == Format_command::LONG_LONG) { + _out_unsigned(numeric_arg, cmd.base, cmd.padding); + break; + } + + /* fall through */ + + case Format_command::PTR: + + _out_unsigned(numeric_arg, cmd.base, cmd.padding); + break; + + case Format_command::CHAR: + + _out_char(va_arg(list, int)); + break; + + case Format_command::STRING: + + _out_string(va_arg(list, const char *)); + break; + + case Format_command::PERCENT: + + _out_char('%'); + break; + + case Format_command::INVALID: + + _out_string(""); + break; + } + + /* proceed with format string after command */ + format += cmd.consumed; + } +} diff --git a/base/src/base/console/core_printf.cc b/base/src/base/console/core_printf.cc new file mode 100644 index 000000000..eef14e3be --- /dev/null +++ b/base/src/base/console/core_printf.cc @@ -0,0 +1,73 @@ +/* + * \brief Core-specific 'printf' implementation + * \author Norman Feske + * \date 2010-08-31 + * + * In contrast to regular Genode processes, which use the platform- + * independent LOG-session interface as back end of 'printf', core has + * to rely on a platform-specific back end such as a serial driver or a + * kernel-debugger function. The platform-specific back end is called + * 'Core_console'. + * + * This file contains the generic glue code between 'printf' and + * 'Core_console'. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include /* provides 'Core_console' */ +#include +#include + +using namespace Genode; + + +/** + * Synchronized version of the core console + * + * This class synchronizes calls of the 'Console::vprintf' function as + * used by 'printf' and 'vprintf' to prevent multiple printf-using + * threads within core from interfering with each other. + */ +struct Synchronized_core_console : public Core_console, public Lock +{ + void vprintf(const char *format, va_list list) + { + Lock::Guard lock_guard(*this); + Core_console::vprintf(format, list); + } +}; + + +/** + * Return singleton instance of synchronized core console + */ +static Synchronized_core_console &core_console() +{ + static Synchronized_core_console _console; + return _console; +} + + +void Genode::printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + core_console().vprintf(format, list); + + va_end(list); +} + + +void Genode::vprintf(const char *format, va_list list) +{ + core_console().vprintf(format, list); +} + + diff --git a/base/src/base/console/log_console.cc b/base/src/base/console/log_console.cc new file mode 100644 index 000000000..936a58770 --- /dev/null +++ b/base/src/base/console/log_console.cc @@ -0,0 +1,127 @@ +/* + * \brief Printf backend for the LOG interface + * \author Norman Feske + * \date 2006-09-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +using namespace Genode; + +class Log_console : public Console +{ + private: + + enum { _BUF_SIZE = 216 }; + + Log_connection _log; + char _buf[_BUF_SIZE]; + unsigned _num_chars; + Lock _lock; + + void _flush() + { + /* null-terminate string */ + _buf[_num_chars] = 0; + _log.write(_buf); + + /* restart with empty buffer */ + _num_chars = 0; + } + + protected: + + void _out_char(char c) + { + /* flush full buffer */ + if (_num_chars >= sizeof(_buf) - 1) _flush(); + + /* ensure enough buffer space for complete escape sequence */ + if ((c == 27) && (_num_chars + 8 > _BUF_SIZE)) _flush(); + + _buf[_num_chars++] = c; + + /* flush immediately on line break */ + if (c == '\n') _flush(); + } + + public: + + /** + * Constructor + */ + Log_console() + : + _num_chars(0) + { } + + /** + * Console interface + */ + void vprintf(const char *format, va_list list) + { + Lock::Guard lock_guard(_lock); + Console::vprintf(format, list); + } + + /** + * Return LOG session interface + */ + Log_session *log_session() { return &_log; } +}; + + +/* + * In the presence of a libC, we use the libC's full printf implementation and + * use the 'Log_console' as backend. + */ + +Log_console *stdout_log_console() +{ + /* + * Construct the log console object on the first call of this function. + * In constrast to having a static variable in the global scope, the + * constructor gets only called when needed and no static constructor + * gets invoked by the initialization code. + */ + static Log_console static_log_console; + + return &static_log_console; +} + + +/** + * Hook for supporting libC back ends for stdio + */ +extern "C" int stdout_write(const char *s) +{ + return stdout_log_console()->log_session()->write(s); +} + + +void Genode::printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + vprintf(format, list); + + va_end(list); +} + + +void Genode::vprintf(const char *format, va_list list) +{ + stdout_log_console()->vprintf(format, list); +} diff --git a/base/src/base/cxx/exception.cc b/base/src/base/cxx/exception.cc new file mode 100644 index 000000000..e1ce58927 --- /dev/null +++ b/base/src/base/cxx/exception.cc @@ -0,0 +1,38 @@ +/* + * \brief Support for exceptions libsupc++ + * \author Norman Feske + * \author Sebastian Sumpf + * \date 2006-07-21 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +extern "C" char __eh_frame_start__[]; /* from linker script */ +extern "C" void __register_frame (const void *begin); /* from libgcc_eh */ + +/* + * This symbol is overwritten by Genode's dynamic linker (ld.lib.so). + * After setup, the symbol will point to the actual implementation of + * 'dl_iterate_phdr', which is located within the linker. 'dl_iterate_phdr' + * iterates through all (linker loaded) binaries and shared libraries. This + * function has to be implemented in order to support C++ exceptions within + * shared libraries. + * Return values of dl_iterate_phdr (gcc 4.2.4): + * < 0 = error + * 0 = continue program header iteration + * > 0 = stop iteration (no errors occured) + * + * See also: man dl_iterate_phdr + */ +extern "C" int dl_iterate_phdr(int (*callback) (void *info, unsigned long size, void *data), void *data) __attribute__((weak)); +extern "C" int dl_iterate_phdr(int (*callback) (void *info, unsigned long size, void *data), void *data) { + return -1; } + +void init_exception_handling() { + __register_frame(__eh_frame_start__); } + diff --git a/base/src/base/cxx/guard.cc b/base/src/base/cxx/guard.cc new file mode 100644 index 000000000..dc253d766 --- /dev/null +++ b/base/src/base/cxx/guard.cc @@ -0,0 +1,72 @@ +/* + * \brief Support for guarded variables + * \author Christian Helmuth + * \date 2009-11-18 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +namespace __cxxabiv1 +{ + + /* + * A guarded variable can be in three states: + * + * 1) not initialized + * 2) in initialization (transient) + * 3) initialized + * + * The generic ABI uses the first byte of a 64-bit guard variable for state + * 1), 2) and 3). ARM-EABI uses the first byte of a 32-bit guard variable. + * Therefore we define '__guard' as a 32-bit type and use the least + * significant byte for 1) and 3) and the following byte for 2) and let the + * other threads spin until the guard is released by the thread in + * initialization. + */ + + typedef int __guard; + + extern "C" int __cxa_guard_acquire(__guard *guard) + { + volatile char *initialized = (char *)guard; + volatile int *in_init = (int *)guard; + + /* check for state 3) */ + if (*initialized) return 0; + + /* atomically check and set state 2) */ + if (!Genode::cmpxchg(in_init, 0, 0x100)) { + /* spin until state 3) is reached if other thread is in init */ + while (!*initialized) ; + + /* guard not acquired */ + return 0; + } + + /* + * The guard was acquired. The caller is allowed to initialize the + * guarded variable and required to call __cxa_guard_release() to flag + * initialization completion (and unblock other guard applicants). + */ + return 1; + } + + + extern "C" void __cxa_guard_release(__guard *guard) + { + volatile char *initialized = (char *)guard; + + /* set state 3) */ + *initialized = 1; + } + + + extern "C" void __cxa_guard_abort(__guard *) { } +} diff --git a/base/src/base/cxx/malloc_free.cc b/base/src/base/cxx/malloc_free.cc new file mode 100644 index 000000000..92de4d43a --- /dev/null +++ b/base/src/base/cxx/malloc_free.cc @@ -0,0 +1,92 @@ +/* + * \brief Simplistic malloc and free implementation + * \author Norman Feske + * \date 2006-07-21 + * + * Malloc and free are required by the C++ exception handling. + * Therefore, we provide it here. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + +typedef unsigned long Block_header; + + +extern "C" void *malloc(unsigned size) +{ + /* enforce size to be a multiple of 4 bytes */ + size = (size + 3) & ~3; + + /* + * We store the size of the allocation at the very + * beginning of the allocated block and return + * the subsequent address. This way, we can retrieve + * the size information when freeing the block. + */ + unsigned long real_size = size + sizeof(Block_header); + void *addr = 0; + if (!Genode::env()->heap()->alloc(real_size, &addr)) + return 0; + + *(Block_header *)addr = real_size; + return (Block_header *)addr + 1; +} + + +extern "C" void *calloc(unsigned nmemb, unsigned size) +{ + void *addr = malloc(nmemb*size); + Genode::memset(addr, 0, nmemb*size); + return addr; +} + + +extern "C" void free(void *ptr) +{ + if (!ptr) return; + + unsigned long *addr = ((unsigned long *)ptr) - 1; + Genode::env()->heap()->free(addr, *addr); +} + + +extern "C" void *realloc(void *ptr, Genode::size_t size) +{ + if (!ptr) + return malloc(size); + + if (!size) { + free(ptr); + return 0; + } + + /* determine size of old block content (without header) */ + unsigned long old_size = *((Block_header *)ptr - 1) + - sizeof(Block_header); + + /* do not reallocate if new size is less than the current size */ + if (size <= old_size) + return ptr; + + /* allocate new block */ + void *new_addr = malloc(size); + + /* copy content from old block into new block */ + if (new_addr) + memcpy(new_addr, ptr, Genode::min(old_size, (unsigned long)size)); + + /* free old block */ + free(ptr); + + return new_addr; +} diff --git a/base/src/base/cxx/misc.cc b/base/src/base/cxx/misc.cc new file mode 100644 index 000000000..140ca8fd9 --- /dev/null +++ b/base/src/base/cxx/misc.cc @@ -0,0 +1,202 @@ +/* + * \brief Dummy functions to make the damn thing link + * \author Norman Feske + * \date 2006-04-07 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +using namespace Genode; + + +extern "C" void __cxa_pure_virtual() +{ + PWRN("cxa pure virtual function called, return addr is %p", + __builtin_return_address(0)); +} + + +extern "C" void __pure_virtual() +{ + PWRN("pure virtual function called"); +} + + +/** + * Prototype for exit-handler support function provided by '_main.cc' + */ +extern int genode___cxa_atexit(void(*func)(void*), void *arg, + void *dso_handle); + + +extern "C" int __cxa_atexit(void(*func)(void*), void *arg, + void *dso_handle) +{ + return genode___cxa_atexit(func, arg, dso_handle); +} + + +/*********************************** + ** Support required for ARM EABI ** + ***********************************/ + + +extern "C" void *__aeabi_atexit() { return 0; } + + +extern "C" __attribute__((weak)) +void *__tls_get_addr() +{ + static long dummy_tls; + return &dummy_tls; +} + + +/** + * Not needed for exception-handling init on ARM EABI, + * but called from 'init_exception' + */ +extern "C" __attribute__((weak)) +void __register_frame(void *) { } + + +/** + * Needed to build for OKL4-gta01 with ARM EABI + */ +extern "C" __attribute__((weak)) void raise() +{ + PDBG("raise called - not implemented\n"); +} + + +/*************************** + ** Support for libsupc++ ** + ***************************/ + +extern "C" void *abort(void) +{ + PDBG("abort called"); + sleep_forever(); + return 0; +} + + +extern "C" void *fputc(void) { + return 0; +} + + +extern "C" void *fputs(const char *s, void *) { + PWRN("C++ runtime: %s", s); + return 0; +} + + +extern "C" void *fwrite(void) { + return 0; +} + + +extern "C" int memcmp(const void *p0, const void *p1, size_t size) +{ + return Genode::memcmp(p0, p1, size); +} + + +extern "C" __attribute__((weak)) +void *memcpy(void *dst, void *src, size_t n) +{ +// PDBG("dst=%p, src=%p, n=%d", dst, src, n); + return Genode::memcpy(dst, src, n); +} + + +extern "C" __attribute__((weak)) +void *memmove(void *dst, void *src, size_t n) +{ +// PDBG("dst=%p, src=%p, n=%d", dst, src, n); + return Genode::memmove(dst, src, n); +} + + +extern "C" __attribute__((weak)) +void *memset(void *s, int c, size_t n) +{ +// PDBG("s=%p, c=%d, n=%d", s, c, n); + return Genode::memset(s, c, n); +} + + +extern "C" void *stderr(void) { + PWRN("stderr - not yet implemented"); + return 0; +} + + +/* + * Used when having compiled libsupc++ with the FreeBSD libc + */ +struct FILE; +FILE *__stderrp; + + +extern "C" void *strcat(void) +{ + PWRN("strcat - not yet implemented"); + return 0; +} + + +extern "C" int strncmp(const char *s1, const char *s2, size_t n) +{ + return Genode::strcmp(s1, s2, n); +} + + +extern "C" char *strcpy(char *dest, const char *src) +{ + return Genode::strncpy(dest, src, ~0UL); +} + + +extern "C" size_t strlen(const char *s) +{ + return Genode::strlen(s); +} + + +extern "C" int strcmp(const char *s1, const char *s2) +{ + return Genode::strcmp(s1, s2); +} + + +/* + * Needed by ARM EABI (gcc-4.4 Codesourcery release1039) + */ +extern "C" int sprintf(char *str, const char *format, ...) +{ + PWRN("sprintf - not implemented"); + return 0; +} + + +/********************************** + ** Support for stack protection ** + **********************************/ + +extern "C" __attribute__((weak)) void __stack_chk_fail_local(void) +{ + PERR("Violated stack boundary"); +} diff --git a/base/src/base/cxx/new_delete.cc b/base/src/base/cxx/new_delete.cc new file mode 100644 index 000000000..2615c0b05 --- /dev/null +++ b/base/src/base/cxx/new_delete.cc @@ -0,0 +1,35 @@ +/* + * \brief New and delete are special + * \author Norman Feske + * \date 2006-04-07 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + + +/** + * New operator for allocating from a specified allocator + */ +void *operator new(Genode::size_t size, Genode::Allocator *allocator) +{ + if (!allocator) + throw Genode::Allocator::Out_of_memory(); + + return allocator->alloc(size); +} + +void *operator new [] (Genode::size_t size, Genode::Allocator *allocator) +{ + if (!allocator) + throw Genode::Allocator::Out_of_memory(); + + return allocator->alloc(size); +} diff --git a/base/src/base/cxx/unwind.cc b/base/src/base/cxx/unwind.cc new file mode 100644 index 000000000..c90c7b486 --- /dev/null +++ b/base/src/base/cxx/unwind.cc @@ -0,0 +1,45 @@ +/* + * \brief Wrapper for symbols required by libgcc_eh's exception handling + * \author Sebastian Sumpf + * \date 2011-07-20 + * + * The actual wrapper function is prefixed with '__cxx', the wrapper always + * calls a function with prefix '_cxx' (note the missing '_'). In 'cxx.mk' + * we remove the leading '__cxx' from the wrapper which than becomes the symbol + * of the wrapped function, in turn the wrapped function is prefixed with '_cxx'. + * This whole procedure became necessary since the wrapped symbols are marked + * 'GLOBAL', 'HIDDEN' in libgcc_eh.a and thus ligcc_eh had to be linked to *all* + * binaries. In shared libaries this became unfeasible since libgcc_eh uses + * global data which might not be initialized during cross-library interaction. + * The clean way to go would be to link libgcc_s.so to DSOs and dynamic + * binaries, unfortunally ligcc_s requires libc6 in the current Genode tool + * chain. + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +extern "C" { + + /* Unwind function found in all binaries */ + void _cxx__Unwind_Resume(void *exc) __attribute__((weak)); + void __cxx__Unwind_Resume(void *exc) { + _cxx__Unwind_Resume(exc); } + + /* Special ARM-EABI personality functions */ +#ifdef __ARM_EABI__ + + int _cxx___aeabi_unwind_cpp_pr0(int state, void *block, void *context) __attribute__((weak)); + int __cxx___aeabi_unwind_cpp_pr0(int state, void *block, void *context) { + return _cxx___aeabi_unwind_cpp_pr0(state, block, context); } + + int _cxx___aeabi_unwind_cpp_pr1(int state, void *block, void *context) __attribute__((weak)); + int __cxx___aeabi_unwind_cpp_pr1(int state, void *block, void *context) { + return _cxx___aeabi_unwind_cpp_pr1(state, block, context); } + +#endif +} diff --git a/base/src/base/elf/elf.h b/base/src/base/elf/elf.h new file mode 100644 index 000000000..ea05010c2 --- /dev/null +++ b/base/src/base/elf/elf.h @@ -0,0 +1,254 @@ +/** + * \brief ELF binary definition from GNU C library + * \author Christian Helmuth + * \date 2006-05-04 + * + * This file was slightly modified and stripped down. Original copyright + * header follows: + * + * This file defines standard ELF types, structures, and macros. + * Copyright (C) 1995-2003, 2004, 2005 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + * + * The GNU C Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * The GNU C Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the GNU C Library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + */ + +#ifndef _ELF_H_ +#define _ELF_H_ + +extern "C" { + +/* standard ELF types. */ +#include + +/* type for a 16-bit quantity. */ +typedef genode_uint16_t Elf32_Half; +typedef genode_uint16_t Elf64_Half; + +/* types for signed and unsigned 32-bit quantities */ +typedef genode_uint32_t Elf32_Word; +typedef genode_int32_t Elf32_Sword; +typedef genode_uint32_t Elf64_Word; +typedef genode_int32_t Elf64_Sword; + +/* types for signed and unsigned 64-bit quantities */ +typedef genode_uint64_t Elf32_Xword; +typedef genode_int64_t Elf32_Sxword; +typedef genode_uint64_t Elf64_Xword; +typedef genode_int64_t Elf64_Sxword; + +/* type of addresses */ +typedef genode_uint32_t Elf32_Addr; +typedef genode_uint64_t Elf64_Addr; + +/* type of file offsets */ +typedef genode_uint32_t Elf32_Off; +typedef genode_uint64_t Elf64_Off; + +/* type for section indices, which are 16-bit quantities */ +typedef genode_uint16_t Elf32_Section; +typedef genode_uint16_t Elf64_Section; + +/* type for version symbol information */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/** + * The ELF file header. This appears at the start of every ELF file. + */ +enum { EI_NIDENT = 16 }; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* magic number and other info */ + Elf64_Half e_type; /* object file type */ + Elf64_Half e_machine; /* architecture */ + Elf64_Word e_version; /* object file version */ + Elf64_Addr e_entry; /* entry point virtual address */ + Elf64_Off e_phoff; /* program header table file offset */ + Elf64_Off e_shoff; /* section header table file offset */ + Elf64_Word e_flags; /* processor-specific flags */ + Elf64_Half e_ehsize; /* eLF header size in bytes */ + Elf64_Half e_phentsize; /* program header table entry size */ + Elf64_Half e_phnum; /* program header table entry count */ + Elf64_Half e_shentsize; /* section header table entry size */ + Elf64_Half e_shnum; /* section header table entry count */ + Elf64_Half e_shstrndx; /* section header string table index */ +} Elf64_Ehdr; + +/** + * Fields in the e_ident array. The EI_* macros are indices into the + * array. The macros under each EI_* macro are the values the byte + * may have. + */ +enum { + EI_MAG0 = 0, /* file identification byte 0 index */ + ELFMAG0 = 0x7f, /* magic number byte 0 */ + + EI_MAG1 = 1, /* file identification byte 1 index */ + ELFMAG1 = 'E', /* magic number byte 1 */ + + EI_MAG2 = 2, /* file identification byte 2 index */ + ELFMAG2 = 'L', /* magic number byte 2 */ + + EI_MAG3 = 3, /* file identification byte 3 index */ + ELFMAG3 = 'F', /* magic number byte 3 */ +}; + +/** + * Conglomeration of the identification bytes, for easy testing as a word + */ +const char *ELFMAG = "\177ELF"; + +enum { + SELFMAG = 4, + + EI_CLASS = 4, /* file class byte index */ + ELFCLASSNONE = 0, /* invalid class */ + ELFCLASS32 = 1, /* 32-bit objects */ + ELFCLASS64 = 2, /* 64-bit objects */ + ELFCLASSNUM = 3, + + EI_DATA = 5, /* data encoding byte index */ + ELFDATANONE = 0, /* invalid data encoding */ + ELFDATA2LSB = 1, /* 2's complement, little endian */ + ELFDATA2MSB = 2, /* 2's complement, big endian */ + ELFDATANUM = 3, + + EI_ABIVERSION = 8, /* ABI version */ + + EI_PAD = 9, /* byte index of padding bytes */ +}; + +/** + * Legal values for e_type (object file type) + */ +enum { + ET_NONE = 0, /* no file type */ + ET_EXEC = 2, /* executable file */ + ET_DYN = 3, /* shared object file */ +}; + +/** + * Legal values for e_machine (architecture) + */ +enum { + EM_NONE = 0, /* no machine */ + EM_386 = 3, /* intel 80386 */ +}; + +/** + * Legal values for e_version (version) + */ +enum { + EV_NONE = 0, /* invalid ELF version */ + EV_CURRENT = 1, /* current version */ + EV_NUM = 2, +}; + +/** + * Program segment header + */ +typedef struct +{ + Elf32_Word p_type; /* segment type */ + Elf32_Off p_offset; /* segment file offset */ + Elf32_Addr p_vaddr; /* segment virtual address */ + Elf32_Addr p_paddr; /* segment physical address */ + Elf32_Word p_filesz; /* segment size in file */ + Elf32_Word p_memsz; /* segment size in memory */ + Elf32_Word p_flags; /* segment flags */ + Elf32_Word p_align; /* segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* segment type */ + Elf64_Word p_flags; /* segment flags */ + Elf64_Off p_offset; /* segment file offset */ + Elf64_Addr p_vaddr; /* segment virtual address */ + Elf64_Addr p_paddr; /* segment physical address */ + Elf64_Xword p_filesz; /* segment size in file */ + Elf64_Xword p_memsz; /* segment size in memory */ + Elf64_Xword p_align; /* segment alignment */ +} Elf64_Phdr; + +/** + * Legal values for p_type (segment type) + */ +enum { + PT_NULL = 0, /* program header table entry unused */ + PT_LOAD = 1, /* loadable program segment */ + PT_DYNAMIC = 2, /* dynamic linking information */ + PT_INTERP = 3, /* program interpreter */ + PT_NOTE = 4, /* auxiliary information */ + PT_SHLIB = 5, /* reserved */ + PT_PHDR = 6, /* entry for header table itself */ + PT_TLS = 7, /* thread-local storage segment */ + PT_NUM = 8, /* number of defined types */ + PT_LOOS = 0x60000000, /* start of OS-specific */ + PT_GNU_EH_FRAME = 0x6474e550, /* gcc .eh_frame_hdr segment */ + PT_GNU_STACK = 0x6474e551, /* indicates stack executability */ + PT_GNU_RELRO = 0x6474e552, /* read-only after relocation */ + PT_LOPROC = 0x70000000, /* first processor-specific type */ + PT_HIPROC = 0x7fffffff, /* last processor-specific type */ +}; + +/** + * Legal values for p_flags (segment flags) + */ +enum { + PF_X = (1 << 0), /* segment is executable */ + PF_W = (1 << 1), /* segment is writable */ + PF_R = (1 << 2), /* segment is readable */ +}; + +/** + * Define bit-width independent types + */ + +#ifdef _LP64 +typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Phdr Elf_Phdr; +#define ELFCLASS ELFCLASS64 +#else +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; +#define ELFCLASS ELFCLASS32 +#endif /* _LP64 */ + +} /* extern "C" */ + +#endif /* _ELF_H_ */ diff --git a/base/src/base/elf/elf_binary.cc b/base/src/base/elf/elf_binary.cc new file mode 100644 index 000000000..6f5f29df1 --- /dev/null +++ b/base/src/base/elf/elf_binary.cc @@ -0,0 +1,159 @@ +/** + * \brief ELF binary utility + * \author Christian Helmuth + * \date 2006-05-04 + * + * XXX define some useful return values for error checking + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include "elf.h" + +using namespace Genode; + + +int Elf_binary::_ehdr_check_compat() +{ + Elf_Ehdr *ehdr = (Elf_Ehdr *)_start; + + if (memcmp(ehdr, ELFMAG, SELFMAG) != 0) { + PERR("binary is not an ELF"); + return -1; + } + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS) { + PERR("support for 32/64-bit objects only"); + return -1; + } + + /* start executeables and shared objects with entry points only */ + if (!(ehdr->e_type == ET_EXEC || (ehdr->e_type == ET_DYN && ehdr->e_entry))) { + PERR("program is no executable"); + return -1; + } + + return 0; +} + + +bool inline Elf_binary::_dynamic_check_compat(unsigned type) +{ + switch (type) { + case PT_NULL: + case PT_LOAD: + case PT_DYNAMIC: + case PT_INTERP: + case PT_PHDR: + case PT_GNU_EH_FRAME: + case PT_GNU_STACK: + case PT_NOTE: + return true; + default: + break; + } + + if (type >= PT_LOPROC && type <= PT_HIPROC) + return true; + + return false; +} + + +int Elf_binary::_ph_table_check_compat() +{ + Elf_Phdr *ph_table = (Elf_Phdr *)_ph_table; + unsigned num = _phnum; + unsigned i; + + for (i = 0; i < num; i++) { + if (!_dynamic_check_compat(ph_table[i].p_type) /* ignored */) { + PWRN("unsupported program segment type 0x%x", ph_table[i].p_type); + return -1; + } + if (ph_table[i].p_type == PT_LOAD) + if (ph_table[i].p_align & (0x1000 - 1)) { + PWRN("unsupported alignment 0x%lx", (unsigned long) ph_table[i].p_align); + return -1; + } + if (ph_table[i].p_type == PT_DYNAMIC) + _dynamic = true; + + if (ph_table[i].p_type == PT_INTERP) { + Elf_Phdr *phdr = &((Elf_Phdr *)_ph_table)[i]; + char *interp = (char *)(_start + phdr->p_offset); + + if (!strcmp(interp, "ld.lib.so")) + _interp = true; + } + } + + return 0; +} + + +Elf_segment Elf_binary::get_segment(unsigned num) +{ + void *start; + size_t offset, filesz, memsz; + Elf_binary::Flags flags = { 0, 0, 0, 0 }; + + if (!valid()) return Elf_segment(); + + if (!(num < _phnum)) return Elf_segment(); + + Elf_Phdr *phdr = &((Elf_Phdr *)_ph_table)[num]; + + start = (void *)phdr->p_vaddr; + offset = phdr->p_offset; + filesz = phdr->p_filesz; + memsz = phdr->p_memsz; + + flags.r = (phdr->p_flags & PF_R) ? 1 : 0; + flags.w = (phdr->p_flags & PF_W) ? 1 : 0; + flags.x = (phdr->p_flags & PF_X) ? 1 : 0; + + /* + * Skip loading of ELF segments that are not PT_LOAD or have no memory + * size. + */ + if (phdr->p_type != PT_LOAD || !memsz) + flags.skip = 1; + + return Elf_segment(this, start, offset, filesz, memsz, flags); +} + + +Elf_binary::Elf_binary(addr_t start) +: _valid(false), _dynamic(false), _interp(false), _start(start) +{ + Elf_Ehdr *ehdr = (Elf_Ehdr *)start; + + /* check for unsupported ELF features */ + if (_ehdr_check_compat()) return; + + /* program entry point */ + if (!(_entry = ehdr->e_entry)) return; + + /* segment tables */ + _ph_table = _start + ehdr->e_phoff; + _phentsize = ehdr->e_phentsize; + _phnum = ehdr->e_phnum; + + /* program segments */ + if (_ph_table_check_compat()) return; + + /* ready to rock */ + _valid = true; +} diff --git a/base/src/base/env/context_area.cc b/base/src/base/env/context_area.cc new file mode 100644 index 000000000..e233c186c --- /dev/null +++ b/base/src/base/env/context_area.cc @@ -0,0 +1,47 @@ +/* + * \brief Process-local thread-context area + * \author Norman Feske + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + + +struct Context_area_rm_session : Genode::Rm_connection +{ + Context_area_rm_session() + : Genode::Rm_connection(0, Genode::Thread_base::CONTEXT_AREA_VIRTUAL_SIZE) + { + using namespace Genode; + + addr_t local_base = Thread_base::CONTEXT_AREA_VIRTUAL_BASE; + size_t size = Thread_base::CONTEXT_AREA_VIRTUAL_SIZE; + + env()->rm_session()->attach_at(dataspace(), local_base, size); + } +}; + + +namespace Genode { + + Rm_session *env_context_area_rm_session() + { + static Context_area_rm_session inst; + return &inst; + } + + Ram_session *env_context_area_ram_session() + { + return env()->ram_session(); + } +} + diff --git a/base/src/base/env/env.cc b/base/src/base/env/env.cc new file mode 100644 index 000000000..500b5bf31 --- /dev/null +++ b/base/src/base/env/env.cc @@ -0,0 +1,31 @@ +/* + * \brief Environment initialization + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-27 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +namespace Genode { + + /* + * Request pointer to static environment of the Genode application + */ + Env *env() + { + /* + * By placing the environment as static object here, we ensure that its + * constructor gets called when this function is used the first time. + */ + static Genode::Platform_env _env; + return &_env; + } +} diff --git a/base/src/base/heap/heap.cc b/base/src/base/heap/heap.cc new file mode 100644 index 000000000..3f5c99adb --- /dev/null +++ b/base/src/base/heap/heap.cc @@ -0,0 +1,150 @@ +/* + * \brief Implementation of Genode heap partition + * \author Norman Feske + * \date 2006-05-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +using namespace Genode; + + +Heap::Dataspace_pool::~Dataspace_pool() +{ + /* free all ram_dataspaces */ + for (Dataspace *ds; (ds = first()); ) { + + /* + * read dataspace capability and modify _ds_list before detaching + * possible backing store for Dataspace - we rely on LIFO list + * manipulation here! + */ + + Ram_dataspace_capability ds_cap = ds->cap; + + remove(ds); + _rm_session->detach(ds->local_addr); + _ram_session->free(ds_cap); + } +} + + +int Heap::Dataspace_pool::expand(size_t size, Range_allocator *alloc) +{ + Ram_dataspace_capability new_ds_cap; + void *local_addr, *ds_addr = 0; + + /* make new ram dataspace available at our local address space */ + try { + new_ds_cap = _ram_session->alloc(size); + local_addr = _rm_session->attach(new_ds_cap); + } catch (Ram_session::Alloc_failed) { + return -2; + } catch (Rm_session::Attach_failed) { + _ram_session->free(new_ds_cap); + return -3; + } + + /* add new local address range to our local allocator */ + alloc->add_range((addr_t)local_addr, size); + + /* now that we have new backing store, allocate Dataspace structure */ + if (!alloc->alloc_aligned(sizeof(Dataspace), &ds_addr, 2)) { + PWRN("could not allocate meta data - this should never happen"); + return -1; + } + + /* add dataspace information to list of dataspaces */ + Dataspace *ds = reinterpret_cast(ds_addr); + ds->cap = new_ds_cap; + ds->local_addr = local_addr; + insert(ds); + + return 0; +} + + +int Heap::quota_limit(size_t new_quota_limit) +{ + if (new_quota_limit < _quota_used) return -1; + _quota_limit = new_quota_limit; + return 0; +} + + +bool Heap::_try_local_alloc(size_t size, void **out_addr) +{ + if (!_alloc.alloc_aligned(size, out_addr, 2)) + return false; + + _quota_used += size; + return true; +} + + +bool Heap::alloc(size_t size, void **out_addr) +{ + /* serialize access of heap functions */ + Lock::Guard lock_guard(_lock); + + /* check requested allocation against quota limit */ + if (size + _quota_used > _quota_limit) + return false; + + /* try allocation at our local allocator */ + if (_try_local_alloc(size, out_addr)) + return true; + + /* + * Calculate block size of needed backing store. The block must hold the + * requested 'size' and a new Dataspace structure if the allocation above + * failed. Finally, we align the size to a 4K page. + */ + size_t request_size = size + 1024; + + if (request_size < _chunk_size*sizeof(umword_t)) { + request_size = _chunk_size*sizeof(umword_t); + + /* + * Exponentially increase chunk size with each allocated chunk until + * we hit 'MAX_CHUNK_SIZE'. + */ + _chunk_size = min(2*_chunk_size, (size_t)MAX_CHUNK_SIZE); + } + + if (_ds_pool.expand(align_addr(request_size, 12), &_alloc) < 0) { + PWRN("could not expand dataspace pool"); + return 0; + } + + /* allocate originally requested block */ + return _try_local_alloc(size, out_addr); +} + + +void Heap::free(void *addr, size_t size) +{ + /* serialize access of heap functions */ + Lock::Guard lock_guard(_lock); + + /* forward request to our local allocator */ + _alloc.free(addr, size); + + _quota_used -= size; + + /* + * We could check for completely unused dataspaces... + * Yes, we could... + */ +} diff --git a/base/src/base/heap/sliced_heap.cc b/base/src/base/heap/sliced_heap.cc new file mode 100644 index 000000000..4a1957ab8 --- /dev/null +++ b/base/src/base/heap/sliced_heap.cc @@ -0,0 +1,114 @@ +/* + * \brief Heap that stores each block at a separate dataspace + * \author Norman Feske + * \date 2006-08-16 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +namespace Genode { + + class Sliced_heap::Block : public List::Element + { + private: + + Ram_dataspace_capability _ds_cap; + size_t _size; + char _data[]; + + public: + + inline void *operator new(size_t size, void *at_addr) { + return at_addr; } + + /** + * Constructor + */ + Block(Ram_dataspace_capability ds_cap, size_t size): + _ds_cap(ds_cap), _size(size) { } + + /** + * Accessors + */ + Ram_dataspace_capability ds_cap() { return _ds_cap; } + size_t size() { return _size; } + void *data_start() { return &_data[0]; } + + /** + * Lookup Slab_entry by given address + * + * The specified address is supposed to point to data[0]. + */ + static Block *block(void *addr) { + return (Block *)((addr_t)addr - sizeof(Block)); } + }; +} + +using namespace Genode; + + +Sliced_heap::Sliced_heap(Ram_session *ram_session, Rm_session *rm_session): + _ram_session(ram_session), _rm_session(rm_session), + _consumed(0) { } + + +Sliced_heap::~Sliced_heap() +{ + for (Block *b; (b = _block_list.first()); ) + free(b->data_start(), b->size()); } + + +bool Sliced_heap::alloc(size_t size, void **out_addr) +{ + /* serialize access to block list */ + Lock::Guard lock_guard(_lock); + + /* allocation includes space for block meta data and is page-aligned */ + size = align_addr(size + sizeof(Block), 12); + + Ram_dataspace_capability ds_cap; + void *local_addr; + + try { + ds_cap = _ram_session->alloc(size); + local_addr = _rm_session->attach(ds_cap); + } catch (Rm_session::Attach_failed) { + PERR("Could not attach dataspace to local address space"); + _ram_session->free(ds_cap); + return false; + } catch (Ram_session::Alloc_failed) { + PERR("Could not allocate dataspace with size %zd", size); + return false; + } + + Block *b = new(local_addr) Block(ds_cap, size); + _consumed += size; + _block_list.insert(b); + *out_addr = b->data_start(); + return true; +} + + +void Sliced_heap::free(void *addr, size_t size) +{ + /* serialize access to block list */ + Lock::Guard lock_guard(_lock); + + Block *b = Block::block(addr); + _block_list.remove(b); + _consumed -= b->size(); + Ram_dataspace_capability ds_cap = b->ds_cap(); + _rm_session->detach(b); + _ram_session->free(ds_cap); +} + + +size_t Sliced_heap::overhead(size_t size) { return align_addr(size + sizeof(Block), 12) - size; } diff --git a/base/src/base/lock/lock.cc b/base/src/base/lock/lock.cc new file mode 100644 index 000000000..9fd26b443 --- /dev/null +++ b/base/src/base/lock/lock.cc @@ -0,0 +1,214 @@ +/* + * \brief Lock implementation + * \author Norman Feske + * \date 2009-03-25 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include + +using namespace Genode; + + +/** + * Track interesting lock conditions, counters are only used for testing + */ +int debug_spinlock_contention_cnt; +int debug_lock_sleep_race_cnt; + + +/*************** + ** Utilities ** + ***************/ + +/* + * Spinlock functions used for protecting the critical sections within the + * 'lock' and 'unlock' functions. Contention in these short-running code + * portions is rare but is must be considered. + */ + +enum State { SPINLOCK_LOCKED, SPINLOCK_UNLOCKED }; + +static inline void spinlock_lock(volatile int *lock_variable) +{ + while (!cmpxchg(lock_variable, SPINLOCK_UNLOCKED, SPINLOCK_LOCKED)) { + + debug_spinlock_contention_cnt++; /* only for statistics */ + + /* + * Yield our remaining time slice to help the spinlock holder to pass + * the critical section. + */ + thread_yield(); + } +} + + +static inline void spinlock_unlock(volatile int *lock_variable) +{ + *lock_variable = SPINLOCK_UNLOCKED; +} + + +/******************** + ** Lock applicant ** + ********************/ + +void Cancelable_lock::Applicant::wake_up() +{ + if (!thread_id_valid(_tid)) return; + + /* + * Deal with the race that may occur in the 'lock' function between + * releasing the spinlock and calling 'L4_Stop'. + */ + + for (;;) { + + if (thread_check_stopped_and_restart(_tid)) + return; + + debug_lock_sleep_race_cnt++; /* only for statistics */ + thread_switch_to(_tid); + } +} + + +/********************* + ** Cancelable lock ** + *********************/ + +void Cancelable_lock::lock() +{ + Applicant myself(thread_get_my_native_id()); + + spinlock_lock(&_spinlock_state); + + /* reset ownership if one thread 'lock' twice */ + if (_owner == myself) + _owner = Applicant(thread_invalid_id()); + + if (cmpxchg(&_state, UNLOCKED, LOCKED)) { + + /* we got the lock */ + _owner = myself; + _last_applicant = &_owner; + spinlock_unlock(&_spinlock_state); + return; + } + + /* + * We failed to grab the lock, lets add ourself to the + * list of applicants and block for the current lock holder. + */ + + _last_applicant->applicant_to_wake_up(&myself); + _last_applicant = &myself; + spinlock_unlock(&_spinlock_state); + + /* + * At this point, a race can happen. We have added ourself to the wait + * queue but do not block yet. If we get preempted here, the lock holder + * may call 'unlock' and thereby find us as the next applicant to wake up. + * However, the 'L4_Start' call will then be issued before we went to sleep + * via 'L4_Stop'. When we get scheduled for the next time, we are expected + * to enter the critical section but we will execute 'L4_Stop' instead. + * We handle this case in the 'unlock' function by checking the previous + * thread state when resuming its execution. + * + * Note for testing: To artificially increase the chance for triggering the + * race condition, we can delay the execution here. For example via: + * + * ! for (int i = 0; i < 10; i++) + * ! thread_yield(); + */ + thread_stop_myself(); + + /* + * We expect to be the lock owner when woken up. If this is not + * the case, the blocking was canceled via core's cancel-blocking + * mechanism. We have to dequeue ourself from the list of applicants + * and reflect this condition as a C++ exception. + */ + spinlock_lock(&_spinlock_state); + if (_owner != myself) { + + + /* check if we are the applicant to be waken up next */ + if (*_owner.applicant_to_wake_up() == myself) { + _owner.applicant_to_wake_up(myself.applicant_to_wake_up()); + + /* otherwise, go through the list of remaining applicants */ + } else { + + Applicant *a = _owner.applicant_to_wake_up(); + for (; a; a = a->applicant_to_wake_up()) { + + /* remove reference to ourself from the applicants list */ + if (a->applicant_to_wake_up() == &myself) { + a->applicant_to_wake_up(myself.applicant_to_wake_up()); + break; + } + } + } + + spinlock_unlock(&_spinlock_state); + + throw Blocking_canceled(); + } + spinlock_unlock(&_spinlock_state); +} + + +void Cancelable_lock::unlock() +{ + spinlock_lock(&_spinlock_state); + + Applicant *next_owner = _owner.applicant_to_wake_up(); + + if (next_owner) { + + /* transfer lock ownership to next applicant and wake him up */ + _owner = *next_owner; + if (_last_applicant == next_owner) + _last_applicant = &_owner; + + spinlock_unlock(&_spinlock_state); + + _owner.wake_up(); + + } else { + + /* there is no further applicant, leave the lock alone */ + _owner = Applicant(thread_invalid_id()); + _last_applicant = 0; + _state = UNLOCKED; + + spinlock_unlock(&_spinlock_state); + } +} + + +Cancelable_lock::Cancelable_lock(Cancelable_lock::State initial) +: + _spinlock_state(SPINLOCK_UNLOCKED), + _state(UNLOCKED), + _last_applicant(0), + _owner(thread_invalid_id()) +{ + if (initial == LOCKED) + lock(); +} + diff --git a/base/src/base/process/process.cc b/base/src/base/process/process.cc new file mode 100644 index 000000000..fa09a23ae --- /dev/null +++ b/base/src/base/process/process.cc @@ -0,0 +1,279 @@ +/* + * \brief Process creation + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-18 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +using namespace Genode; + +Dataspace_capability Process::_dynamic_linker_cap; + +/** + * Check for dynamic ELF header + * + * \param elf_ds_cap dataspace containing the ELF binary + */ + +static bool _check_dynamic_elf(Dataspace_capability elf_ds_cap) +{ + /* attach ELF locally */ + addr_t elf_addr; + try { elf_addr = env()->rm_session()->attach(elf_ds_cap); } + catch (Rm_session::Attach_failed) { return false; } + + /* read program header */ + Elf_binary elf((addr_t)elf_addr); + env()->rm_session()->detach((void *)elf_addr); + + return elf.is_dynamically_linked(); +} + +/** + * Parse ELF and setup segment dataspace + * + * \param parent_cap parent capability for child (i.e. myself) + * \param elf_ds_cap dataspace containing the ELF binary + * \param ram RAM session of the new protection domain + * \param rm region manager session of the new protection domain + */ +static addr_t _setup_elf(Parent_capability parent_cap, + Dataspace_capability elf_ds_cap, + Ram_session &ram, Rm_session &rm) +{ + /* attach ELF locally */ + addr_t elf_addr; + try { elf_addr = env()->rm_session()->attach(elf_ds_cap); } + catch (Rm_session::Attach_failed) { return 0; } + + /* setup ELF object and read program entry pointer */ + Elf_binary elf((addr_t)elf_addr); + if (!elf.valid()) + return 0; + + /* + * entry point of program - can be set to 0 to indicate errors in ELF + * handling + */ + addr_t entry = elf.entry(); + + /* setup region map for the new pd */ + Elf_segment seg; + + for (unsigned n = 0; (seg = elf.get_segment(n)).valid(); ++n) { + if (seg.flags().skip) continue; + + /* same values for r/o and r/w segments */ + addr_t addr = (addr_t)seg.start(); + size_t size = seg.mem_size(); + + bool parent_info = false; + off_t offset; + Dataspace_capability ds_cap; + + bool write = seg.flags().w; + if (write) { + + /* read-write segment */ + offset = 0; + + /* alloc dataspace */ + try { ds_cap = ram.alloc(size); } + catch (Ram_session::Alloc_failed) { + PERR("Ram.alloc() failed"); + entry = 0; + break; + } + + /* attach dataspace */ + void *base; + try { base = env()->rm_session()->attach(ds_cap); } + catch (Rm_session::Attach_failed) { + PERR("env()->rm_session()->attach() failed"); + entry = 0; + break; + } + + void *ptr = base; + addr_t laddr = elf_addr + seg.file_offset(); + + /* copy contents and fill with zeros */ + memcpy(ptr, (void *)laddr, seg.file_size()); + if (size > seg.file_size()) + memset((void *)((addr_t)ptr + seg.file_size()), + 0, size - seg.file_size()); + + /* + * we store the parent information at the beginning of the first + * data segment + */ + if (!parent_info) { + memcpy(ptr, &parent_cap, sizeof(parent_cap)); + parent_info = true; + } + + /* detach dataspace */ + env()->rm_session()->detach(base); + + } else { + + /* read-only segment */ + offset = seg.file_offset(); + ds_cap = elf_ds_cap; + + /* XXX currently we assume r/o segment sizes never differ */ + if (seg.file_size() != seg.mem_size()) + PWRN("filesz and memsz for read-only segment differ"); + } + + void *out_ptr = 0; + try { out_ptr = rm.attach(ds_cap, size, offset, true, addr); } + catch (Rm_session::Attach_failed) { } + + if ((addr_t)out_ptr != addr) + PWRN("addresses differ after attach (addr=%p out_ptr=%p)", + (void *)addr, out_ptr); + } + + /* detach ELF */ + env()->rm_session()->detach((void *)elf_addr); + + return entry; +} + + +Process::Process(Dataspace_capability elf_ds_cap, + Ram_session_capability ram_session_cap, + Cpu_session_capability cpu_session_cap, + Rm_session_capability rm_session_cap, + Parent_capability parent_cap, + const char *name, + char *const argv[]) +: + _cpu_session_client(cpu_session_cap), + _rm_session_client(rm_session_cap) +{ + if (!_pd.cap().valid()) + return; + + enum Local_exception + { + THREAD_FAIL, ELF_FAIL, ASSIGN_PARENT_FAIL, THREAD_ADD_FAIL, + THREAD_BIND_FAIL, THREAD_PAGER_FAIL, THREAD_START_FAIL, + }; + + /* XXX this only catches local exceptions */ + + /* FIXME find sane quota values or make them configurable */ + try { + int err; + + /* create thread0 */ + try { + _thread0_cap = _cpu_session_client.create_thread(name); + } catch (Cpu_session::Thread_creation_failed) { + PERR("Creation of thread0 failed"); + throw THREAD_FAIL; + } + + /* check for dynamic program header */ + if (_check_dynamic_elf(elf_ds_cap)) { + if (!_dynamic_linker_cap.valid()) { + PERR("Dynamically linked file found, but no dynamic linker binary present"); + throw ELF_FAIL; + } + elf_ds_cap = _dynamic_linker_cap; + } + + /* init temporary allocator object */ + Ram_session_client ram(ram_session_cap); + + /* parse ELF binary and setup segment dataspaces */ + addr_t entry = _setup_elf(parent_cap, elf_ds_cap, ram, _rm_session_client); + if (!entry) { + PERR("Setup ELF failed"); + throw ELF_FAIL; + } + + /* register parent interface for new protection domain */ + if (_pd.assign_parent(parent_cap)) { + PERR("Could not assign parent interface to new PD"); + throw ASSIGN_PARENT_FAIL; + } + + /* bind thread0 */ + err = _pd.bind_thread(_thread0_cap); + if (err) { + PERR("Thread binding failed (%d)", err); + throw THREAD_BIND_FAIL; + } + + /* register thread0 at region manager session */ + Pager_capability pager; + try { + pager = _rm_session_client.add_client(_thread0_cap); + } catch (...) { + PERR("Pager setup failed (%d)", err); + throw THREAD_ADD_FAIL; + } + + /* set pager in thread0 */ + err = _cpu_session_client.set_pager(_thread0_cap, pager); + if (err) { + PERR("Setting pager for thread0 failed"); + throw THREAD_PAGER_FAIL; + } + + /* start thread */ + err = _cpu_session_client.start(_thread0_cap, entry, 0 /* unused */); + if (err) { + PERR("Thread0 startup failed"); + throw THREAD_START_FAIL; + } + + } + catch (Local_exception cause) { + + switch (cause) { + + case THREAD_START_FAIL: + case THREAD_PAGER_FAIL: + case THREAD_ADD_FAIL: + case THREAD_BIND_FAIL: + case ASSIGN_PARENT_FAIL: + case ELF_FAIL: + + _cpu_session_client.kill_thread(_thread0_cap); + + case THREAD_FAIL: + + default: + PWRN("unknown exception?"); + } + } +} + + +Process::~Process() +{ + /* + * Try to kill thread0, which was created in the process constructor. If + * this fails, do nothing. + */ + try { _cpu_session_client.kill_thread(_thread0_cap); } + catch (Genode::Ipc_error) { } +} diff --git a/base/src/base/server/common.cc b/base/src/base/server/common.cc new file mode 100644 index 000000000..5e0b07f2d --- /dev/null +++ b/base/src/base/server/common.cc @@ -0,0 +1,151 @@ +/* + * \brief Platform-independent part of server-side RPC framework + * \author Norman Feske + * \date 2006-05-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + + +void Rpc_entrypoint::_dissolve(Rpc_object_base *obj) +{ + /* make sure nobody is able to find this object */ + remove(obj); + + /* + * The activation may execute a blocking operation in a dispatch function. + * Before resolving the corresponding object, we need to ensure that it is + * no longer used. Therefore, we to need cancel an eventually blocking + * operation and let the activation leave the context of the object. + */ + _leave_server_object(obj); + + /* wait until nobody is inside dispatch */ + obj->lock(); + + _cap_session->free(obj->cap()); + + /* now the object may be safely destructed */ +} + + +void Rpc_entrypoint::entry() +{ + Ipc_server srv(&_snd_buf, &_rcv_buf); + _ipc_server = &srv; + _cap = srv; + _cap_valid.unlock(); + + /* + * Now, the capability of the server activation is initialized + * an can be passed around. However, the processing of capability + * invocations should not happen until activation-using server + * is completely initialized. Thus, we wait until the activation + * gets explicitly unblocked by calling 'Rpc_entrypoint::activate()'. + */ + _delay_start.lock(); + + while (1) { + int opcode = 0; + + srv >> IPC_REPLY_WAIT >> opcode; + + /* set default return value */ + srv.ret(ERR_INVALID_OBJECT); + + /* atomically lookup and lock referenced object */ + { + Lock::Guard lock_guard(_curr_obj_lock); + + _curr_obj = obj_by_id(srv.badge()); + if (!_curr_obj) + continue; + + _curr_obj->lock(); + } + + /* dispatch request */ + try { srv.ret(_curr_obj->dispatch(opcode, srv, srv)); } + catch (Blocking_canceled) { } + + _curr_obj->unlock(); + _curr_obj = 0; + } +} + + +void Rpc_entrypoint::_leave_server_object(Rpc_object_base *obj) +{ + Lock::Guard lock_guard(_curr_obj_lock); + + if (obj == _curr_obj) + cancel_blocking(); +} + + +void Rpc_entrypoint::_block_until_cap_valid() +{ + _cap_valid.lock(); +} + + +Untyped_capability Rpc_entrypoint::reply_dst() +{ + return _ipc_server ? _ipc_server->dst() : Untyped_capability(); +} + + +void Rpc_entrypoint::omit_reply() +{ + /* set current destination to an invalid capability */ + if (_ipc_server) _ipc_server->dst(Untyped_capability()); +} + + +void Rpc_entrypoint::explicit_reply(Untyped_capability reply_cap, int return_value) +{ + if (!_ipc_server) return; + + /* backup reply capability of current request */ + Untyped_capability last_reply_cap = _ipc_server->dst(); + + /* direct ipc server to the specified reply destination */ + _ipc_server->ret(return_value); + _ipc_server->dst(reply_cap); + *_ipc_server << IPC_REPLY; + + /* restore reply capability of the original request */ + _ipc_server->dst(last_reply_cap); +} + + +void Rpc_entrypoint::activate() +{ + _delay_start.unlock(); +} + + +Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size, + char const *name, bool start_on_construction) +: + Thread_base(name, stack_size), + _cap(Untyped_capability()), + _curr_obj(0), _cap_valid(Lock::LOCKED), _delay_start(Lock::LOCKED), + _cap_session(cap_session) +{ + Thread_base::start(); + _block_until_cap_valid(); + + if (start_on_construction) + activate(); +} diff --git a/base/src/base/server/server.cc b/base/src/base/server/server.cc new file mode 100644 index 000000000..2d38da381 --- /dev/null +++ b/base/src/base/server/server.cc @@ -0,0 +1,39 @@ +/* + * \brief Default version of platform-specific part of RPC framework + * \author Norman Feske + * \date 2006-05-12 + * + * This version is suitable for platforms similar to L4. Each platform + * for which this implementation is not suited contains a platform- + * specific version in its respective 'base-' repository. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +using namespace Genode; + + +/*********************** + ** Server entrypoint ** + ***********************/ + +Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj) +{ + Untyped_capability ep_cap = Native_capability(_cap.tid(), 0); + Untyped_capability new_obj_cap = _cap_session->alloc(ep_cap); + + /* add server object to object pool */ + obj->cap(new_obj_cap); + insert(obj); + + /* return capability that uses the object id as badge */ + return new_obj_cap; +} diff --git a/base/src/base/signal/signal.cc b/base/src/base/signal/signal.cc new file mode 100644 index 000000000..51c6192d5 --- /dev/null +++ b/base/src/base/signal/signal.cc @@ -0,0 +1,299 @@ +/* + * \brief Generic implementation parts of the signaling framework + * \author Norman Feske + * \date 2008-09-16 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +using namespace Genode; + + +/** + * Return process-wide signal session used for signal allocation and submission + */ +static Signal_connection *signal_connection() +{ + static Signal_connection sc; + return ≻ +} + + +/****************************************************** + ** Process-wide connection to core's signal service ** + ******************************************************/ + +enum { STACK_SIZE = 4096 }; + +class Signal_handler_thread : Thread, Lock +{ + private: + + /** + * Return process-wide signal source used for signal reception + * + * This function must be called from the context of the signal handler + * thread because on some platforms (e.g., Fiasco.OC), the calling + * thread context is used for implementing the signal-source protocol. + */ + static Signal_source *signal_source(); + + void entry() + { + Signal_source *source = signal_source(); + unlock(); + Signal_receiver::dispatch_signals(source); + } + + public: + + /** + * Constructor + */ + Signal_handler_thread() + : Thread("signal handler"), Lock(Lock::LOCKED) + { + start(); + + /* + * Make sure to have initialized the 'signal_source()' channel + * before proceeding with the use of signals. Otherwise, signals + * that occurred until the construction of 'signal_source' is + * completed may get lost. + */ + lock(); + } +}; + + +Signal_source *Signal_handler_thread::signal_source() +{ + static Signal_source_client sigsrc(signal_connection()->signal_source()); + return &sigsrc; +} + + +/** + * Return process-wide signal source used for signal reception + */ +static Signal_handler_thread *signal_handler_thread() +{ + static Signal_handler_thread signal_handler_thread; + return &signal_handler_thread; +} + + + +/************************ + ** Signal transmitter ** + ************************/ + +Signal_transmitter::Signal_transmitter(Signal_context_capability context) +: _context(context) { } + + +void Signal_transmitter::context(Signal_context_capability context) +{ + _context = context; +} + + +void Signal_transmitter::submit(int cnt) +{ + signal_connection()->submit(_context, cnt); +} + + +/********************* + ** Signal receiver ** + *********************/ + +void Signal_receiver::_unsynchronized_dissolve(Signal_context *context) +{ + /* tell core to stop sending signals referring to the context */ + signal_connection()->free_context(context->_cap); + + /* restore default initialization of signal context */ + context->_receiver = 0; + context->_list_element.context = 0; + context->_cap = Signal_context_capability(); + + /* remove context from context list */ + _contexts.remove(&context->_list_element); +} + + +Signal_receiver::Signal_receiver() +{ + /* make sure that the process-local signal handler thread is running */ + signal_handler_thread(); +} + + +Signal_receiver::~Signal_receiver() +{ + Lock::Guard list_lock_guard(_contexts_lock); + + /* disassociate contexts from the receiver */ + for (Signal_context::List_element *le; (le = _contexts.first()); ) + _unsynchronized_dissolve(le->context); +} + + +Signal_context_capability Signal_receiver::manage(Signal_context *context) +{ + if (context->_receiver) + throw Context_already_in_use(); + + context->_receiver = this; + context->_list_element.context = context; + + Lock::Guard list_lock_guard(_contexts_lock); + + /* insert context into context list */ + _contexts.insert(&context->_list_element); + + bool try_again; + do { + try_again = false; + try { + + /* use signal context as imprint */ + context->_cap = signal_connection()->alloc_context((long)context); + return context->_cap; + + } catch (Signal_session::Out_of_metadata) { + + /* give up if the error occurred a second time */ + if (try_again) + break; + + PINF("upgrade quota donation for SIGNAL session"); + env()->parent()->upgrade(signal_connection()->cap(), "ram_quota=4K"); + try_again = true; + } + } while (try_again); + return Signal_context_capability(); +} + + +void Signal_receiver::dissolve(Signal_context *context) +{ + if (context->_receiver != this) + throw Context_not_associated(); + + Lock::Guard list_lock_guard(_contexts_lock); + + _unsynchronized_dissolve(context); +} + + +bool Signal_receiver::pending() +{ + Lock::Guard list_lock_guard(_contexts_lock); + + /* look up the contexts for the pending signal */ + for (Signal_context::List_element *le = _contexts.first(); le; le = le->next()) { + + Signal_context *context = le->context; + + Lock::Guard lock_guard(context->_lock); + + if (context->_pending) + return true; + } + return false; +} + + +Signal Signal_receiver::wait_for_signal() +{ + for (;;) { + + /* block until the receiver has received a signal */ + _signal_available.down(); + + Lock::Guard list_lock_guard(_contexts_lock); + + /* look up the contexts for the pending signal */ + for (Signal_context::List_element *le = _contexts.first(); le; le = le->next()) { + + Signal_context *context = le->context; + + Lock::Guard lock_guard(context->_lock); + + /* check if context has a pending signal */ + if (!context->_pending) + continue; + + context->_pending = false; + Signal result = context->_curr_signal; + + /* invalidate current signal in context */ + context->_curr_signal = Signal(0, 0); + + /* return last received signal */ + return result; + } + + /* + * Normally, we should never arrive at this point because that would + * mean, the '_signal_available' semaphore was increased without + * registering the signal in any context associated to the receiver. + */ + class Wait_for_signal_unexpected_error { }; + throw Wait_for_signal_unexpected_error(); + } + return Signal(0, 0); /* unreachable */ +} + + +void Signal_receiver::local_submit(Signal ns) +{ + Signal_context *context = ns.context(); + + if (!context) return; + + Lock::Guard lock_guard(context->_lock); + + /* + * Replace current signal of the context by signal with accumulated + * counters. In the common case, the current signal is an invalid + * signal with a counter value of zero. + */ + int num = context->_curr_signal.num() + ns.num(); + context->_curr_signal = Signal(context, num); + + /* wake up the receiver if the context becomes pending */ + if (!context->_pending) { + context->_pending = true; + _signal_available.up(); + } +} + + +void Signal_receiver::dispatch_signals(Signal_source *signal_source) +{ + for (;;) { + Signal_source::Signal source_signal = signal_source->wait_for_signal(); + + /* look up context as pointed to by the signal imprint */ + Signal_context *context = (Signal_context *)(source_signal.imprint()); + + /* sanity check */ + if (!context) continue; + + /* construct and locally submit signal object */ + Signal signal(context, source_signal.num()); + context->_receiver->local_submit(signal); + } +} diff --git a/base/src/base/thread/thread.cc b/base/src/base/thread/thread.cc new file mode 100644 index 000000000..cb1e4da1b --- /dev/null +++ b/base/src/base/thread/thread.cc @@ -0,0 +1,208 @@ +/* + * \brief Implementation of the Thread API + * \author Norman Feske + * \date 2010-01-11 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +using namespace Genode; + + +/** + * Return the managed dataspace holding the thread context area + * + * This function is provided by the process environment. + */ +namespace Genode { + Rm_session *env_context_area_rm_session(); + Ram_session *env_context_area_ram_session(); +} + + +/****************************** + ** Thread-context allocator ** + ******************************/ + +Thread_base::Context *Thread_base::Context_allocator::base_to_context(addr_t base) +{ + addr_t result = base + CONTEXT_VIRTUAL_SIZE - sizeof(Context); + return reinterpret_cast(result); +} + + +addr_t Thread_base::Context_allocator::addr_to_base(void *addr) +{ + return ((addr_t)addr) & CONTEXT_VIRTUAL_BASE_MASK; +} + + +bool Thread_base::Context_allocator::_is_in_use(addr_t base) +{ + List_element *le = _threads.first(); + for (; le; le = le->next()) + if (base_to_context(base) == le->object()->_context) + return true; + + return false; +} + + +Thread_base::Context *Thread_base::Context_allocator::alloc(Thread_base *thread_base) +{ + Lock::Guard _lock_guard(_threads_lock); + + /* + * Find slot in context area for the new context + */ + addr_t base = CONTEXT_AREA_VIRTUAL_BASE; + for (; _is_in_use(base); base += CONTEXT_VIRTUAL_SIZE) { + + /* check upper bound of context area */ + if (base >= CONTEXT_AREA_VIRTUAL_BASE + CONTEXT_AREA_VIRTUAL_SIZE) + return 0; + } + + _threads.insert(&thread_base->_list_element); + + return base_to_context(base); +} + + +void Thread_base::Context_allocator::free(Thread_base *thread_base) +{ + Lock::Guard _lock_guard(_threads_lock); + + _threads.remove(&thread_base->_list_element); +} + + +/***************** + ** Thread base ** + *****************/ + +Thread_base::Context_allocator *Thread_base::_context_allocator() +{ + static Context_allocator context_allocator_inst; + return &context_allocator_inst; +} + + +Thread_base::Context *Thread_base::_alloc_context(size_t stack_size) +{ + /* + * Synchronize context list when creating new threads from multiple threads + * + * XXX: remove interim fix + */ + static Lock alloc_lock; + Lock::Guard _lock_guard(alloc_lock); + + /* allocate thread context */ + Context *context = _context_allocator()->alloc(this); + if (!context) + throw Context_alloc_failed(); + + /* determine size of dataspace to allocate for context members and stack */ + enum { PAGE_SIZE_LOG2 = 12 }; + size_t ds_size = align_addr(stack_size, PAGE_SIZE_LOG2); + + if (stack_size >= CONTEXT_VIRTUAL_SIZE - sizeof(Native_utcb) - (1 << PAGE_SIZE_LOG2)) + throw Stack_too_large(); + + /* + * Calculate base address of the stack + * + * The stack is always located at the top of the context. + */ + addr_t ds_addr = Context_allocator::addr_to_base(context) + CONTEXT_VIRTUAL_SIZE + - ds_size; + + /* add padding for UTCB if defined for the platform */ + if (sizeof(Native_utcb) >= (1 << PAGE_SIZE_LOG2)) + ds_addr -= sizeof(Native_utcb); + + /* allocate and attach backing store for the stack */ + Ram_dataspace_capability ds_cap; + try { + ds_cap = env_context_area_ram_session()->alloc(ds_size); + addr_t attach_addr = ds_addr - CONTEXT_AREA_VIRTUAL_BASE; + env_context_area_rm_session()->attach_at(ds_cap, attach_addr, ds_size); + + } catch (Ram_session::Alloc_failed) { + throw Stack_alloc_failed(); + } + + /* + * Now the thread context is backed by memory, so it is safe to access its + * members. + */ + + context->thread_base = this; + context->stack_base = ds_addr; + context->ds_cap = ds_cap; + return context; +} + + +void Thread_base::_free_context() +{ + addr_t ds_addr = _context->stack_base - CONTEXT_AREA_VIRTUAL_BASE; + Ram_dataspace_capability ds_cap = _context->ds_cap; + Genode::env_context_area_rm_session()->detach((void *)ds_addr); + Genode::env_context_area_ram_session()->free(ds_cap); + _context_allocator()->free(this); +} + + +void Thread_base::name(char *dst, size_t dst_len) +{ + snprintf(dst, min(dst_len, (size_t)Context::NAME_LEN), _context->name); +} + + +Thread_base *Thread_base::myself() +{ + int dummy = 0; /* used for determining the stack pointer */ + + /* + * If the stack pointer is outside the thread-context area, we assume that + * we are the main thread because this condition can never met by any other + * thread. + */ + addr_t sp = (addr_t)(&dummy); + if (sp < CONTEXT_AREA_VIRTUAL_BASE + || sp >= CONTEXT_AREA_VIRTUAL_BASE + CONTEXT_AREA_VIRTUAL_SIZE) + return 0; + + addr_t base = Context_allocator::addr_to_base(&dummy); + return Context_allocator::base_to_context(base)->thread_base; +} + + +Thread_base::Thread_base(const char *name, size_t stack_size) +: + _list_element(this), + _context(_alloc_context(stack_size)) +{ + strncpy(_context->name, name, sizeof(_context->name)); + _init_platform_thread(); +} + + +Thread_base::~Thread_base() +{ + _deinit_platform_thread(); + _free_context(); +} diff --git a/base/src/base/thread/thread_bootstrap.cc b/base/src/base/thread/thread_bootstrap.cc new file mode 100644 index 000000000..8ae56ba31 --- /dev/null +++ b/base/src/base/thread/thread_bootstrap.cc @@ -0,0 +1,18 @@ +/* + * \brief Default thread bootstrap code + * \author Norman Feske + * \date 2009-04-02 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + +void Thread_base::_thread_bootstrap() { } diff --git a/base/src/base/thread/thread_start.cc b/base/src/base/thread/thread_start.cc new file mode 100644 index 000000000..5c2f4b5ac --- /dev/null +++ b/base/src/base/thread/thread_start.cc @@ -0,0 +1,71 @@ +/* + * \brief NOVA-specific implementation of the Thread API + * \author Norman Feske + * \date 2010-01-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +using namespace Genode; + + +/** + * Entry point entered by new threads + */ +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + Genode::sleep_forever(); +} + + +/***************** + ** Thread base ** + *****************/ + +void Thread_base::_init_platform_thread() { } + + +void Thread_base::_deinit_platform_thread() +{ + env()->cpu_session()->kill_thread(_thread_cap); +} + + +void Thread_base::start() +{ + /* create thread at core */ + char buf[48]; + name(buf, sizeof(buf)); + _thread_cap = env()->cpu_session()->create_thread(buf); + + /* assign thread to protection domain */ + env()->pd_session()->bind_thread(_thread_cap); + + /* create new pager object and assign it to the new thread */ + Pager_capability pager_cap = env()->rm_session()->add_client(_thread_cap); + env()->cpu_session()->set_pager(_thread_cap, pager_cap); + + /* register initial IP and SP at core */ + addr_t thread_sp = (addr_t)&_context->stack[-4]; + thread_sp &= ~0xf; /* align initial stack to 16 byte boundary */ + env()->cpu_session()->start(_thread_cap, (addr_t)_thread_start, thread_sp); +} + + +void Thread_base::cancel_blocking() +{ + env()->cpu_session()->cancel_blocking(_thread_cap); +} diff --git a/base/src/core/arm/io_port_session_component.cc b/base/src/core/arm/io_port_session_component.cc new file mode 100644 index 000000000..15465b30a --- /dev/null +++ b/base/src/core/arm/io_port_session_component.cc @@ -0,0 +1,63 @@ +/* + * \brief Dummy implementation of the IO_PORT session interface + * \author Norman Feske + * \date 2007-09-27 + * + * On ARM, port I/O does not exist. + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +//#include +//#include + +#include "io_port_session_component.h" + +using namespace Genode; + + +/************** + ** Port API ** + **************/ + +unsigned char Io_port_session_component::inb(unsigned short address) { + return 0; } + + +unsigned short Io_port_session_component::inw(unsigned short address) { + return 0; } + + +unsigned Io_port_session_component::inl(unsigned short address) { + return 0; } + + +void Io_port_session_component::outb(unsigned short address, unsigned char value) +{ } + + +void Io_port_session_component::outw(unsigned short address, unsigned short value) +{ } + + +void Io_port_session_component::outl(unsigned short address, unsigned value) +{ } + + +/****************************** + ** Constructor / destructor ** + ******************************/ + +Io_port_session_component::Io_port_session_component(Range_allocator *io_port_alloc, + const char *args) +: _io_port_alloc(io_port_alloc) +{ } + + +Io_port_session_component::~Io_port_session_component() +{ } diff --git a/base/src/core/context_area.cc b/base/src/core/context_area.cc new file mode 100644 index 000000000..512e3b86e --- /dev/null +++ b/base/src/core/context_area.cc @@ -0,0 +1,153 @@ +/* + * \brief Support code for the thread API + * \author Norman Feske + * \date 2010-01-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include + +using namespace Genode; + + +/** + * Pointer to dataspace used to hold core contexts + */ +enum { MAX_CORE_CONTEXTS = 256 }; +static Dataspace_component *context_ds[MAX_CORE_CONTEXTS]; + + +/** + * Region-manager session for allocating thread contexts + * + * This class corresponds to the managed dataspace that is normally + * used for organizing thread contexts with the thread context area. + * It "emulates" the sub address space by adjusting the local address + * argument to 'attach' with the offset of the thread context area. + */ +class Context_area_rm_session : public Rm_session +{ + public: + + /** + * Attach backing store to thread-context area + */ + Local_addr attach(Dataspace_capability ds_cap, + size_t size, off_t offset, + bool use_local_addr, Local_addr local_addr) + { + Dataspace_component *ds = context_ds[ds_cap.local_name()]; + if (!ds) { + PERR("dataspace for core context does not exist"); + return (addr_t)0; + } + + if (!map_local(ds->phys_addr(), + (addr_t)local_addr + Thread_base::CONTEXT_AREA_VIRTUAL_BASE, + ds->size() >> get_page_size_log2())) + return (addr_t)0; + + return local_addr; + } + + void detach(Local_addr local_addr) + { + printf("context area detach from 0x%p - not implemented\n", + (void *)local_addr); + } + + Pager_capability add_client(Thread_capability) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } +}; + + +class Context_area_ram_session : public Ram_session +{ + public: + + Ram_dataspace_capability alloc(size_t size) + { + /* find free context */ + unsigned i; + for (i = 0; i < MAX_CORE_CONTEXTS; i++) + if (!context_ds[i]) + break; + + if (i == MAX_CORE_CONTEXTS) { + PERR("maximum number of core contexts (%d) reached", MAX_CORE_CONTEXTS); + return Ram_dataspace_capability(); + } + + /* allocate physical memory */ + size = round_page(size); + void *phys_base; + if (!platform_specific()->ram_alloc()->alloc_aligned(size, &phys_base, + get_page_size_log2())) { + PERR("could not allocate backing store for new context"); + return Ram_dataspace_capability(); + } + + context_ds[i] = new (platform()->core_mem_alloc()) + Dataspace_component(size, 0, (addr_t)phys_base, false, true); + + /* + * We do not manage the dataspace via an entrypoint because it will + * only be used by the 'context_area_rm_session'. Therefore, we + * construct a "capability" by hand using the context ID as local + * name. + */ + Native_capability cap; + return reinterpret_cap_cast(Native_capability(cap.dst(), i)); + } + + void free(Ram_dataspace_capability ds) { PDBG("not yet implemented"); } + + int ref_account(Ram_session_capability ram_session) { return 0; } + + int transfer_quota(Ram_session_capability ram_session, size_t amount) { return 0; } + + size_t quota() { return 0; } + + size_t used() { return 0; } +}; + + +/** + * Return single instance of the context-area RM and RAM session + */ +namespace Genode { + + Rm_session *env_context_area_rm_session() + { + static Context_area_rm_session inst; + return &inst; + } + + Ram_session *env_context_area_ram_session() + { + static Context_area_ram_session inst; + return &inst; + } +} + diff --git a/base/src/core/core_mem_alloc.cc b/base/src/core/core_mem_alloc.cc new file mode 100644 index 000000000..4617d6506 --- /dev/null +++ b/base/src/core/core_mem_alloc.cc @@ -0,0 +1,63 @@ +/* + * \brief Allocator for core-local memory + * \author Norman Feske + * \date 2009-10-12 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include +#include + +using namespace Genode; + +static const bool verbose_core_mem_alloc = false; + + +bool Core_mem_allocator::Mapped_mem_allocator::alloc(size_t size, void **out_addr) +{ + /* try to allocate block in cores already mapped virtual address ranges */ + if (_alloc.alloc(size, out_addr)) + return true; + + /* there is no sufficient space in core's mapped virtual memory, expansion needed */ + size_t page_rounded_size = (size + get_page_size() - 1) & get_page_mask(); + void *phys_addr = 0, *virt_addr = 0; + + /* allocate physical pages */ + if (!_phys_alloc->alloc(page_rounded_size, &phys_addr)) { + PERR("Could not allocate physical memory region of size %zd\n", page_rounded_size); + return false; + } + + /* allocate range in core's virtual address space */ + if (!_virt_alloc->alloc(page_rounded_size, &virt_addr)) { + PERR("Could not allocate virtual address range in core of size %zd\n", page_rounded_size); + + /* revert physical allocation */ + _phys_alloc->free(phys_addr); + return false; + } + + if (verbose_core_mem_alloc) + printf("added core memory block of %zd bytes at virt=%p phys=%p\n", + page_rounded_size, virt_addr, phys_addr); + + /* make physical page accessible at the designated virtual address */ + _map_local((addr_t)virt_addr, (addr_t)phys_addr, get_page_size_log2()); + + /* add new range to core's allocator for mapped virtual memory */ + _alloc.add_range((addr_t)virt_addr, page_rounded_size); + + /* now that we have added enough memory, try again... */ + return _alloc.alloc(size, out_addr); +} diff --git a/base/src/core/cpu_session_component.cc b/base/src/core/cpu_session_component.cc new file mode 100644 index 000000000..5c761be69 --- /dev/null +++ b/base/src/core/cpu_session_component.cc @@ -0,0 +1,196 @@ +/** + * \brief Core implementation of the CPU session/thread interfaces + * \author Christian Helmuth + * \date 2006-07-17 + * + * FIXME arg_string and quota missing + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* Core includes */ +#include +#include + +using namespace Genode; + + +Thread_capability Cpu_session_component::create_thread(Name const &name) +{ + Lock::Guard thread_list_lock_guard(_thread_list_lock); + Lock::Guard slab_lock_guard(_slab_lock); + + Cpu_thread_component *thread = 0; + try { + thread = new(&_slab) Cpu_thread_component(name.string(), _priority); + } catch (Allocator::Out_of_memory) { + throw Thread_creation_failed(); + } + + _thread_list.insert(thread); + return _thread_ep->manage(thread); +} + + +void Cpu_session_component::_unsynchronized_kill_thread(Cpu_thread_component *thread) +{ + Lock::Guard lock_guard(_slab_lock); + + _thread_ep->dissolve(thread); + _thread_list.remove(thread); + + /* If the thread is associated with a rm_session dissolve it */ + Rm_client *rc = dynamic_cast(thread->platform_thread()->pager()); + if (rc) + rc->member_rm_session()->dissolve(rc); + + destroy(&_slab, thread); +} + + +void Cpu_session_component::kill_thread(Thread_capability thread_cap) +{ + Lock::Guard lock_guard(_thread_list_lock); + + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return; + + _unsynchronized_kill_thread(thread); +} + + +Thread_capability Cpu_session_component::first() +{ + Lock::Guard lock_guard(_thread_list_lock); + + return _thread_list.first() ? _thread_list.first()->cap() + : Thread_capability(); +} + + +Thread_capability Cpu_session_component::next(Thread_capability thread_cap) +{ + Lock::Guard lock_guard(_thread_list_lock); + + Cpu_thread_component *thread = _lookup_thread(thread_cap); + + if (!thread || !thread->next()) + return Thread_capability(); + + return Thread_capability(thread->next()->cap()); +} + + +int Cpu_session_component::set_pager(Thread_capability thread_cap, + Pager_capability pager_cap) +{ + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return -1; + + Pager_object *p = dynamic_cast(_pager_ep->obj_by_cap(pager_cap)); + if (!p) return -2; + + thread->platform_thread()->pager(p); + return 0; +} + + +int Cpu_session_component::start(Thread_capability thread_cap, + addr_t ip, addr_t sp) +{ + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return -1; + + thread->platform_thread()->start((void *)ip, (void *)sp); + return 0; +} + + +void Cpu_session_component::pause(Thread_capability thread_cap) +{ + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return; + + thread->platform_thread()->pause(); +} + + +void Cpu_session_component::resume(Thread_capability thread_cap) +{ + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return; + + thread->platform_thread()->resume(); +} + + +void Cpu_session_component::cancel_blocking(Thread_capability thread_cap) +{ + Cpu_thread_component *thread = _lookup_thread(thread_cap); + + if (thread) + thread->platform_thread()->cancel_blocking(); +} + + +int Cpu_session_component::state(Thread_capability thread_cap, + Thread_state *state_dst) +{ + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread) return -1; + + thread->platform_thread()->state(state_dst); + return 0; +} + + +void Cpu_session_component::exception_handler(Thread_capability thread_cap, + Signal_context_capability sigh_cap) +{ + Cpu_thread_component *thread = _lookup_thread(thread_cap); + if (!thread || !thread->platform_thread()->pager()) return; + + thread->platform_thread()->pager()->exception_handler(sigh_cap); +} + + +Cpu_session_component::Cpu_session_component(Rpc_entrypoint *thread_ep, + Pager_entrypoint *pager_ep, + Allocator *md_alloc, + const char *args) +: _thread_ep(thread_ep), _pager_ep(pager_ep), + _md_alloc(md_alloc, Arg_string::find_arg(args, "ram_quota").long_value(0)), + _slab(&_md_alloc), _priority(0) +{ + Arg a = Arg_string::find_arg(args, "priority"); + if (a.valid()) { + _priority = a.ulong_value(0); + + /* clamp priority value to valid range */ + _priority = min((unsigned)PRIORITY_LIMIT - 1, _priority); + } +} + + +Cpu_session_component::~Cpu_session_component() +{ + Lock::Guard lock_guard(_thread_list_lock); + + /* + * We have to keep the '_thread_list_lock' during the whole destructor to + * prevent races with incoming calls of the 'create_thread' function, + * adding new threads while we are destroying them. + */ + + for (Cpu_thread_component *thread; (thread = _thread_list.first()); ) + _unsynchronized_kill_thread(thread); +} diff --git a/base/src/core/dataspace_component.cc b/base/src/core/dataspace_component.cc new file mode 100644 index 000000000..a0ac0169d --- /dev/null +++ b/base/src/core/dataspace_component.cc @@ -0,0 +1,52 @@ +/* + * \brief Dataspace component + * \date 2006-09-18 + * \author Christian Helmuth + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* core includes */ +#include +#include + +using namespace Genode; + + +void Dataspace_component::attached_to(Rm_region *region) +{ + Lock::Guard lock_guard(_lock); + _regions.insert(region); +} + + +void Dataspace_component::detached_from(Rm_region *region) +{ + Lock::Guard lock_guard(_lock); + _regions.remove(region); +} + + +Dataspace_component::~Dataspace_component() +{ + _lock.lock(); + + /* remove from all regions */ + while (Rm_region *r = _regions.first()) { + + /* + * The 'detach' function calls 'Dataspace_component::detached_from' + * and thereby removes the current region from the '_regions' list. + */ + _lock.unlock(); + r->session()->detach((void *)r->base()); + _lock.lock(); + } + + _lock.unlock(); +} diff --git a/base/src/core/dump_alloc.cc b/base/src/core/dump_alloc.cc new file mode 100644 index 000000000..a9b216c22 --- /dev/null +++ b/base/src/core/dump_alloc.cc @@ -0,0 +1,58 @@ +/* + * \brief Allocator dump helpers + * \author Norman Feske + * \date 2009-10-15 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + + +void Allocator_avl_base::Block::dump() +{ + printf(" Block: [%08lx,%08lx) size=%08zx avail=%08zx max_avail=%08zx\n", + addr(), addr() + size(), size(), avail(), max_avail()); +} + + +void Allocator_avl_base::dump_addr_tree(Block *addr_node) +{ + bool top = false; + static unsigned mem_size; + static unsigned mem_avail; + + if (addr_node == 0) { + addr_node = _addr_tree.first(); + + printf("Allocator %p dump:\n", this); + mem_size = mem_avail = 0; + top = true; + } + + if (!addr_node) return; + + if (addr_node->child(0)) + dump_addr_tree(addr_node->child(0)); + + Block *b = (Block *)addr_node; + b->dump(); + mem_size += b->size(); + mem_avail += b->avail(); + + if (addr_node->child(1)) + dump_addr_tree(addr_node->child(1)); + + if (top) + printf(" => mem_size=%u (%u MB) / mem_avail=%u (%u MB)\n", + mem_size, mem_size / 1024 / 1024, + mem_avail, mem_avail / 1024 / 1024); +} diff --git a/base/src/core/include/cap_root.h b/base/src/core/include/cap_root.h new file mode 100644 index 000000000..d27d3a793 --- /dev/null +++ b/base/src/core/include/cap_root.h @@ -0,0 +1,47 @@ +/* + * \brief CAP root interface + * \author Norman Feske + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CAP_ROOT_H_ +#define _CORE__INCLUDE__CAP_ROOT_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +namespace Genode { + + class Cap_root : public Root_component + { + protected: + + Cap_session_component *_create_session(const char *args) { + return new (md_alloc()) Cap_session_component(); } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing session objects + * \param md_alloc meta-data allocator to be used by root component + */ + Cap_root(Rpc_entrypoint *session_ep, + Allocator *md_alloc) + : + Root_component(session_ep, md_alloc) { } + }; +} + +#endif /* _CORE__INCLUDE__CAP_ROOT_H_ */ diff --git a/base/src/core/include/cap_session_component.h b/base/src/core/include/cap_session_component.h new file mode 100644 index 000000000..e217fd334 --- /dev/null +++ b/base/src/core/include/cap_session_component.h @@ -0,0 +1,47 @@ +/* + * \brief Capability allocation service + * \author Norman Feske + * \date 2006-06-26 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ + +#include +#include + +namespace Genode { + + class Cap_session_component : public Rpc_object + { + private: + + static long _unique_id_cnt; + + static Lock &_lock() + { + static Lock static_lock; + return static_lock; + } + + public: + + Native_capability alloc(Native_capability ep) + { + Lock::Guard lock_guard(_lock()); + + return Native_capability(ep.tid(), ++_unique_id_cnt); + } + + void free(Native_capability cap) { } + }; +} + +#endif /* _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/core_env.h b/base/src/core/include/core_env.h new file mode 100644 index 000000000..c5d720976 --- /dev/null +++ b/base/src/core/include/core_env.h @@ -0,0 +1,179 @@ +/* + * \brief Core-specific environment + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-28 + * + * The Core-specific environment ensures that all sessions of Core's + * environment a local (_component) not remote (_client). + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_ENV_H_ +#define _CORE__INCLUDE__CORE_ENV_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include + +namespace Genode { + + /** + * Lock-guarded version of a RAM-session implementation + * + * \param RAM_SESSION_IMPL non-thread-safe RAM-session class + * + * In contrast to normal processes, core's 'env()->ram_session()' is not + * synchronized by an RPC interface. However, it is accessed by different + * threads using the 'env()->heap()' and the sliced heap used for + * allocating sessions to core's services. + */ + template + class Synchronized_ram_session : public RAM_SESSION_IMPL + { + private: + + Lock _lock; + + public: + + /** + * Constructor + */ + Synchronized_ram_session(Rpc_entrypoint *ds_ep, + Rpc_entrypoint *ram_session_ep, + Range_allocator *ram_alloc, + Allocator *md_alloc, + const char *args, + size_t quota_limit = 0) + : + RAM_SESSION_IMPL(ds_ep, ram_session_ep, ram_alloc, md_alloc, args, quota_limit) + { } + + + /*************************** + ** RAM-session interface ** + ***************************/ + + Ram_dataspace_capability alloc(size_t size) + { + Lock::Guard lock_guard(_lock); + return RAM_SESSION_IMPL::alloc(size); + } + + void free(Ram_dataspace_capability ds) + { + Lock::Guard lock_guard(_lock); + RAM_SESSION_IMPL::free(ds); + } + + int ref_account(Ram_session_capability session) + { + Lock::Guard lock_guard(_lock); + return RAM_SESSION_IMPL::ref_account(session); + } + + int transfer_quota(Ram_session_capability session, size_t size) + { + Lock::Guard lock_guard(_lock); + return RAM_SESSION_IMPL::transfer_quota(session, size); + } + + size_t quota() + { + Lock::Guard lock_guard(_lock); + return RAM_SESSION_IMPL::quota(); + } + + size_t used() + { + Lock::Guard lock_guard(_lock); + return RAM_SESSION_IMPL::used(); + } + }; + + + class Core_env : public Env + { + private: + + typedef Synchronized_ram_session Core_ram_session; + + enum { ENTRYPOINT_STACK_SIZE = 8*1024 }; + + Core_parent _core_parent; + Cap_session_component _cap_session; + Rpc_entrypoint _entrypoint; + Core_rm_session _rm_session; + Core_ram_session _ram_session; + Heap _heap; + Ram_session_capability const _ram_session_cap; + + public: + + /** + * Constructor + */ + Core_env() : + _entrypoint(&_cap_session, ENTRYPOINT_STACK_SIZE, "entrypoint"), + _rm_session(&_entrypoint), + _ram_session(&_entrypoint, &_entrypoint, + platform()->ram_alloc(), platform()->core_mem_alloc(), + "ram_quota=4M", platform()->ram_alloc()->avail()), + _heap(&_ram_session, &_rm_session), + _ram_session_cap(_entrypoint.manage(&_ram_session)) + { } + + /** + * Destructor + */ + ~Core_env() { parent()->exit(0); } + + Cap_session *cap_session() { return &_cap_session; } + Rpc_entrypoint *entrypoint() { return &_entrypoint; } + + + /******************* + ** Env interface ** + *******************/ + + Parent *parent() { return &_core_parent; } + Ram_session *ram_session() { return &_ram_session; } + Ram_session_capability ram_session_cap() { return _ram_session_cap; } + Rm_session *rm_session() { return &_rm_session; } + Allocator *heap() { return &_heap; } + + Cpu_session *cpu_session() + { + PWRN("not implemented"); + return 0; + } + + Pd_session *pd_session() + { + PWRN("not implemented"); + return 0; + } + }; + + + /** + * Request pointer to static environment of Core + */ + extern Core_env *core_env(); +} + +#endif /* _CORE__INCLUDE__CORE_ENV_H_ */ diff --git a/base/src/core/include/core_mem_alloc.h b/base/src/core/include/core_mem_alloc.h new file mode 100644 index 000000000..e584eb935 --- /dev/null +++ b/base/src/core/include/core_mem_alloc.h @@ -0,0 +1,172 @@ +/* + * \brief Allocator infrastructure for core + * \author Norman Feske + * \date 2009-10-12 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_MEM_ALLOC_H_ +#define _CORE__INCLUDE__CORE_MEM_ALLOC_H_ + +#include +#include +#include + +namespace Genode { + + /** + * Allocators for physical memory, core's virtual address space, + * and core-local memory. The interface of this class is thread safe. + */ + class Core_mem_allocator : public Allocator + { + public: + + typedef Synchronized_range_allocator Phys_allocator; + + private: + + /** + * Unsynchronized allocator for core-mapped memory + * + * This is an allocator of core-mapped memory. It is meant to be used as + * meta-data allocator for the other allocators and as back end for core's + * synchronized memory allocator. + */ + class Mapped_mem_allocator : public Allocator + { + private: + + Allocator_avl _alloc; + Range_allocator *_phys_alloc; + Range_allocator *_virt_alloc; + + /** + * Initial chunk to populate the core mem allocator + * + * This chunk is used at platform initialization time. + */ + char _initial_chunk[16*1024]; + + /** + * Map physical page locally to specified virtual address + * + * \param virt_addr core-local address + * \param phys_addr physical memory address + * \param size_log2 size of memory block to map + * \return true on success + */ + bool _map_local(addr_t virt_addr, addr_t phys_addr, unsigned size_log2); + + public: + + /** + * Constructor + * + * \param phys_alloc allocator of physical memory + * \param virt_alloc allocator of core-local virtual memory ranges + */ + Mapped_mem_allocator(Range_allocator *phys_alloc, + Range_allocator *virt_alloc) + : _alloc(0), _phys_alloc(phys_alloc), _virt_alloc(virt_alloc) + { + _alloc.add_range((addr_t)_initial_chunk, sizeof(_initial_chunk)); + } + + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t size, void **out_addr); + void free(void *addr, size_t size) { _alloc.free(addr, size); } + size_t consumed() { return _phys_alloc->consumed(); } + size_t overhead(size_t size) { return _phys_alloc->overhead(size); } + }; + + + /** + * Lock used for synchronization of all operations on the + * embedded allocators. + */ + Lock _lock; + + /** + * Synchronized allocator of physical memory ranges + * + * This allocator must only be used to allocate memory + * ranges at page granularity. + */ + Phys_allocator _phys_alloc; + + /** + * Synchronized allocator of core's virtual memory ranges + * + * This allocator must only be used to allocate memory + * ranges at page granularity. + */ + Phys_allocator _virt_alloc; + + /** + * Unsynchronized core-mapped memory allocator + * + * This allocator is internally used within this class for + * allocating meta data for the other allocators. It is not + * synchronized to avoid nested locking. The lock-guarded + * access to this allocator from the outer world is + * provided via the 'Allocator' interface implemented by + * 'Core_mem_allocator'. The allocator works at byte + * granularity. + */ + Mapped_mem_allocator _mem_alloc; + + public: + + /** + * Constructor + */ + Core_mem_allocator() : + _phys_alloc(&_lock, &_mem_alloc), + _virt_alloc(&_lock, &_mem_alloc), + _mem_alloc(_phys_alloc.raw(), _virt_alloc.raw()) + { } + + /** + * Access physical-memory allocator + */ + Phys_allocator *phys_alloc() { return &_phys_alloc; } + + /** + * Access core's virtual-memory allocator + */ + Phys_allocator *virt_alloc() { return &_virt_alloc; } + + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t size, void **out_addr) + { + Lock::Guard lock_guard(_lock); + return _mem_alloc.alloc(size, out_addr); + } + + void free(void *addr, size_t size) + { + Lock::Guard lock_guard(_lock); + _mem_alloc.free(addr, size); + } + + size_t consumed() { return _phys_alloc.consumed(); } + size_t overhead(size_t size) { return _phys_alloc.overhead(size); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_MEM_ALLOC_H_ */ diff --git a/base/src/core/include/core_parent.h b/base/src/core/include/core_parent.h new file mode 100644 index 000000000..91d69e15b --- /dev/null +++ b/base/src/core/include/core_parent.h @@ -0,0 +1,61 @@ +/* + * \brief Core-specific parent client implementation + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-20 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_PARENT_H_ +#define _CORE__INCLUDE__CORE_PARENT_H_ + +#include +#include + +namespace Genode { + + /** + * In fact, Core has _no_ parent. But most of our libraries could work + * seamlessly inside Core too, if it had one. Core_parent fills this gap. + */ + class Core_parent : public Parent + { + public: + + /** + * Constructor + */ + Core_parent() { } + + + /********************** + ** Parent interface ** + **********************/ + + void exit(int); + + void announce(Service_name const &, Root_capability) + { + PDBG("implement me, please"); + } + + Session_capability session(Service_name const &, Session_args const &); + + void upgrade(Session_capability, Upgrade_args const &) + { + PDBG("implement me, please"); + throw Quota_exceeded(); + } + + void close(Session_capability) { + PDBG("implement me, please"); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_PARENT_H_ */ diff --git a/base/src/core/include/core_rm_session.h b/base/src/core/include/core_rm_session.h new file mode 100644 index 000000000..c2dd8a2bd --- /dev/null +++ b/base/src/core/include/core_rm_session.h @@ -0,0 +1,63 @@ +/* + * \brief Core-specific region manager session + * \author Norman Feske + * \date 2006-07-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_RM_SESSION_H_ +#define _CORE__INCLUDE__CORE_RM_SESSION_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +namespace Genode { + + /** + * Region manager that uses the physical dataspace + * addresses directly as virtual addresses. + */ + class Core_rm_session : public Rm_session + { + private: + + Rpc_entrypoint *_ds_ep; + + public: + + Core_rm_session(Rpc_entrypoint *ds_ep): _ds_ep(ds_ep) { } + + Local_addr attach(Dataspace_capability ds_cap, size_t size=0, + off_t offset=0, bool use_local_addr = false, + Local_addr local_addr = 0) + { + Dataspace_component *ds = static_cast(_ds_ep->obj_by_cap(ds_cap)); + if (!ds) + throw Invalid_dataspace(); + + return (void *)ds->phys_addr(); + } + + void detach(Local_addr local_addr) { } + + Pager_capability add_client(Thread_capability thread) { + return Pager_capability(); } + + void fault_handler(Signal_context_capability handler) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } + }; +} + +#endif /* _CORE__INCLUDE__CORE_RM_SESSION_H_ */ diff --git a/base/src/core/include/cpu_root.h b/base/src/core/include/cpu_root.h new file mode 100644 index 000000000..4271d4725 --- /dev/null +++ b/base/src/core/include/cpu_root.h @@ -0,0 +1,59 @@ +/* + * \brief CPU root interface + * \author Christian Helmuth + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CPU_ROOT_H_ +#define _CORE__INCLUDE__CPU_ROOT_H_ + +/* Genode includes */ +#include + +/* Core includes */ +#include + +namespace Genode { + + class Cpu_root : public Root_component + { + private: + + Rpc_entrypoint *_thread_ep; + Pager_entrypoint *_pager_ep; + Allocator *_md_alloc; + + protected: + + Cpu_session_component *_create_session(const char *args) { + return new (md_alloc()) + Cpu_session_component(_thread_ep, _pager_ep, _md_alloc, args); } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing cpu session objects + * \param thread_ep entry point for managing threads + * \param md_alloc meta data allocator to be used by root component + */ + Cpu_root(Rpc_entrypoint *session_ep, + Rpc_entrypoint *thread_ep, + Pager_entrypoint *pager_ep, + Allocator *md_alloc) + : + Root_component(session_ep, md_alloc), + _thread_ep(thread_ep), _pager_ep(pager_ep), _md_alloc(md_alloc) + { } + }; +} + +#endif /* _CORE__INCLUDE__CPU_ROOT_H_ */ diff --git a/base/src/core/include/cpu_session_component.h b/base/src/core/include/cpu_session_component.h new file mode 100644 index 000000000..e2c7e4605 --- /dev/null +++ b/base/src/core/include/cpu_session_component.h @@ -0,0 +1,144 @@ +/* + * \brief Core-specific instance of the CPU session/thread interfaces + * \author Christian Helmuth + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + /** + * RPC interface of CPU thread + * + * We make 'Cpu_thread' a RPC object only to be able to lookup CPU threads + * from thread capabilities supplied as arguments to CPU-session functions. + * A CPU thread does not provide an actual RPC interface. + */ + struct Cpu_thread + { + GENODE_RPC_INTERFACE(); + }; + + + class Cpu_thread_component : public Rpc_object, + public List::Element + { + private: + + Platform_thread _platform_thread; + + bool _bound; /* pd binding flag */ + + public: + + Cpu_thread_component(const char *name, unsigned priority) + : _platform_thread(name, priority), _bound(false) { } + + + /************************ + ** Accessor functions ** + ************************/ + + inline Platform_thread * platform_thread() { return &_platform_thread; } + inline bool bound() const { return _bound; } + inline void bound(bool b) { _bound = b; } + }; + + + class Cpu_session_component : public Rpc_object + { + private: + + /** + * Allocator used for managing the CPU threads associated with the + * CPU session + */ + typedef Tslab Cpu_thread_allocator; + + Rpc_entrypoint *_thread_ep; + Pager_entrypoint *_pager_ep; + Allocator_guard _md_alloc; /* guarded meta-data allocator */ + Cpu_thread_allocator _slab; /* meta-data allocator */ + Lock _slab_lock; /* protect slab access */ + List _thread_list; + Lock _thread_list_lock; /* protect thread list */ + unsigned _priority; /* priority of threads + created with this + session */ + + /** + * Lookup thread in CPU session by its capability + * + * \retval NULL thread capability is invalid or + * does not belong to the CPU session + */ + Cpu_thread_component *_lookup_thread(Thread_capability thread) { + return dynamic_cast + (_thread_ep->obj_by_cap(thread)); } + + /** + * Raw thread-killing functionality + * + * This function is called from the 'kill_thread' function and + * the destructor. Each these functions grab the list lock + * by themselves and call this function to perform the actual + * killing. + */ + void _unsynchronized_kill_thread(Cpu_thread_component *thread); + + public: + + /** + * Constructor + */ + Cpu_session_component(Rpc_entrypoint *thread_ep, + Pager_entrypoint *pager_ep, + Allocator *md_alloc, const char *args); + + /** + * Destructor + */ + ~Cpu_session_component(); + + + /*************************** + ** CPU session interface ** + ***************************/ + + Thread_capability create_thread(Name const &); + void kill_thread(Thread_capability); + Thread_capability first(); + Thread_capability next(Thread_capability); + int set_pager(Thread_capability, Pager_capability); + int start(Thread_capability, addr_t, addr_t); + void pause(Thread_capability thread_cap); + void resume(Thread_capability thread_cap); + void cancel_blocking(Thread_capability); + int name(Thread_capability, char *, size_t); + int state(Thread_capability, Thread_state *); + void exception_handler(Thread_capability, Signal_context_capability); + }; +} + +#endif /* _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/dataspace_component.h b/base/src/core/include/dataspace_component.h new file mode 100644 index 000000000..f0a873563 --- /dev/null +++ b/base/src/core/include/dataspace_component.h @@ -0,0 +1,139 @@ +/* + * \brief Core-internal dataspace representation + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-20 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__DATASPACE_COMPONENT_H_ +#define _CORE__INCLUDE__DATASPACE_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Rm_region; + class Rm_session_component; + + class Dataspace_component : public Rpc_object + { + private: + + addr_t _phys_addr; /* address of dataspace in physical memory */ + addr_t _core_local_addr; /* address of core-local mapping */ + size_t _size; /* size of dataspace in bytes */ + bool _is_io_mem; /* dataspace is I/O mem, not to be touched */ + bool _write_combined; /* access I/O memory write-combined */ + bool _writable; /* false if dataspace is read-only */ + + List _regions; /* regions this is attached to */ + Lock _lock; + + protected: + + bool _managed; /* true if this is a managed dataspace */ + + private: + + /* + * Prevent copy-construction of objects with virtual functions. + */ + Dataspace_component(const Dataspace_component&); + + public: + + /** + * Default constructor returning an invalid dataspace + */ + Dataspace_component() + : _phys_addr(0), _core_local_addr(0), _size(0), + _is_io_mem(false), _write_combined(false), _writable(false), + _managed(false) { } + + /** + * Constructor for non-I/O dataspaces + * + * This constructor is used by RAM and ROM dataspaces. + */ + Dataspace_component(size_t size, addr_t core_local_addr, bool writable) + : _phys_addr(core_local_addr), _core_local_addr(core_local_addr), + _size(round_page(size)), _is_io_mem(false), _write_combined(false), + _writable(writable), _managed(false) { } + + /** + * Constructor for dataspaces with different core-local and + * physical addresses + * + * This constructor is used by IO_MEM. Because I/O-memory areas may + * be located at addresses that are populated by data or text in + * Core's virtual address space, we need to map these areas to + * another core-local address. The local mapping in core's address + * space is needed to send a mapping to another address space. + */ + Dataspace_component(size_t size, addr_t core_local_addr, + addr_t phys_addr, bool write_combined, + bool writable) + : _phys_addr(phys_addr), _core_local_addr(core_local_addr), + _size(size), _is_io_mem(true), _write_combined(write_combined), + _writable(writable), _managed(false) { } + + /** + * Destructor + */ + ~Dataspace_component(); + + /** + * Return region-manager session corresponding to nested dataspace + * + * \retval 0 dataspace is not a nested dataspace + */ + virtual Rm_session_component *sub_rm_session() { return 0; } + + addr_t core_local_addr() const { return _core_local_addr; } + bool is_io_mem() const { return _is_io_mem; } + bool write_combined() const { return _write_combined; } + + /** + * Return dataspace base address to be used for map operations + * + * Depending on the used kernel, this may be a core-local address + * or a physical address. + */ + addr_t map_src_addr() const + { + return Genode::map_src_addr(_core_local_addr, _phys_addr); + } + + void assign_core_local_addr(void *addr) { _core_local_addr = (addr_t)addr; } + + void attached_to(Rm_region *region); + void detached_from(Rm_region *region); + + List *regions() { return &_regions; } + + /************************* + ** Dataspace interface ** + *************************/ + + size_t size() { return _size; } + addr_t phys_addr() { return _phys_addr; } + bool writable() { return _writable; } + bool is_managed() { return _managed; } + }; +} + +#endif /* _CORE__INCLUDE__DATASPACE_COMPONENT_H_ */ diff --git a/base/src/core/include/io_mem_root.h b/base/src/core/include/io_mem_root.h new file mode 100644 index 000000000..0c3c2d8e2 --- /dev/null +++ b/base/src/core/include/io_mem_root.h @@ -0,0 +1,63 @@ +/* + * \brief IO_MEM root interface + * \author Christian Helmuth + * \date 2006-08-01 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IO_MEM_ROOT_H_ +#define _CORE__INCLUDE__IO_MEM_ROOT_H_ + +#include + +#include "io_mem_session_component.h" + +namespace Genode { + + class Io_mem_root : public Root_component + { + + private: + + Range_allocator *_io_mem_alloc; /* MMIO region allocator */ + Range_allocator *_ram_alloc; /* RAM allocator */ + Rpc_entrypoint *_ds_ep; /* entry point for managing io_mem dataspaces */ + + protected: + + Io_mem_session_component *_create_session(const char *args) + { + return new (md_alloc()) + Io_mem_session_component(_io_mem_alloc, _ram_alloc, + _ds_ep, args); + } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing io_mem session objects + * \param ds_ep entry point for managing dataspaces + * \param io_mem_alloc platform IO_MEM allocator + * \param ram_alloc platform RAM allocator + * \param md_alloc meta-data allocator to be used by root component + */ + Io_mem_root(Rpc_entrypoint *session_ep, + Rpc_entrypoint *ds_ep, + Range_allocator *io_mem_alloc, + Range_allocator *ram_alloc, + Allocator *md_alloc) + : + Root_component(session_ep, md_alloc), + _io_mem_alloc(io_mem_alloc), _ram_alloc(ram_alloc), _ds_ep(ds_ep) { } + }; +} + +#endif /* _CORE__INCLUDE__IO_MEM_ROOT_H_ */ diff --git a/base/src/core/include/io_mem_session_component.h b/base/src/core/include/io_mem_session_component.h new file mode 100644 index 000000000..54525cab9 --- /dev/null +++ b/base/src/core/include/io_mem_session_component.h @@ -0,0 +1,139 @@ +/* + * \brief Core-specific instance of the IO_MEM session interface + * \author Christian Helmuth + * \date 2006-09-14 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IO_MEM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__IO_MEM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Io_mem_session_component : public Rpc_object + { + private: + + /* + * Helper class used to pass the dataspace attributes as + * parameters from the _prepare_io_mem function to the + * constructor of Dataspace_component. + */ + struct Dataspace_attr + { + size_t size; + addr_t core_local_addr; + addr_t phys_addr; + bool write_combined; + + /** + * Default constructor + * + * This constructor enables Dataspace_attr objects to be + * returned from the '_prepare_io_mem' function. + */ + Dataspace_attr() { } + + /** + * Constructor + * + * An invalid dataspace is represented by setting all + * arguments to zero. + */ + Dataspace_attr(size_t s, addr_t cla, addr_t pa, bool write_combined): + size(s), core_local_addr(cla), phys_addr(pa) { } + + } ds_attr; + + class Io_dataspace_component : public Dataspace_component + { + public: + + /** + * Constructor + */ + Io_dataspace_component(Dataspace_attr da) + : Dataspace_component(da.size, da.core_local_addr, + da.phys_addr, da.write_combined, + true) { } + + bool valid() { return size() != 0; } + }; + + Range_allocator *_io_mem_alloc; + Io_dataspace_component _ds; + Rpc_entrypoint *_ds_ep; + Io_mem_dataspace_capability _ds_cap; + bool _write_combined; + + Dataspace_attr _prepare_io_mem(const char *args, Range_allocator *ram_alloc); + + + /******************************************** + ** Platform-implemented support functions ** + ********************************************/ + + /* FIXME Could this be merged with Dataspace::unmap() and friends? */ + + /** + * Map region locally and return local base address + * + * Both parameters - base and size - must be page-aligned. + */ + addr_t _map_local(addr_t base, size_t size); + + /** + * Unmap Core-local mapping of region + * + * Both parameters - base and size - must be page-aligned. + */ + void _unmap_local(addr_t base, size_t size); + + public: + + /** + * Constructor + * + * \param io_mem_alloc MMIO region allocator + * \param ram_alloc RAM allocator that will be checked for + * region collisions + * \param ds_ep entry point to manage the dataspace + * corresponding the io_mem session + * \param args session construction arguments, in + * particular MMIO region base, size and + * caching demands + */ + Io_mem_session_component(Range_allocator *io_mem_alloc, + Range_allocator *ram_alloc, + Rpc_entrypoint *ds_ep, + const char *args); + + /** + * Destructor + */ + ~Io_mem_session_component(); + + + /****************************** + ** Io-mem session interface ** + ******************************/ + + Io_mem_dataspace_capability dataspace() { return _ds_cap; } + }; +} + +#endif /* _CORE__INCLUDE__IO_MEM_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/io_port_root.h b/base/src/core/include/io_port_root.h new file mode 100644 index 000000000..0f1e95f58 --- /dev/null +++ b/base/src/core/include/io_port_root.h @@ -0,0 +1,71 @@ +/* + * \brief IO_PORT root interface + * \author Christian Helmuth + * \date 2007-04-17 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IO_PORT_ROOT_H_ +#define _CORE__INCLUDE__IO_PORT_ROOT_H_ + +#include + +#include "io_port_session_component.h" + +namespace Genode { + + struct Io_port_handler + { + private: + + enum { STACK_SIZE = 4096 }; + Rpc_entrypoint _ep; + + public: + + Io_port_handler(Cap_session *cap_session) : + _ep(cap_session, STACK_SIZE, "ioport") + { } + + Rpc_entrypoint *entrypoint() { return &_ep; } + }; + + class Io_port_root : private Io_port_handler, + public Root_component + { + + private: + + Range_allocator *_io_port_alloc; /* I/O port allocator */ + + protected: + + Io_port_session_component *_create_session(const char *args) { + return new (md_alloc()) Io_port_session_component(_io_port_alloc, args); } + + public: + + /** + * Constructor + * + * \param cap_session capability allocator + * \param io_port_alloc platform IO_PORT allocator + * \param md_alloc meta-data allocator to be used by root component + */ + Io_port_root(Cap_session *cap_session, + Range_allocator *io_port_alloc, + Allocator *md_alloc) + : + Io_port_handler(cap_session), + Root_component(entrypoint(), md_alloc), + _io_port_alloc(io_port_alloc) { } + }; +} + +#endif /* _CORE__INCLUDE__IO_PORT_ROOT_H_ */ diff --git a/base/src/core/include/io_port_session_component.h b/base/src/core/include/io_port_session_component.h new file mode 100644 index 000000000..2ad82b35c --- /dev/null +++ b/base/src/core/include/io_port_session_component.h @@ -0,0 +1,77 @@ +/* + * \brief Core-specific instance of the IO_PORT session interface + * \author Christian Helmuth + * \date 2007-04-17 + * + * We assume Core is running on IOPL3. + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IO_PORT_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__IO_PORT_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Io_port_session_component : public Rpc_object + { + private: + + Range_allocator *_io_port_alloc; + unsigned short _base; + unsigned short _size; + + /** + * Check if access exceeds range + */ + bool _in_bounds(unsigned short address, unsigned width) { + return (address >= _base) && (address + width <= _base + _size); } + + + public: + + /** + * Constructor + * + * \param io_port_alloc IO_PORT region allocator + * \param args session construction arguments, in + * particular port base and size + * \throw Root::Invalid_args + */ + Io_port_session_component(Range_allocator *io_port_alloc, + const char *args); + + /** + * Destructor + */ + ~Io_port_session_component(); + + + /******************************* + ** Io-port session interface ** + *******************************/ + + unsigned char inb(unsigned short); + unsigned short inw(unsigned short); + unsigned inl(unsigned short); + + void outb(unsigned short, unsigned char); + void outw(unsigned short, unsigned short); + void outl(unsigned short, unsigned); + }; +} + +#endif /* _CORE__INCLUDE__IO_PORT_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/irq_root.h b/base/src/core/include/irq_root.h new file mode 100644 index 000000000..7e06cc4b3 --- /dev/null +++ b/base/src/core/include/irq_root.h @@ -0,0 +1,113 @@ +/* + * \brief IRQ root interface + * \author Christian Helmuth + * \date 2007-09-13 + * + * FIXME locking + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IRQ_ROOT_H_ +#define _CORE__INCLUDE__IRQ_ROOT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Irq_root : public Rpc_object > + { + + private: + + Cap_session *_cap_session; + Range_allocator *_irq_alloc; /* platform irq allocator */ + Allocator *_md_alloc; /* meta-data allocator */ + List _sessions; /* started irq sessions */ + + public: + + /** + * Constructor + * + * \param cap_session capability allocator + * \param irq_alloc IRQ range that can be assigned to clients + * \param md_alloc meta-data allocator to be used by root component + */ + Irq_root(Cap_session *cap_session, + Range_allocator *irq_alloc, + Allocator *md_alloc) + : _cap_session(cap_session), _irq_alloc(irq_alloc), _md_alloc(md_alloc) { } + + + /******************** + ** Root interface ** + ********************/ + + Session_capability session(Session_args const &args) + { + if (!args.is_valid_string()) throw Invalid_args(); + + /* + * We need to decrease 'ram_quota' by + * the size of the session object. + */ + size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + long remaining_ram_quota = ram_quota - sizeof(Irq_session_component) - + _md_alloc->overhead(sizeof(Irq_session_component)); + if (remaining_ram_quota < 0) { + PERR("Insufficient ram quota, provided=%zd, required=%zd", + ram_quota, sizeof(Irq_session_component) + + _md_alloc->overhead(sizeof(Irq_session_component))); + return Session_capability(); + } + + Irq_session_component *s; + try { + s = new (_md_alloc) Irq_session_component(_cap_session, _irq_alloc, args.string()); + } catch (Allocator::Out_of_memory) { return Session_capability(); } + + if (!s->cap().valid()) + return Session_capability(); + + _sessions.insert(s); + + return s->cap(); + } + + void upgrade(Session_capability, Upgrade_args const &) + { + /* there is no need to upgrade an IRQ session */ + } + + void close(Session_capability session) + { + Irq_session_component *s = _sessions.first(); + + for (; s; s = s->next()) { + if (s->cap().local_name() == session.local_name()) + break; + } + if (!s) return; + + _sessions.remove(s); + + /* XXX Currently we support only one client... */ + destroy(_md_alloc, s); + } + }; +} + +#endif /* _CORE__INCLUDE__IRQ_ROOT_H_ */ diff --git a/base/src/core/include/irq_session_component.h b/base/src/core/include/irq_session_component.h new file mode 100644 index 000000000..1d626608f --- /dev/null +++ b/base/src/core/include/irq_session_component.h @@ -0,0 +1,140 @@ +/* + * \brief Core-specific instance of the IRQ session interface + * \author Christian Helmuth + * \date 2007-09-13 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ + +#include +#include +#include +#include +#include +#include + +/* XXX Notes + * + * - each H/W IRQ is an irq thread + * - each irq thread has an Rpc_entrypoint + * -> irq thread is special Server_activation + * -> IRQ session is Rpc_object + * + * - session("IRQ", "irq_num=") -> Native_capability(irq_thread, cap) + * - cap must be generated at CAP + * - cap must be managed by irq_thread-local Rpc_entrypoint + * + * - irq thread states + * 1. wait_for_client --[ client calls wait_for_irq ]--> 2. + * 2. wait_for_irq --[ kernel signals irq ]--> 3. + * 3. irq_occured --[ inform client about occurence ]--> 1. + * + * - irq thread roles + * - Irq_server (Ipc_server) for client + * - Fiasco_irq_client (Ipc_client) at kernel + */ + +namespace Genode { + + class Irq_session_component : public Rpc_object, + public List::Element + { + private: + + struct Irq_control + { + GENODE_RPC(Rpc_associate_to_irq, bool, associate_to_irq, unsigned); + GENODE_RPC_INTERFACE(Rpc_associate_to_irq); + }; + + struct Irq_control_client : Rpc_client + { + Irq_control_client(Capability cap) + : Rpc_client(cap) { } + + bool associate_to_irq(unsigned irq_number) { + return call(irq_number); } + }; + + struct Irq_control_component : Rpc_object + { + /** + * Associate to IRQ at Fiasco + * + * This is executed by the IRQ server activation itself. + */ + bool associate_to_irq(unsigned irq_number); + }; + + unsigned _irq_number; + Range_allocator *_irq_alloc; + + enum { STACK_SIZE = 2048 }; + Rpc_entrypoint _ep; + + /* + * On Pistachio, an IRQ is unmasked right after attaching. + * Hence, the kernel may send an IRQ IPC when the IRQ hander is + * not explicitly waiting for an IRQ but when it is waiting for + * a client's 'wait_for_irq' RPC call. To avoid this conflict, we + * lazily associate to the IRQ when calling the 'wait_for_irq' + * function for the first time. We use the '_irq_attached' flag + * for detecting the first call. On other kernels, this variable + * may be unused. + */ + unsigned _irq_attached; /* true if IRQ is already attached */ + + + /******************************************** + ** IRQ control server (internal use only) ** + ********************************************/ + + Irq_control_component _control_component; /* ctrl component */ + Capability _control_cap; /* capability for ctrl server */ + Irq_control_client _control_client; /* ctrl client */ + Capability _irq_cap; /* capability for IRQ */ + + public: + + /** + * Constructor + * + * \param cap_session capability session to use + * \param irq_alloc platform-dependent IRQ allocator + * \param args session construction arguments + */ + Irq_session_component(Cap_session *cap_session, + Range_allocator *irq_alloc, + const char *args); + + /** + * Destructor + */ + ~Irq_session_component(); + + /** + * Return capability to this session + * + * If an initialization error occurs, returned _cap is invalid. + */ + Capability cap() const { return _irq_cap; } + + + /*************************** + ** Irq session interface ** + ***************************/ + + void wait_for_irq(); + }; +} + +#endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/log_root.h b/base/src/core/include/log_root.h new file mode 100644 index 000000000..f9944f547 --- /dev/null +++ b/base/src/core/include/log_root.h @@ -0,0 +1,54 @@ +/* + * \brief Log root interface + * \author Norman Feske + * \date 2006-09-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LOG_ROOT_H_ +#define _CORE__INCLUDE__LOG_ROOT_H_ + +#include +#include + +#include "log_session_component.h" + +namespace Genode { + + class Log_root : public Root_component + { + protected: + + /** + * Root component interface + */ + Log_session_component *_create_session(const char *args) + { + char label_buf[Log_session_component::LABEL_LEN]; + + Arg label_arg = Arg_string::find_arg(args, "label"); + label_arg.string(label_buf, sizeof(label_buf), ""); + + return new (md_alloc()) Log_session_component(label_buf); + } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing cpu session objects + * \param md_alloc meta-data allocator to be used by root component + */ + Log_root(Rpc_entrypoint *session_ep, Allocator *md_alloc): + Root_component(session_ep, md_alloc) { } + }; +} + +#endif /* _CORE__INCLUDE__LOG_ROOT_H_ */ diff --git a/base/src/core/include/log_session_component.h b/base/src/core/include/log_session_component.h new file mode 100644 index 000000000..b81d4da74 --- /dev/null +++ b/base/src/core/include/log_session_component.h @@ -0,0 +1,85 @@ +/* + * \brief Log output service for Core + * \author Norman Feske + * \date 2006-09-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__LOG_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__LOG_SESSION_COMPONENT_H_ + +#include +#include +#include +#include + +namespace Genode { + + class Log_session_component : public Rpc_object + { + public: + + enum { LABEL_LEN = 64 }; + + private: + + char _label[LABEL_LEN]; + + public: + + /** + * Constructor + */ + Log_session_component(const char *label) { + strncpy(_label, label, sizeof(_label)); } + + + /***************** + ** Log session ** + *****************/ + + size_t write(String const &string_buf) + { + if (!(string_buf.is_valid_string())) { + PERR("corrupted string"); + return 0; + } + + char const *string = string_buf.string(); + int len = strlen(string); + + /* + * Heuristic: The Log console implementation flushes + * the output preferably in front of escape + * sequences. If the line contains only + * the escape sequence, we skip the printing + * of the label and cut the line break (last + * character). + */ + enum { ESC = 27 }; + if ((string[0] == ESC) && (len == 5) && (string[4] == '\n')) { + char buf[5]; + strncpy(buf, string, 5); + printf("%s", buf); + return len; + } + + printf("[init%s%s] %s", strcmp(_label, "") == 0 ? "" : " -> ", + _label, string); + + /* if last character of string was not a line break, add one */ + if ((len > 0) && (string[len - 1] != '\n')) + printf("\n"); + + return len; + } + }; +} + +#endif /* _CORE__INCLUDE__LOG_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/multiboot.h b/base/src/core/include/multiboot.h new file mode 100644 index 000000000..6e4abe972 --- /dev/null +++ b/base/src/core/include/multiboot.h @@ -0,0 +1,69 @@ +/** + * \brief GRUB multi-boot information handling + * \author Christian Helmuth + * \date 2006-05-09 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__MULTIBOOT_H_ +#define _CORE__INCLUDE__MULTIBOOT_H_ + +#include + +#include + +namespace Genode { + + class Multiboot_info + { + private: + + /* Location of MBI in memory */ + void *_mb_info; + + public: + + /** Standard constructor creates invalid object */ + Multiboot_info() : _mb_info(0) { } + + Multiboot_info(void *mb_info); + + /** + * Number of boot modules + */ + unsigned num_modules(); + + /** + * Use boot module num + * + * The module is marked as invalid in MBI and cannot be gotten again + */ + Rom_module get_module(unsigned num); + + /** + * Read module info + */ + bool check_module(unsigned num, addr_t *start, addr_t *end); + + /** + * Debugging (may be removed later) + */ + void print_debug(); + + /** + * Check validity + */ + bool valid() { return _mb_info ? true : false; } + + /* Accessors */ + size_t size() const { return 0x1000; } + }; +} + +#endif /* _CORE__INCLUDE__MULTIBOOT_H_ */ diff --git a/base/src/core/include/pd_root.h b/base/src/core/include/pd_root.h new file mode 100644 index 000000000..4db48e1d1 --- /dev/null +++ b/base/src/core/include/pd_root.h @@ -0,0 +1,54 @@ +/* + * \brief PD root interface + * \author Christian Helmuth + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PD_ROOT_H_ +#define _CORE__INCLUDE__PD_ROOT_H_ + +/* Genode */ +#include + +/* Core */ +#include + +namespace Genode { + + class Pd_root : public Root_component + { + private: + + Rpc_entrypoint *_thread_ep; + + protected: + + Pd_session_component *_create_session(const char *args) { + return new (md_alloc()) Pd_session_component(_thread_ep, args); } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing pd session objects + * \param thread_ep entry point for managing threads + * \param md_alloc meta-data allocator to be used by root component + */ + Pd_root(Rpc_entrypoint *session_ep, + Rpc_entrypoint *thread_ep, + Allocator *md_alloc) + : + Root_component(session_ep, md_alloc), + _thread_ep(thread_ep) { } + }; +} + +#endif /* _CORE__INCLUDE__PD_ROOT_H_ */ diff --git a/base/src/core/include/pd_session_component.h b/base/src/core/include/pd_session_component.h new file mode 100644 index 000000000..1d8078b13 --- /dev/null +++ b/base/src/core/include/pd_session_component.h @@ -0,0 +1,49 @@ +/* + * \brief Core-specific instance of the PD session interface + * \author Christian Helmuth + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Pd_session_component : public Rpc_object + { + private: + + Platform_pd _pd; + Parent_capability _parent; + Rpc_entrypoint *_thread_ep; + + public: + + Pd_session_component(Rpc_entrypoint *thread_ep, const char *args) + : _thread_ep(thread_ep) { } + + + /**************************/ + /** PD session interface **/ + /**************************/ + + int bind_thread(Thread_capability); + int assign_parent(Parent_capability); + }; +} + +#endif /* _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/platform_generic.h b/base/src/core/include/platform_generic.h new file mode 100644 index 000000000..b6a5fa097 --- /dev/null +++ b/base/src/core/include/platform_generic.h @@ -0,0 +1,109 @@ +/* + * \brief Generic platform + * \author Norman Feske + * \author Christian Helmuth + * \date 2007-09-10 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__PLATFORM_GENERIC_H_ +#define _CORE__INCLUDE__PLATFORM_GENERIC_H_ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +namespace Genode { + + /** + * Generic platform interface + */ + class Platform_generic + { + public: + + virtual ~Platform_generic() { } + + /** + * Allocator of core-local mapped virtual memory + */ + virtual Allocator *core_mem_alloc() = 0; + + /** + * Allocator of physical memory + */ + virtual Range_allocator *ram_alloc() = 0; + + /** + * Allocator of free address ranges within core + */ + virtual Range_allocator *region_alloc() = 0; + + /** + * I/O memory allocator + */ + virtual Range_allocator *io_mem_alloc() = 0; + + /** + * I/O port allocator + */ + virtual Range_allocator *io_port_alloc() = 0; + + /** + * IRQ allocator + */ + virtual Range_allocator *irq_alloc() = 0; + + /** + * Virtual memory configuration accessors + */ + virtual addr_t vm_start() const = 0; + virtual size_t vm_size() const = 0; + + /** + * ROM modules + */ + virtual Rom_fs *rom_fs() = 0; + + /** + * Wait for exit condition + */ + virtual void wait_for_exit() = 0; + + /** + * Return true if platform supports unmap + */ + virtual bool supports_unmap() { return true; } + + /** + * Return true if platform supports direct unmap (no mapping db) + */ + virtual bool supports_direct_unmap() const { return false; } + }; + + + /** + * Request pointer to static generic platform interface of core + */ + extern Platform_generic *platform(); + + class Platform; + + /** + * Access the platform-specific platform interface of core + * + * This function should only be called from platform-specific code. + */ + extern Platform *platform_specific(); +} + +#endif /* _CORE__INCLUDE__PLATFORM_GENERIC_H_ */ diff --git a/base/src/core/include/ram_root.h b/base/src/core/include/ram_root.h new file mode 100644 index 000000000..e8a9da1de --- /dev/null +++ b/base/src/core/include/ram_root.h @@ -0,0 +1,65 @@ +/* + * \brief RAM root interface + * \author Norman Feske + * \date 2006-05-30 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__RAM_ROOT_H_ +#define _CORE__INCLUDE__RAM_ROOT_H_ + +#include + +#include "ram_session_component.h" + +namespace Genode { + + class Ram_root : public Root_component + { + private: + + Range_allocator *_ram_alloc; + Rpc_entrypoint *_ds_ep; + + protected: + + Ram_session_component *_create_session(const char *args) + { + return new (md_alloc()) + Ram_session_component(_ds_ep, ep(), _ram_alloc, + md_alloc(), args); + } + + void _upgrade_session(Ram_session_component *ram, const char *args) + { + size_t ram_quota = Arg_string::find_arg(args, "ram_quota").long_value(0); + ram->upgrade_ram_quota(ram_quota); + } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing ram session objects + * \param ds_ep entry point for managing dataspaces + * \param ram_alloc pool of memory to be assigned to ram sessions + * \param md_alloc meta-data allocator to be used by root component + */ + Ram_root(Rpc_entrypoint *session_ep, + Rpc_entrypoint *ds_ep, + Range_allocator *ram_alloc, + Allocator *md_alloc) + : + Root_component(session_ep, md_alloc), + _ram_alloc(ram_alloc), _ds_ep(ds_ep) { } + }; +} + +#endif /* _CORE__INCLUDE__RAM_ROOT_H_ */ diff --git a/base/src/core/include/ram_session_component.h b/base/src/core/include/ram_session_component.h new file mode 100644 index 000000000..c60e19a87 --- /dev/null +++ b/base/src/core/include/ram_session_component.h @@ -0,0 +1,163 @@ +/* + * \brief Core-specific instance of the RAM session interface + * \author Norman Feske + * \date 2006-06-19 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__RAM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__RAM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + class Ram_session_component; + typedef List Ram_ref_account_members; + + class Ram_session_component : public Rpc_object, + public Ram_ref_account_members::Element + { + private: + + enum { SBS = 1024 }; /* slab block size */ + + typedef Tslab Ds_slab; + + Rpc_entrypoint *_ds_ep; + Rpc_entrypoint *_ram_session_ep; + Range_allocator *_ram_alloc; + size_t _quota_limit; + size_t _payload; /* quota used for payload */ + Allocator_guard _md_alloc; /* guarded meta-data allocator */ + Ds_slab _ds_slab; /* meta-data allocator */ + Ram_session_component *_ref_account; /* reference ram session */ + + enum { MAX_LABEL_LEN = 64 }; + char _label[MAX_LABEL_LEN]; + + /** + * List of RAM sessions that use us as their reference account + */ + Ram_ref_account_members _ref_members; + Lock _ref_members_lock; /* protect '_ref_members' */ + + /** + * Register RAM session to use us as reference account + */ + void _register_ref_account_member(Ram_session_component *new_member); + + /** + * Dissolve reference-account relationship of a member account + */ + void _remove_ref_account_member(Ram_session_component *member); + void _unsynchronized_remove_ref_account_member(Ram_session_component *member); + + /** + * Return portion of RAM quota that is currently in use + */ + size_t used_quota() { + return _ds_slab.consumed() + _payload + sizeof(*this); } + + /** + * Free dataspace + */ + void _free_ds(Dataspace_component *ds); + + /** + * Transfer quota to another RAM session + */ + int _transfer_quota(Ram_session_component *dst, size_t amount); + + + /******************************************** + ** Platform-implemented support functions ** + ********************************************/ + + /** + * Export RAM dataspace as shared memory block + */ + void _export_ram_ds(Dataspace_component *ds); + + /** + * Revert export of RAM dataspace + */ + void _revoke_ram_ds(Dataspace_component *ds); + + /** + * Zero-out content of dataspace + */ + void _clear_ds(Dataspace_component *ds); + + + public: + + /** + * Constructor + * + * \param ds_ep server entry point to manage the + * dataspaces created by the Ram session + * \param ram_session_ep entry point that manages Ram sessions, + * used for looking up another ram session + * in transfer_quota() + * \param ram_alloc memory pool to manage + * \param md_alloc meta-data allocator + * \param md_ram_quota limit of meta-data backing store + * \param quota_limit initial quota limit + * + * The 'quota_limit' parameter is only used for the very + * first ram session in the system. All other ram session + * load their quota via 'transfer_quota'. + */ + Ram_session_component(Rpc_entrypoint *ds_ep, + Rpc_entrypoint *ram_session_ep, + Range_allocator *ram_alloc, + Allocator *md_alloc, + const char *args, + size_t quota_limit = 0); + + /** + * Destructor + */ + ~Ram_session_component(); + + + /** + * Accessors + */ + Ram_session_component *ref_account() { return _ref_account; } + + + /** + * Register quota donation at allocator guard + */ + void upgrade_ram_quota(size_t ram_quota) { _md_alloc.upgrade(ram_quota); } + + + /*************************** + ** RAM Session interface ** + ***************************/ + + Ram_dataspace_capability alloc(size_t); + void free(Ram_dataspace_capability); + int ref_account(Ram_session_capability); + int transfer_quota(Ram_session_capability, size_t); + size_t quota() { return _quota_limit; } + size_t used() { return _payload; } + }; +} + +#endif /* _CORE__INCLUDE__RAM_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/rm_root.h b/base/src/core/include/rm_root.h new file mode 100644 index 000000000..5aefc0b03 --- /dev/null +++ b/base/src/core/include/rm_root.h @@ -0,0 +1,97 @@ +/** + * \brief RM root interface + * \author Christian Helmuth + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__RM_ROOT_H_ +#define _CORE__INCLUDE__RM_ROOT_H_ + +/* Genode */ +#include + +/* Core */ +#include + +namespace Genode { + + class Rm_root : public Root_component + { + private: + + Rpc_entrypoint *_ds_ep; + Rpc_entrypoint *_thread_ep; + Allocator *_md_alloc; + + enum { PAGER_STACK_SIZE = 2*4096 }; + Pager_activation _pager_thread; + + Pager_entrypoint _pager_ep; + + addr_t _vm_start; + size_t _vm_size; + + protected: + + Rm_session_component *_create_session(const char *args) + { + addr_t start = Arg_string::find_arg(args, "start").ulong_value(~0UL); + size_t size = Arg_string::find_arg(args, "size").ulong_value(0); + size_t ram_quota = Arg_string::find_arg(args, "ram_quota").long_value(0); + + return new (md_alloc()) + Rm_session_component(_ds_ep, + _thread_ep, + _md_alloc, ram_quota, + &_pager_ep, + start == ~0UL ? _vm_start : start, + size == 0 ? _vm_size : size); + } + + void _upgrade_session(Rm_session_component *rm, const char *args) + { + size_t ram_quota = Arg_string::find_arg(args, "ram_quota").long_value(0); + rm->upgrade_ram_quota(ram_quota); + } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing RM session objects + * \param ds_ep entry point for managing dataspaces + * \param thread_ep entry point for managing threads + * \param md_alloc meta data allocator to be used by root component + * \param cap_session allocator for pager-object capabilities + * \param vm_start begin of virtual memory (default value) + * \param vm_size size of virtual memory (default value) + */ + Rm_root(Rpc_entrypoint *session_ep, + Rpc_entrypoint *ds_ep, + Rpc_entrypoint *thread_ep, + Allocator *md_alloc, + Cap_session *cap_session, + addr_t vm_start, + size_t vm_size) + : + Root_component(session_ep, md_alloc), + _ds_ep(ds_ep), _thread_ep(thread_ep), _md_alloc(md_alloc), + _pager_thread(), _pager_ep(cap_session, &_pager_thread), + _vm_start(vm_start), _vm_size(vm_size) { } + + /** + * Return pager entrypoint + */ + Pager_entrypoint *pager_ep() { return &_pager_ep; } + }; +} + +#endif /* _CORE__INCLUDE__RM_ROOT_H_ */ diff --git a/base/src/core/include/rm_session_component.h b/base/src/core/include/rm_session_component.h new file mode 100644 index 000000000..d460c36be --- /dev/null +++ b/base/src/core/include/rm_session_component.h @@ -0,0 +1,354 @@ +/* + * \brief RM session interface + * \author Christian Helmuth + * \author Norman Feske + * \date 2006-07-17 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__RM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__RM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* core includes */ +#include +#include +#include + +namespace Genode { + + + class Dataspace_component; + class Rm_session_component; + class Rm_client; + + /** + * Representation of a single entry of a region-manager session + * + * Each 'Rm_region' is associated with one dataspace and makes a portion + * of this dataspace visible in a address space of a region-manager session. + * All 'Rm_regions' to which one and the same dataspace is attached to, are + * organized in a linked list. The head of the list is a member of the + * 'Dataspace_component'. + */ + class Rm_region : public List::Element + { + private: + + addr_t _base; + size_t _size; + bool _write; + + Dataspace_component *_dsc; + off_t _off; + + Rm_session_component *_session; /* corresponding region manager + session */ + + public: + + /** + * Default constructor - invalid region + */ + Rm_region() { } + + Rm_region(addr_t base, size_t size, bool write, + Dataspace_component *dsc, off_t offset, + Rm_session_component *session) + : _base(base), _size(size), _write(write), + _dsc(dsc), _off(offset), _session(session) { } + + + /*************** + ** Accessors ** + ***************/ + + addr_t base() const { return _base; } + size_t size() const { return _size; } + bool write() const { return _write; } + Dataspace_component* dataspace() const { return _dsc; } + off_t offset() const { return _off; } + Rm_session_component* session() const { return _session; } + }; + + + /** + * Member of faulter list + * + * Each 'Rm_client' can fault not only at the RM session that it is member + * of but also on any other RM session used as a nested dataspace. If a + * 'Rm_client' faults, it gets enqueued at the leaf RM session that + * detected the fault and waits for this RM session to resolve the fault. + * For example, the dataspace manager that resolves the faults for the + * nested dataspace exported to its client. Because each RM session must + * be able to handle faults by arbitrary clients (not only its own + * clients), it maintains the list head of faulters. + */ + class Rm_faulter : public List::Element + { + private: + + Pager_object *_pager_object; + Lock _lock; + Rm_session_component *_faulting_rm_session; + Rm_session::State _fault_state; + + public: + + /** + * Constructor + * + * \param Pager_object pager object that corresponds to the faulter + * + * Currently, there is only one pager in core. + */ + Rm_faulter(Pager_object *pager_object) : + _pager_object(pager_object), _faulting_rm_session(0) { } + + /** + * Assign fault state + */ + void fault(Rm_session_component *faulting_rm_session, + Rm_session::State fault_state); + + /** + * Disassociate faulter from the faulted region-manager session + * + * This function must be called when destructing region-manager + * sessions to prevent dangling pointers in '_faulters' lists. + */ + void dissolve_from_faulting_rm_session(); + + /** + * Return true if page fault occurred in specified address range + */ + bool fault_in_addr_range(addr_t addr, size_t size) { + return (_fault_state.addr >= addr) && (_fault_state.addr <= addr + size - 1); } + + /** + * Return fault state as exported via the rm-session interface + */ + Rm_session::State fault_state() { return _fault_state; } + + /** + * Wake up faulter by answering the pending page fault + */ + void continue_after_resolved_fault(); + }; + + + /** + * Member role of region manager session + * + * A region-manager session can be used as address space for any number + * of threads (region-manager clients). This class represents the client's + * role as member of this address space. + */ + class Rm_member : public List + { + private: + + Rm_session_component *_rm_session; + + public: + + /** + * Constructor + */ + Rm_member(Rm_session_component *rm_session): _rm_session(rm_session) { } + + /** + * Return region-manager session that the RM client is member of + */ + Rm_session_component *member_rm_session() { return _rm_session; } + }; + + + class Rm_client : public Pager_object, public Rm_member, public Rm_faulter, + public List::Element + { + public: + + /** + * Constructor + * + * \param session RM session to which the client belongs + * \param badge pager-object badge used of identifying the client + * when a page-fault occurs + */ + Rm_client(Rm_session_component *session, unsigned long badge) : + Pager_object(badge), Rm_member(session), Rm_faulter(this) { } + + int pager(Ipc_pager &pager); + + /** + * Flush memory mappings for the specified virtual address range + */ + void unmap(addr_t core_local_base, addr_t virt_base, size_t size); + }; + + + class Rm_session_component : public Rpc_object + { + private: + + Rpc_entrypoint *_ds_ep; + Rpc_entrypoint *_thread_ep; + + Allocator_guard _md_alloc; + Signal_transmitter _fault_notifier; /* notification mechanism for + region-manager faults */ + + /********************* + ** Paging facility ** + *********************/ + + class Rm_region_ref : public List::Element + { + private: + + Rm_region *_region; + + public: + + Rm_region_ref(Rm_region *region) : _region(region) { } + + Rm_region* region() const { return _region; } + }; + + + class Rm_dataspace_component : public Dataspace_component + { + private: + + Rm_session_component *_rm_session_component; + + public: + + /** + * Constructor + */ + Rm_dataspace_component(Rm_session_component *rsc, size_t size) : + Dataspace_component(size, 0, false), + _rm_session_component(rsc) { _managed = true; } + + + /*********************************** + ** Dataspace component interface ** + ***********************************/ + + Rm_session_component *sub_rm_session() { return _rm_session_component; } + }; + + + Tslab _client_slab; /* backing store for + client structures */ + Tslab _ref_slab; /* backing store for + region list */ + Allocator_avl_tpl _map; /* region map for attach, + detach, pagefaults */ + List _regions; /* region list for destruction */ + + List _faulters; /* list of threads that faulted at + the region-manager session and wait + for fault resolution */ + List _clients; /* list of RM clients using this RM + session */ + Lock _lock; /* lock for map and list */ + Pager_entrypoint *_pager_ep; + Rm_dataspace_component _ds; /* dataspace representation of region map */ + Dataspace_capability _ds_cap; + + public: + + /** + * Constructor + */ + Rm_session_component(Rpc_entrypoint *ds_ep, + Rpc_entrypoint *thread_ep, + Allocator *md_alloc, + size_t ram_quota, + Pager_entrypoint *pager_ep, + addr_t vm_start, + size_t vm_size); + + ~Rm_session_component(); + + class Fault_area; + + /** + * Reversely lookup dataspace and offset matching the specified address + * + * \return true lookup succeeded + */ + bool reverse_lookup(addr_t dst_base, + Fault_area *dst_fault_region, + Dataspace_component **src_dataspace, + Fault_area *src_fault_region); + + /** + * Register fault + * + * This function is called by the pager to schedule a page fault + * for resolution. + * + * \param faulter faulting region-manager client + * \param pf_addr page-fault address + * \param pf_type type of page fault (read/write/execute) + */ + void fault(Rm_faulter *faulter, addr_t pf_addr, + Rm_session::Fault_type pf_type); + + /** + * Dissolve faulter from region-manager session + */ + void discard_faulter(Rm_faulter *faulter); + + List *clients() { return &_clients; } + + /** + * Return the dataspace representation of this session + */ + Rm_dataspace_component *dataspace_component() { return &_ds; } + + /** + * Register quota donation at allocator guard + */ + void upgrade_ram_quota(size_t ram_quota) { _md_alloc.upgrade(ram_quota); } + + /** + * Dissolves client from region-manager session + */ + void dissolve(Rm_client *cl); + + + /************************************** + ** Region manager session interface ** + **************************************/ + + Local_addr attach (Dataspace_capability, size_t, off_t, bool, Local_addr); + void detach (Local_addr); + Pager_capability add_client (Thread_capability); + void fault_handler (Signal_context_capability handler); + State state (); + Dataspace_capability dataspace () { return _ds_cap; } + }; +} + +#endif /* _CORE__INCLUDE__RM_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/rom_fs.h b/base/src/core/include/rom_fs.h new file mode 100644 index 000000000..0f4b00a6c --- /dev/null +++ b/base/src/core/include/rom_fs.h @@ -0,0 +1,100 @@ +/** + * \brief Read-only memory modules + * \author Christian Helmuth + * \date 2006-05-15 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__ROM_FS_H_ +#define _CORE__INCLUDE__ROM_FS_H_ + +#include +#include +#include +#include + +namespace Genode { + + /** + * Convert module command line to module base name + * + * The conversion is performed in place. The returned + * pointer refers to a substring of the 'name' argument. + */ + inline char *commandline_to_basename(char *name) + { + for (char *c = name; *c != 0; c++) { + if (*c == '/') name = c + 1; + if (*c == ' ') { + *c = 0; + break; + } + } + return name; + } + + class Rom_module : public Avl_string_base + { + private: + + /* Location of module in memory and size */ + addr_t _addr; + size_t _size; + + public: + + /** Standard constructor creates invalid object */ + Rom_module() + : Avl_string_base(0), _addr(0), _size(0) { } + + Rom_module(addr_t addr, size_t size, const char *name) + : Avl_string_base(name), _addr(addr), _size(size) { } + + /** Check validity */ + bool valid() { return _size ? true : false; } + + /** Accessor functions */ + addr_t addr() const { return _addr; } + size_t size() const { return _size; } + }; + + class Rom_fs : public Avl_tree + { + public: + + Rom_module * find(const char *name) + { + return first() ? (Rom_module *)first()->find_by_name(name) : 0; + } + + /* DEBUG */ + void print_fs(Rom_module *r = 0) + { + if (!r) { + Rom_module *first_module = (Rom_module *)first(); + if (first_module) { + printf("Rom_fs %p dump:\n", this); + print_fs(first_module); + } else { + printf("No modules in Rom_fs %p\n", this); + } + } else { + Rom_module *child; + + printf(" Rom: [%08lx,%08lx) %s\n", + r->addr(), r->addr() + r->size(), r->name()); + + if ((child = (Rom_module *)r->child(Rom_module::LEFT))) print_fs(child); + if ((child = (Rom_module *)r->child(Rom_module::RIGHT))) print_fs(child); + } + } + }; +} + +#endif /* _CORE__INCLUDE__ROM_FS_H_ */ diff --git a/base/src/core/include/rom_root.h b/base/src/core/include/rom_root.h new file mode 100644 index 000000000..d04b28268 --- /dev/null +++ b/base/src/core/include/rom_root.h @@ -0,0 +1,55 @@ +/* + * \brief ROM root interface + * \author Norman Feske + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__ROM_ROOT_H_ +#define _CORE__INCLUDE__ROM_ROOT_H_ + +#include +#include "rom_session_component.h" + +namespace Genode { + + class Rom_root : public Root_component + { + + private: + + Rom_fs *_rom_fs; /* rom file system */ + Rpc_entrypoint *_ds_ep; /* entry point for managing rom dataspaces */ + + protected: + + Rom_session_component *_create_session(const char *args) { + return new (md_alloc()) Rom_session_component(_rom_fs, _ds_ep, args); } + + public: + + /** + * Constructor + * + * \param session_ep entry point for managing ram session objects + * \param ds_ep entry point for managing dataspaces + * \param rom_fs platform ROM file system + * \param md_alloc meta-data allocator to be used by root component + */ + Rom_root(Rpc_entrypoint *session_ep, + Rpc_entrypoint *ds_ep, + Rom_fs *rom_fs, + Allocator *md_alloc) + : + Root_component(session_ep, md_alloc), + _rom_fs(rom_fs), _ds_ep(ds_ep) { } + }; +} + +#endif /* _CORE__INCLUDE__ROM_ROOT_H_ */ diff --git a/base/src/core/include/rom_session_component.h b/base/src/core/include/rom_session_component.h new file mode 100644 index 000000000..00e574f6b --- /dev/null +++ b/base/src/core/include/rom_session_component.h @@ -0,0 +1,72 @@ +/* + * \brief Core-specific instance of the ROM session interface + * \author Norman Feske + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__ROM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__ROM_SESSION_COMPONENT_H_ + +#include +#include +#include +#include + +namespace Genode { + + class Rom_session_component : public Rpc_object + { + private: + + Rom_module *_rom_module; + char _fname[32]; + Dataspace_component _ds; + Rpc_entrypoint *_ds_ep; + Rom_dataspace_capability _ds_cap; + + Rom_module * _find_rom(Rom_fs *rom_fs, const char *args) + { + /* extract filename from session arguments */ + Arg_string::find_arg(args, "filename").string(_fname, sizeof(_fname), ""); + + /* find ROM module for file name */ + return rom_fs->find(_fname); + } + + public: + + /** + * Constructor + * + * \param rom_fs ROM filesystem + * \param ds_ep entry point to manage the dataspace + * corresponding the rom session + * \param args session-construction arguments, in + * particular the filename + */ + Rom_session_component(Rom_fs *rom_fs, + Rpc_entrypoint *ds_ep, + const char *args); + + /** + * Destructor + */ + ~Rom_session_component(); + + + /*************************** + ** Rom session interface ** + ***************************/ + + Rom_dataspace_capability dataspace() { return _ds_cap; } + }; +} + +#endif /* _CORE__INCLUDE__ROM_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/include/signal_root.h b/base/src/core/include/signal_root.h new file mode 100644 index 000000000..2b59e9798 --- /dev/null +++ b/base/src/core/include/signal_root.h @@ -0,0 +1,75 @@ +/* + * \brief Signal root interface + * \author Norman Feske + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__SIGNAL_ROOT_H_ +#define _CORE__INCLUDE__SIGNAL_ROOT_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include + +namespace Genode { + + class Signal_handler + { + private: + + enum { STACK_SIZE = 4096 }; + Rpc_entrypoint _ep; + + public: + + Signal_handler(Cap_session *cap_session) : + _ep(cap_session, STACK_SIZE, "signal") + { } + + Rpc_entrypoint *entrypoint() { return &_ep; } + }; + + class Signal_root : private Signal_handler, + public Root_component + { + protected: + + Signal_session_component *_create_session(const char *args) + { + size_t ram_quota = Arg_string::find_arg(args, "ram_quota").long_value(0); + return new (md_alloc()) + Signal_session_component(entrypoint(), entrypoint(), + md_alloc(), ram_quota); + } + + void _upgrade_session(Signal_session_component *s, const char *args) + { + size_t ram_quota = Arg_string::find_arg(args, "ram_quota").long_value(0); + s->upgrade_ram_quota(ram_quota); + } + + public: + + /** + * Constructor + * + * \param md_alloc meta-data allocator to be used by root component + */ + Signal_root(Allocator *md_alloc, Cap_session *cap_session) + : + Signal_handler(cap_session), + Root_component(entrypoint(), md_alloc) + { } + }; +} + +#endif /* _CORE__INCLUDE__SIGNAL_ROOT_H_ */ diff --git a/base/src/core/include/signal_session_component.h b/base/src/core/include/signal_session_component.h new file mode 100644 index 000000000..499a3b077 --- /dev/null +++ b/base/src/core/include/signal_session_component.h @@ -0,0 +1,166 @@ +/* + * \brief Signal service + * \author Norman Feske + * \date 2009-08-05 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__SIGNAL_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__SIGNAL_SESSION_COMPONENT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + class Signal_source_component; + class Signal_context_component; + + typedef Fifo Signal_queue; + + class Signal_context_component : public Rpc_object, + public Signal_queue::Element + { + private: + + long _imprint; + int _cnt; + Signal_source_component *_source; + + public: + + /** + * Constructor + */ + Signal_context_component(long imprint, Signal_source_component *source) + : _imprint(imprint), _cnt(0), _source(source) { } + + /** + * Increment number of signals to be delivered at once + */ + void increment_signal_cnt(int increment) { _cnt += increment; } + + /** + * Reset number of pending signals + */ + void reset_signal_cnt() { _cnt = 0; } + + long imprint() { return _imprint; } + int cnt() { return _cnt; } + Signal_source_component *source() { return _source; } + }; + + + class Signal_source_component : public Signal_source_rpc_object + { + private: + + Signal_queue _signal_queue; + Rpc_entrypoint *_entrypoint; + Native_capability _reply_cap; + + public: + + /** + * Constructor + */ + Signal_source_component(Rpc_entrypoint *rpc_entrypoint); + + void submit(Signal_context_component *context, + Ipc_ostream *ostream, + int cnt); + + + /***************************** + ** Signal-source interface ** + *****************************/ + + Signal wait_for_signal(); + }; + + + class Signal_session_component : public Rpc_object + { + private: + + Rpc_entrypoint *_source_ep; + Rpc_entrypoint *_context_ep; + Signal_source_component _source; + Signal_source_capability _source_cap; + Allocator_guard _md_alloc; + Tslab _contexts_slab; + Ipc_ostream *_ipc_ostream; + + public: + + /** + * Constructor + * + * \param source_ep entrypoint holding signal-source component + * objects + * \param context_ep global pool of all signal contexts + * \param md_alloc backing-store allocator for + * signal-context component objects + * + * To maintain proper synchronization, 'signal_source_ep' must be + * the same entrypoint as used for the signal-session component. + * The 'signal_context_ep' is only used for associative array + * to map signal-context capabilities to 'Signal_context_component' + * objects and as capability allocator for such objects. + */ + Signal_session_component(Rpc_entrypoint *source_ep, + Rpc_entrypoint *context_ep, + Allocator *context_md_alloc, + size_t ram_quota); + + ~Signal_session_component(); + + /** + * Register quota donation at allocator guard + */ + void upgrade_ram_quota(size_t ram_quota) { _md_alloc.upgrade(ram_quota); } + + + /****************************** + ** Signal-session interface ** + ******************************/ + + Signal_source_capability signal_source(); + Signal_context_capability alloc_context(long imprint); + void free_context(Signal_context_capability context_cap); + void submit(Signal_context_capability context_cap, unsigned cnt); + + + /************************** + ** Rpc_object interface ** + **************************/ + + Rpc_exception_code dispatch(int opcode, Ipc_istream &is, Ipc_ostream &os) + { + /* + * Make IPC output stream available to the submit function. The + * stream is used to carry signal payload for the out-of-order + * handling of 'wait_for_signal' replies. + */ + _ipc_ostream = &os; + + /* dispatch RPC */ + return Rpc_object::dispatch(opcode, is, os); + } + }; +} + +#endif /* _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ */ diff --git a/base/src/core/io_mem_session_component.cc b/base/src/core/io_mem_session_component.cc new file mode 100644 index 000000000..f88664118 --- /dev/null +++ b/base/src/core/io_mem_session_component.cc @@ -0,0 +1,114 @@ +/* + * \brief Core implementation of the IO_MEM session interface + * \author Christian Helmuth + * \date 2006-08-01 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +#include "util.h" + +using namespace Genode; + + +static const bool verbose = false; + + +Io_mem_session_component::Dataspace_attr +Io_mem_session_component::_prepare_io_mem(const char *args, + Range_allocator *ram_alloc) +{ + addr_t req_base = Arg_string::find_arg(args, "base").ulong_value(0); + size_t req_size = Arg_string::find_arg(args, "size").ulong_value(0); + + /* align base and size on page boundaries */ + addr_t end = align_addr(req_base + req_size, get_page_size_log2()); + addr_t base = req_base & ~(get_page_size() - 1); + size_t size = end - base; + + _write_combined = false; + + Arg a = Arg_string::find_arg(args, "wc"); + if (a.valid()) + _write_combined = a.bool_value(0); + + /* check for RAM collision */ + int ret; + if ((ret = ram_alloc->remove_range(base, size))) { + PERR("I/O memory [%lx,%lx) used by RAM allocator (%d)", base, base + size, ret); + return Dataspace_attr(0, 0, 0, 0); + } + + /* allocate region */ + switch (_io_mem_alloc->alloc_addr(req_size, req_base)) { + case Range_allocator::RANGE_CONFLICT: + PERR("I/O memory [%lx,%lx) not available", base, base + size); + return Dataspace_attr(0, 0, 0, 0); + + case Range_allocator::OUT_OF_METADATA: + PERR("I/O memory allocator ran out of meta data"); + return Dataspace_attr(0, 0, 0, 0); + + case Range_allocator::ALLOC_OK: break; + } + + /* request local mapping */ + addr_t local_addr = _map_local(base, size); + + if (verbose) + PDBG("I/O mem [%lx,%lx) => [%lx,%lx)%s", + base, base + size, local_addr, local_addr + size, + _write_combined ? " (write-combined)" : ""); + + return Dataspace_attr(size, local_addr, base, _write_combined); +} + + +Io_mem_session_component::Io_mem_session_component(Range_allocator *io_mem_alloc, + Range_allocator *ram_alloc, + Rpc_entrypoint *ds_ep, + const char *args) +: + _io_mem_alloc(io_mem_alloc), + _ds(_prepare_io_mem(args, ram_alloc)), + _ds_ep(ds_ep) +{ + if (!_ds.valid()) { + PERR("Local MMIO mapping failed!"); + + _ds_cap = Io_mem_dataspace_capability(); + throw Root::Invalid_args(); + } + + _ds_cap = static_cap_cast(_ds_ep->manage(&_ds)); +} + + +Io_mem_session_component::~Io_mem_session_component() +{ + /* dissolve IO_MEM dataspace from service entry point */ + _ds_ep->dissolve(&_ds); + + /* flush local mapping of IO_MEM */ + _unmap_local(_ds.core_local_addr(), _ds.size()); + + /* + * The Dataspace will remove itself from all RM sessions when its + * destructor is called. Thereby, it will get unmapped from all RM + * clients that currently have the dataspace attached. + */ + + /* free region in IO_MEM allocator */ + _io_mem_alloc->free(reinterpret_cast(_ds.phys_addr())); +} diff --git a/base/src/core/main.cc b/base/src/core/main.cc new file mode 100644 index 000000000..23c8143ad --- /dev/null +++ b/base/src/core/main.cc @@ -0,0 +1,245 @@ +/* + * \brief Core main program + * \author Norman Feske + * \date 2006-07-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + + +/* support for cap session component */ +long Cap_session_component::_unique_id_cnt; + +/* pool of provided core services */ +static Service_registry local_services; + + +/*************************************** + ** Core environment/platform support ** + ***************************************/ + +Core_env * Genode::core_env() +{ + /* + * Make sure to initialize the platform before constructing the core + * environment. + */ + platform(); + + /* + * By placing the environment as static object here, we ensure that its + * constructor gets called when this function is used the first time. + */ + static Core_env _env; + return &_env; +} + + +Env * Genode::env() { + return core_env(); } + + +Platform *Genode::platform_specific() +{ + static Platform _platform; + return &_platform; +} + + +Platform_generic *Genode::platform() { return platform_specific(); } + + +/************************* + ** Core parent support ** + *************************/ + +Session_capability Core_parent::session(Parent::Service_name const &name, + Parent::Session_args const &args) +{ + Service *service = local_services.find(name.string()); + + if (service) + return service->session(args.string()); + + PWRN("service_name=\"%s\" arg=\"%s\" not handled", name.string(), args.string()); + return Session_capability(); +} + + +/**************** + ** Core child ** + ****************/ + +class Core_child : public Child_policy +{ + private: + + /* + * Entry point used for serving the parent interface + */ + Rpc_entrypoint _entrypoint; + enum { STACK_SIZE = 8*1024 }; + + Child _child; + + Service_registry *_local_services; + + public: + + /** + * Constructor + */ + Core_child(Dataspace_capability elf_ds, Cap_session *cap_session, + Ram_session_capability ram, Cpu_session_capability cpu, + Rm_session_capability rm, Service_registry *services) + : + _entrypoint(cap_session, STACK_SIZE, "init", false), + _child(elf_ds, ram, cpu, rm, &_entrypoint, this), + _local_services(services) + { + _entrypoint.activate(); + } + + + /**************************** + ** Child-policy interface ** + ****************************/ + + const char *name() const { return "init"; } + + Service *resolve_session_request(const char *service, const char *) + { + return _local_services->find(service); + } +}; + + +/*************** + ** Core main ** + ***************/ + +int main() +{ + PDBG("--- create local services ---"); + + /* + * Initialize root interfaces for our services + */ + Rpc_entrypoint *e = core_env()->entrypoint(); + + /* + * Allocate session meta data on distinct dataspaces to enable independent + * destruction (to enable quota trading) of session component objects. + */ + static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session()); + + static Cap_root cap_root (e, &sliced_heap); + static Ram_root ram_root (e, e, platform()->ram_alloc(), &sliced_heap); + static Rom_root rom_root (e, e, platform()->rom_fs(), &sliced_heap); + static Rm_root rm_root (e, e, e, &sliced_heap, core_env()->cap_session(), + platform()->vm_start(), platform()->vm_size()); + static Cpu_root cpu_root (e, e, rm_root.pager_ep(), &sliced_heap); + static Pd_root pd_root (e, e, &sliced_heap); + static Log_root log_root (e, &sliced_heap); + static Io_mem_root io_mem_root (e, e, platform()->io_mem_alloc(), + platform()->ram_alloc(), &sliced_heap); + static Io_port_root io_port_root (core_env()->cap_session(), platform()->io_port_alloc(), &sliced_heap); + static Irq_root irq_root (core_env()->cap_session(), + platform()->irq_alloc(), &sliced_heap); + static Signal_root signal_root (&sliced_heap, core_env()->cap_session()); + + /* + * Play our role as parent of init and declare our services. + */ + + static Local_service ls[] = { + Local_service(Rom_session::service_name(), &rom_root), + Local_service(Ram_session::service_name(), &ram_root), + Local_service(Cap_session::service_name(), &cap_root), + Local_service(Rm_session::service_name(), &rm_root), + Local_service(Cpu_session::service_name(), &cpu_root), + Local_service(Pd_session::service_name(), &pd_root), + Local_service(Log_session::service_name(), &log_root), + Local_service(Io_mem_session::service_name(), &io_mem_root), + Local_service(Io_port_session::service_name(), &io_port_root), + Local_service(Irq_session::service_name(), &irq_root), + Local_service(Signal_session::service_name(), &signal_root) + }; + + /* make our local services known to service pool */ + for (unsigned i = 0; i < sizeof(ls) / sizeof(Local_service); i++) + local_services.insert(&ls[i]); + + PDBG("--- start init ---"); + + /* obtain ROM session with init binary */ + Rom_session_capability init_rom_session_cap; + try { + static Rom_connection rom("init"); + init_rom_session_cap = rom.cap(); } + catch (...) { + PERR("ROM module \"init\" not present"); } + + /* create ram session for init and transfer some of our own quota */ + Ram_session_capability init_ram_session_cap + = static_cap_cast(ram_root.session("ram_quota=16K")); + Ram_session_client(init_ram_session_cap).ref_account(env()->ram_session_cap()); + + Cpu_connection init_cpu; + Rm_connection init_rm; + + /* transfer all left memory to init, but leave some memory left for core */ + /* NOTE: exception objects thrown in core components are currently allocated on + core's heap and not accounted by the component's meta data allocator */ + size_t init_quota = platform()->ram_alloc()->avail() - 72*1024; + env()->ram_session()->transfer_quota(init_ram_session_cap, init_quota); + PDBG("transferred %zd MB to init", init_quota / (1024*1024)); + + Core_child *init = new (env()->heap()) + Core_child(Rom_session_client(init_rom_session_cap).dataspace(), + core_env()->cap_session(), init_ram_session_cap, + init_cpu.cap(), init_rm.cap(), &local_services); + + PDBG("--- init created, waiting for exit condition ---"); + platform()->wait_for_exit(); + + PDBG("--- destroying init ---"); + destroy(env()->heap(), init); + + rom_root.close(init_rom_session_cap); + ram_root.close(init_ram_session_cap); + + PDBG("--- core main says good bye ---"); + + return 0; +} diff --git a/base/src/core/mb_info.h b/base/src/core/mb_info.h new file mode 100644 index 000000000..7aff03a6b --- /dev/null +++ b/base/src/core/mb_info.h @@ -0,0 +1,175 @@ +/** + * \brief Multiboot info structure as defined by GRUB + * \author Christian Helmuth + * \date 2006-05-09 + * + * This is a stripped down version. Original code in + * l4/pkg/l4util/include/ARCH-x86/mb_info.h. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _MB_INFO_H_ +#define _MB_INFO_H_ + +#include + +/** + * Multi-boot module + */ +typedef struct +{ + uint32_t mod_start; /* starting address of module in memory. */ + uint32_t mod_end; /* end address of module in memory. */ + uint32_t cmdline; /* module command line */ + uint32_t pad; /* padding to take it to 16 bytes */ +} mb_mod_t; + + +/** VBE controller information. */ +typedef struct +{ + uint8_t signature[4]; + uint16_t version; + uint32_t oem_string; + uint32_t capabilities; + uint32_t video_mode; + uint16_t total_memory; + uint16_t oem_software_rev; + uint32_t oem_vendor_name; + uint32_t oem_product_name; + uint32_t oem_product_rev; + uint8_t reserved[222]; + uint8_t oem_data[256]; +} __attribute__((packed)) mb_vbe_ctrl_t; + + +/** VBE mode information. */ +typedef struct +{ + /* all VESA versions */ + uint16_t mode_attributes; + uint8_t win_a_attributes; + uint8_t win_b_attributes; + uint16_t win_granularity; + uint16_t win_size; + uint16_t win_a_segment; + uint16_t win_b_segment; + uint32_t win_func; + uint16_t bytes_per_scanline; + + /* >= VESA version 1.2 */ + uint16_t x_resolution; + uint16_t y_resolution; + uint8_t x_char_size; + uint8_t y_char_size; + uint8_t number_of_planes; + uint8_t bits_per_pixel; + uint8_t number_of_banks; + uint8_t memory_model; + uint8_t bank_size; + uint8_t number_of_image_pages; + uint8_t reserved0; + + /* direct color */ + uint8_t red_mask_size; + uint8_t red_field_position; + uint8_t green_mask_size; + uint8_t green_field_position; + uint8_t blue_mask_size; + uint8_t blue_field_position; + uint8_t reserved_mask_size; + uint8_t reserved_field_position; + uint8_t direct_color_mode_info; + + /* >= VESA version 2.0 */ + uint32_t phys_base; + uint32_t reserved1; + uint16_t reversed2; + + /* >= VESA version 3.0 */ + uint16_t linear_bytes_per_scanline; + uint8_t banked_number_of_image_pages; + uint8_t linear_number_of_image_pages; + uint8_t linear_red_mask_size; + uint8_t linear_red_field_position; + uint8_t linear_green_mask_size; + uint8_t linear_green_field_position; + uint8_t linear_blue_mask_size; + uint8_t linear_blue_field_position; + uint8_t linear_reserved_mask_size; + uint8_t linear_reserved_field_position; + uint32_t max_pixel_clock; + + uint8_t reserved3[189]; +} __attribute__ ((packed)) mb_vbe_mode_t; + + +/** + * Multi-boot information + */ +typedef struct +{ + uint32_t flags; /* MultiBoot info version number */ + uint32_t mem_lower; /* available memory below 1MB */ + uint32_t mem_upper; /* available memory starting from 1MB [kB] */ + uint32_t boot_device; /* "root" partition */ + uint32_t cmdline; /* Kernel command line */ + uint32_t mods_count; /* number of modules */ + uint32_t mods_addr; /* module list */ + + union + { + struct + { + /* (a.out) Kernel symbol table info */ + uint32_t tabsize; + uint32_t strsize; + uint32_t addr; + uint32_t pad; + } a; + + struct + { + /* (ELF) Kernel section header table */ + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; + } e; + } syms; + + uint32_t mmap_length; /* size of memory mapping buffer */ + uint32_t mmap_addr; /* address of memory mapping buffer */ + uint32_t drives_length; /* size of drive info buffer */ + uint32_t drives_addr; /* address of driver info buffer */ + uint32_t config_table; /* ROM configuration table */ + uint32_t boot_loader_name; /* Boot Loader Name */ + uint32_t apm_table; /* APM table */ + uint32_t vbe_ctrl_info; /* VESA video contoller info */ + uint32_t vbe_mode_info; /* VESA video mode info */ + uint16_t vbe_mode; /* VESA video mode number */ + uint16_t vbe_interface_seg; /* VESA segment of prot BIOS interface */ + uint16_t vbe_interface_off; /* VESA offset of prot BIOS interface */ + uint16_t vbe_interface_len; /* VESA lenght of prot BIOS interface */ +} mb_info_t; + +/** + * Flags to be set in the 'flags' parameter above + */ + +/** is the command-line defined? */ +#define MB_CMDLINE 0x00000004 + +/** Is there video information? */ +#define MB_VIDEO_INFO 0x00000800 + +/** If we are multiboot-compliant, this value is present in the eax register */ +#define MB_VALID 0x2BADB002 + +#endif diff --git a/base/src/core/multiboot_info.cc b/base/src/core/multiboot_info.cc new file mode 100644 index 000000000..024e9c7d9 --- /dev/null +++ b/base/src/core/multiboot_info.cc @@ -0,0 +1,143 @@ +/** + * \brief GRUB multi-boot information handling + * \author Christian Helmuth + * \date 2006-05-10 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +using namespace Genode; + +namespace Mb_info { +#include "mb_info.h" +} + + +static const bool verbose = false; + + +void Multiboot_info::print_debug() +{ + Mb_info::mb_info_t *mbi = (Mb_info::mb_info_t *)_mb_info; + + printf(" flags = %x %s\n", mbi->flags, + mbi->flags & MB_CMDLINE ? "CMDLINE" : ""); + printf(" mem_lower = %xu\n", mbi->mem_lower); + printf(" mem_upper = %xu\n", mbi->mem_upper); + printf(" boot_device = %x\n", mbi->boot_device); +// printf(" cmdline = %08p \"%s\"\n", mbi->cmdline, (char *)mbi->cmdline); + printf(" mods_count = %d\n", mbi->mods_count); + printf(" mods_addr = %xu\n", mbi->mods_addr); + + unsigned i = 0; + Mb_info::mb_mod_t *mods = reinterpret_cast(mbi->mods_addr); + for (i = 0; i < mbi->mods_count; i++) + printf(" mod[%02d] [%xu,%xu) %s\n", i, + mods[i].mod_start, (mods[i].mod_end), + reinterpret_cast(mods[i].cmdline)); + + printf(" mmap_length = %x\n", mbi->mmap_length); + printf(" mmap_addr = %x\n", mbi->mmap_addr); + printf(" drives_length = %x\n", mbi->drives_length); + printf(" drives_addr = %x\n", mbi->drives_addr); + printf(" config_table = %x\n", mbi->config_table); + printf(" boot_loader_name = %x\n", mbi->boot_loader_name); + printf(" apm_table = %x\n", mbi->apm_table); + printf(" vbe_ctrl_info = %xu\n", mbi->vbe_ctrl_info); + printf(" vbe_mode_info = %xu\n", mbi->vbe_mode_info); + printf(" vbe_mode = %x\n", mbi->vbe_mode); + printf(" vbe_interface_seg = %x\n", mbi->vbe_interface_seg); + printf(" vbe_interface_off = %x\n", mbi->vbe_interface_off); + printf(" vbe_interface_len = %x\n", mbi->vbe_interface_len); +} + + +unsigned Multiboot_info::num_modules() +{ + Mb_info::mb_info_t *mbi = (Mb_info::mb_info_t *)_mb_info; + + return mbi->mods_count; +} + + +Rom_module Multiboot_info::get_module(unsigned num) +{ + Mb_info::mb_info_t *mbi = reinterpret_cast(_mb_info); + Mb_info::mb_mod_t *mods = reinterpret_cast(mbi->mods_addr); + + /* num exceeds number of modules */ + if (!(num < mbi->mods_count)) return Rom_module(); + + /* invalid module -- maybe returned earlier */ + if (!mods[num].cmdline) return Rom_module(); + + char *cmdline = reinterpret_cast(mods[num].cmdline); + + /* skip everything in front of the base name of the file */ + for (unsigned i = 0; cmdline[i]; i++) { + + if (cmdline[i] != '/') continue; + + /* + * If we detect the end of a directory name, take the + * next character as the start of the command line + */ + cmdline = cmdline + i + 1; + i = 0; + } + + Rom_module ret = Rom_module(mods[num].mod_start, + mods[num].mod_end - mods[num].mod_start, + cmdline); + + /* mark module as invalid */ + mods[num].cmdline = 0; + + return ret; +} + + +bool Multiboot_info::check_module(unsigned num, addr_t *start, addr_t *end) +{ + Mb_info::mb_info_t *mbi = reinterpret_cast(_mb_info); + Mb_info::mb_mod_t *mods = reinterpret_cast(mbi->mods_addr); + + /* num exceeds number of modules */ + if (!(num < mbi->mods_count)) return false; + + *start = mods[num].mod_start; + *end = mods[num].mod_end; + + return true; +} + +/** + * Constructor + */ +Multiboot_info::Multiboot_info(void *mb_info) +: _mb_info(mb_info) +{ + Mb_info::mb_info_t *mbi = reinterpret_cast(_mb_info); + Mb_info::mb_mod_t *mods = reinterpret_cast(mbi->mods_addr); + + /* strip path and arguments from module name */ + for (unsigned i = 0; i < mbi->mods_count; i++) { + char *cmdline = reinterpret_cast(mods[i].cmdline); + mods[i].cmdline = (addr_t)commandline_to_basename(cmdline); + } + + if (verbose) { + printf("Multi-boot info with %d modules @ %p.\n", + mbi->mods_count, _mb_info); + print_debug(); + } +} diff --git a/base/src/core/pd_session_component.cc b/base/src/core/pd_session_component.cc new file mode 100644 index 000000000..881b56a70 --- /dev/null +++ b/base/src/core/pd_session_component.cc @@ -0,0 +1,50 @@ +/* + * \brief Core implementation of the PD session interface + * \author Christian Helmuth + * \date 2006-07-17 + * + * FIXME arg_string and quota missing + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode */ +#include + +/* Core */ +#include +#include +#include + +using namespace Genode; + + +int Pd_session_component::bind_thread(Thread_capability thread) +{ + Cpu_thread_component *cpu_thread = dynamic_cast + (_thread_ep->obj_by_cap(thread)); + if (!cpu_thread) return -1; + + if (cpu_thread->bound()) { + PWRN("rebinding of threads not supported"); + return -2; + } + + Platform_thread *p_thread = cpu_thread->platform_thread(); + + _pd.bind_thread(p_thread); + cpu_thread->bound(true); + + return 0; +} + + +int Pd_session_component::assign_parent(Parent_capability parent) +{ + return _pd.assign_parent(parent); +} diff --git a/base/src/core/ram_session_component.cc b/base/src/core/ram_session_component.cc new file mode 100644 index 000000000..e5b0402ce --- /dev/null +++ b/base/src/core/ram_session_component.cc @@ -0,0 +1,275 @@ +/* + * \brief Core implementation of the RAM session interface + * \author Norman Feske + * \date 2006-05-19 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +using namespace Genode; + + +static const bool verbose = false; + + +void Ram_session_component::_free_ds(Dataspace_component *ds) +{ + if (!ds) return; + + size_t ds_size = ds->size(); + + /* destroy native shared memory representation */ + _revoke_ram_ds(ds); + + /* tell entry point to forget the dataspace */ + _ds_ep->dissolve(ds); + + /* XXX: remove dataspace from all RM sessions */ + + /* free physical memory that was backing the dataspace */ + _ram_alloc->free((void *)ds->phys_addr(), ds_size); + + /* call dataspace destructors and free memory */ + destroy(&_ds_slab, ds); + + /* adjust payload */ + _payload -= ds_size; +} + + +int Ram_session_component::_transfer_quota(Ram_session_component *dst, size_t amount) +{ + /* check if recipient is a valid Ram_session_component */ + if (!dst) return -1; + + /* check for reference account relationship */ + if ((ref_account() != dst) && (dst->ref_account() != this)) + return -3; + + /* decrease quota limit of this session - check against used quota */ + if (_quota_limit < amount + _payload) { + PWRN("Insufficient quota for transfer: %s", _label); + PWRN(" have %zd, need %zd", _quota_limit - _payload, amount); + return -3; + } + + _quota_limit -= amount; + + /* increase quota_limit of recipient */ + dst->_quota_limit += amount; + + return 0; +} + + +void Ram_session_component::_register_ref_account_member(Ram_session_component *new_member) +{ + Lock::Guard lock_guard(_ref_members_lock); + _ref_members.insert(new_member); + new_member->_ref_account = this; +} + + +void Ram_session_component::_unsynchronized_remove_ref_account_member(Ram_session_component *member) +{ + member->_ref_account = 0; + _ref_members.remove(member); +} + + +void Ram_session_component::_remove_ref_account_member(Ram_session_component *member) +{ + Lock::Guard lock_guard(_ref_members_lock); + _unsynchronized_remove_ref_account_member(member); +} + + +Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size) +{ + /* zero-sized dataspaces are not allowed */ + if (!ds_size) return Ram_dataspace_capability(); + + /* dataspace allocation granularity is page size */ + ds_size = align_addr(ds_size, 12); + + /* + * Check quota! + * + * In the worst case, we need to allocate a new slab block for the + * meta data of the dataspace to be created - therefore, we add + * the slab block size here. + */ + if (used_quota() + SBS + ds_size >= _quota_limit) { + + PWRN("Quota exceeded: %s", _label); + PWRN(" memory for slab: %zd", _ds_slab.consumed()); + PWRN(" used quota: %zd", used_quota()); + PWRN(" ds_size: %zd", ds_size); + PWRN(" sizeof(Ram_session_component): %zd", sizeof(Ram_session_component)); + PWRN(" quota_limit: %zd", _quota_limit); + + throw Quota_exceeded(); + } + + /* + * Allocate physical backing store + * + * As an optimization for the use of large mapping sizes, we try to + * align the dataspace in physical memory naturally (size-aligned). + * If this does not work, we subsequently weaken the alignment constraint + * until the allocation succeeds. + */ + void *ds_addr = 0; + bool alloc_succeeded = false; + for (size_t align_log2 = log2(ds_size); align_log2 >= 12; align_log2--) { + if (_ram_alloc->alloc_aligned(ds_size, &ds_addr, align_log2)) { + alloc_succeeded = true; + break; + } + } + + /* + * Normally, init's quota equals the size of physical memory and this quota + * is distributed among the processes. As we check the quota before + * allocating, the allocation should always succeed in theory. However, + * fragmentation could cause a failing allocation. + */ + if (!alloc_succeeded) { + PERR("We ran out of physical memory while allocating %zd bytes", ds_size); + throw Quota_exceeded(); + } + + Dataspace_component *ds; + try { + ds = new (&_ds_slab) Dataspace_component(ds_size, (addr_t)ds_addr, true); + } catch (Allocator::Out_of_memory) { + PWRN("Could not allocate metadata"); + throw Out_of_metadata(); + } + + /* fill new dataspaces with zeros */ + _clear_ds(ds); + + /* keep track of the used quota for actual payload */ + _payload += ds_size; + + if (verbose) + PDBG("ds_size=%zd, used_quota=%zd quota_limit=%zd", + ds_size, used_quota(), _quota_limit); + + Dataspace_capability result = _ds_ep->manage(ds); + + /* create native shared memory representation of dataspace */ + _export_ram_ds(ds); + + return static_cap_cast(result); +} + + +void Ram_session_component::free(Ram_dataspace_capability ds_cap) +{ + _free_ds(dynamic_cast(_ds_ep->obj_by_cap(ds_cap))); +} + + +int Ram_session_component::ref_account(Ram_session_capability ram_session_cap) +{ + /* the reference account cannot be defined twice */ + if (_ref_account) return -2; + + Ram_session_component *ref = dynamic_cast + (_ram_session_ep->obj_by_cap(ram_session_cap)); + + /* check if recipient is a valid Ram_session_component */ + if (!ref) return -1; + + /* deny the usage of the ram session as its own ref account */ + /* XXX also check for cycles along the tree of ref accounts */ + if (ref == this) return -3; + + _ref_account = ref; + _ref_account->_register_ref_account_member(this); + return 0; +} + + +int Ram_session_component::transfer_quota(Ram_session_capability ram_session_cap, + size_t amount) +{ + if (verbose) + PDBG("amount=%zd", amount); + Ram_session_component *dst = dynamic_cast + (_ram_session_ep->obj_by_cap(ram_session_cap)); + + return _transfer_quota(dst, amount); +} + + +Ram_session_component::Ram_session_component(Rpc_entrypoint *ds_ep, + Rpc_entrypoint *ram_session_ep, + Range_allocator *ram_alloc, + Allocator *md_alloc, + const char *args, + size_t quota_limit) +: + _ds_ep(ds_ep), _ram_session_ep(ram_session_ep), _ram_alloc(ram_alloc), + _quota_limit(quota_limit), _payload(0), + _md_alloc(md_alloc, Arg_string::find_arg(args, "ram_quota").long_value(0)), + _ds_slab(&_md_alloc), _ref_account(0) +{ + Arg_string::find_arg(args, "label").string(_label, sizeof(_label), ""); +} + + +Ram_session_component::~Ram_session_component() +{ + /* destroy all dataspaces */ + for (Dataspace_component *ds; (ds = _ds_slab.first_object()); _free_ds(ds)); + + if (_payload != 0) + PWRN("Remaining payload of %zd in ram session to destroy", _payload); + + if (!_ref_account) return; + + /* transfer remaining quota to reference account */ + _transfer_quota(_ref_account, _quota_limit); + + /* remember our original reference account */ + Ram_session_component *orig_ref_account = _ref_account; + + /* remove reference to us from the reference account */ + _ref_account->_remove_ref_account_member(this); + + /* + * Now, the '_ref_account' member has become invalid. + */ + + Lock::Guard lock_guard(_ref_members_lock); + + /* assign all sub accounts to our original reference account */ + for (Ram_session_component *rsc; (rsc = _ref_members.first()); ) { + + _unsynchronized_remove_ref_account_member(rsc); + + /* + * This function grabs the '_ref_account_lock' of the '_ref_account', + * which is never identical to ourself. Hence, deadlock cannot happen + * here. + */ + orig_ref_account->_register_ref_account_member(rsc); + } + + _ref_account = 0; +} diff --git a/base/src/core/rm_session_component.cc b/base/src/core/rm_session_component.cc new file mode 100644 index 000000000..ec87a5a9a --- /dev/null +++ b/base/src/core/rm_session_component.cc @@ -0,0 +1,725 @@ +/* + * \brief Implementation of the RM session interface + * \author Christian Helmuth + * \author Norman Feske + * \date 2006-07-17 + * + * FIXME arg_string and quota missing + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* core includes */ +#include +#include +#include +#include + +using namespace Genode; + + +static const bool verbose = false; +static const bool verbose_page_faults = false; + + +namespace Genode { + + struct Rm_session_component::Fault_area + { + addr_t _fault_addr; + addr_t _base; + size_t _size_log2; + + addr_t _upper_bound() const { + return (_size_log2 == ~0UL) ? ~0 : (_base + (1 << _size_log2) - 1); } + + + /** + * Default constructor, constructs invalid fault area + */ + Fault_area() : _size_log2(0) { } + + /** + * Constructor, fault area spans the maximum address-space size + */ + Fault_area(addr_t fault_addr) : + _fault_addr(fault_addr), _base(0), _size_log2(~0) { } + + /** + * Constrain fault area to specified region + */ + void constrain(addr_t region_base, size_t region_size) + { + /* + * Find flexpage around _fault_addr that lies within the + * specified region. + * + * Start with a 'size_log2' of one less than the minimal + * page size. If the specified constraint conflicts with + * the existing fault area, the loop breaks at the first + * iteration and we can check for this condition after the + * loop. + */ + size_t size_log2 = get_page_size_log2() - 1; + addr_t base = 0; + for (size_t try_size_log2 = get_page_size_log2(); + try_size_log2 < sizeof(addr_t)*8 ; try_size_log2++) { + addr_t fpage_mask = ~((1 << try_size_log2) - 1); + addr_t try_base = _fault_addr & fpage_mask; + + /* check lower bound of existing fault area */ + if (try_base < _base) + break; + + /* check against upper bound of existing fault area */ + if (try_base + (1UL << try_size_log2) - 1 > _upper_bound()) + break; + + /* check against lower bound of region */ + if (try_base < region_base) + break; + + /* check against upper bound of region */ + if (try_base + (1 << try_size_log2) - 1 > region_base + region_size - 1) + break; + + /* flexpage is compatible with fault area, use it */ + size_log2 = try_size_log2; + base = try_base; + } + + /* if constraint is compatible with the fault area, invalidate */ + if (size_log2 < get_page_size_log2()) { + _size_log2 = 0; + _base = 0; + } else { + _size_log2 = size_log2; + _base = base; + } + } + + /** + * Constrain fault area to specified flexpage size + */ + void constrain(size_t size_log2) + { + if (size_log2 >= _size_log2) + return; + + _base = _fault_addr & ~((1 << size_log2) - 1); + _size_log2 = size_log2; + } + + /** + * Determine common flexpage size compatible with specified fault areas + */ + static size_t common_size_log2(Fault_area const &a1, Fault_area const &a2) + { + /* + * We have to make sure that the offset of page-fault address + * relative to the flexpage base is the same for both fault areas. + * This condition is met by the flexpage size equal to the number + * of common least-significant bits of both offsets. + */ + size_t const diff = (a1.fault_addr() - a1.base()) + ^ (a2.fault_addr() - a2.base()); + + /* + * Find highest clear bit in 'diff', starting from the least + * significant candidate. We can skip all bits lower then + * 'get_page_size_log2()' because they are not relevant as + * flexpage size (and are always zero). + */ + size_t n = get_page_size_log2(); + size_t const min_size_log2 = min(a1._size_log2, a2._size_log2); + for (; n < min_size_log2 && !(diff & (1 << n)); n++); + + return n; + } + + addr_t fault_addr() const { return _fault_addr; } + addr_t base() const { return _base; } + bool valid() const { return _size_log2 > 0; } + }; +} + + +/*************************** + ** Region-manager client ** + ***************************/ + +/* + * This code is executed by the page-fault handler thread. + */ + +int Rm_client::pager(Ipc_pager &pager) +{ + Rm_session::Fault_type pf_type = pager.is_write_fault() ? Rm_session::WRITE_FAULT + : Rm_session::READ_FAULT; + addr_t pf_addr = pager.fault_addr(); + addr_t pf_ip = pager.fault_ip(); + + if (verbose_page_faults) + print_page_fault("page fault", pf_addr, pf_ip, pf_type, badge()); + + Rm_session_component *curr_rm_session = member_rm_session(); + addr_t curr_rm_base = 0; + Dataspace_component *src_dataspace = 0; + Rm_session_component::Fault_area src_fault_area; + Rm_session_component::Fault_area dst_fault_area(pf_addr); + bool lookup; + + /* traverse potentially nested dataspaces until we hit a leaf dataspace */ + unsigned level; + enum { MAX_NESTING_LEVELS = 5 }; + for (level = 0; level < MAX_NESTING_LEVELS; level++) { + + lookup = curr_rm_session->reverse_lookup(curr_rm_base, + &dst_fault_area, + &src_dataspace, + &src_fault_area); + if (!lookup) + break; + + /* check if we need to traverse into a nested dataspace */ + Rm_session_component *sub_rm_session = src_dataspace->sub_rm_session(); + if (!sub_rm_session) + break; + + /* set up next iteration */ + + curr_rm_base = dst_fault_area.fault_addr() + - src_fault_area.fault_addr() + src_dataspace->map_src_addr(); + curr_rm_session = sub_rm_session; + } + + if (level == MAX_NESTING_LEVELS) { + PWRN("Too many nesting levels of managed dataspaces"); + return -1; + } + + if (!lookup) { + + /* + * We found no attachment at the page-fault address and therefore have + * to reflect the page fault as region-manager fault. The signal + * handler is then expected to request the state of the region-manager + * session. + */ + + /* print a warning if it's no managed-dataspace */ + if (curr_rm_session == member_rm_session()) + print_page_fault("no RM attachment", pf_addr, pf_ip, pf_type, badge()); + + /* register fault at responsible region-manager session */ + curr_rm_session->fault(this, dst_fault_area.fault_addr() - curr_rm_base, pf_type); + /* there is no attachment return an error condition */ + return 1; + } + + /* + * Determine mapping size compatible with source and destination, + * and apply platform-specific constraint of mapping sizes. + */ + size_t map_size_log2 = dst_fault_area.common_size_log2(dst_fault_area, + src_fault_area); + map_size_log2 = constrain_map_size_log2(map_size_log2); + + src_fault_area.constrain(map_size_log2); + dst_fault_area.constrain(map_size_log2); + + /* + * Check if dataspace is compatible with page-fault type + */ + if (pf_type == Rm_session::WRITE_FAULT && !src_dataspace->writable()) { + + /* attempted there is no attachment return an error condition */ + print_page_fault("attempted write at read-only memory", + pf_addr, pf_ip, pf_type, badge()); + + /* register fault at responsible region-manager session */ + curr_rm_session->fault(this, src_fault_area.fault_addr(), pf_type); + return 2; + } + + Mapping mapping(dst_fault_area.base(), + src_fault_area.base(), + src_dataspace->write_combined(), + map_size_log2, + src_dataspace->writable()); + + /* + * On kernels with a mapping database, the 'dsc' dataspace is a leaf + * dataspace that corresponds to a virtual address range within core. To + * prepare the answer for the page fault, we make sure that this range is + * locally mapped in core. On platforms that support map operations of + * pages that are not locally mapped, the 'map_core_local' function may be + * empty. + */ + if (!src_dataspace->is_io_mem()) + mapping.prepare_map_operation(); + + /* answer page fault with a flex-page mapping */ + pager.set_reply_mapping(mapping); + return 0; +} + + +/************* + ** Faulter ** + *************/ + +void Rm_faulter::fault(Rm_session_component *faulting_rm_session, + Rm_session::State fault_state) +{ + Lock::Guard lock_guard(_lock); + + _faulting_rm_session = faulting_rm_session; + _fault_state = fault_state; +} + + +void Rm_faulter::dissolve_from_faulting_rm_session() +{ + Lock::Guard lock_guard(_lock); + + if (_faulting_rm_session) + _faulting_rm_session->discard_faulter(this); + + _faulting_rm_session = 0; +} + + +void Rm_faulter::continue_after_resolved_fault() +{ + Lock::Guard lock_guard(_lock); + + _pager_object->wake_up(); + _faulting_rm_session = 0; + _fault_state = Rm_session::State(); +} + + +/************************************** + ** Region-manager-session component ** + **************************************/ + +Rm_session::Local_addr +Rm_session_component::attach(Dataspace_capability ds_cap, size_t size, + off_t offset, bool use_local_addr, + Rm_session::Local_addr local_addr) +{ + /* serialize access */ + Lock::Guard lock_guard(_lock); + + /* offset must be positive and page-aligned */ + if (offset < 0 + || align_addr(offset, get_page_size_log2()) != (size_t)offset) + throw Invalid_args(); + + /* check dataspace validity */ + Dataspace_component *dsc = dynamic_cast + (_ds_ep->obj_by_cap(ds_cap)); + if (!dsc) throw Invalid_dataspace(); + + if (!size) { + size = dsc->size() - offset; + + if (dsc->size() <= (size_t)offset) { + PWRN("size is 0"); + throw Invalid_dataspace(); + } + } + + /* work with page granularity */ + size = align_addr(size, get_page_size_log2()); + + /* allocate region for attachment */ + void *r = 0; + if (use_local_addr) { + switch (_map.alloc_addr(size, local_addr)) { + + case Range_allocator::OUT_OF_METADATA: + throw Out_of_metadata(); + + case Range_allocator::RANGE_CONFLICT: + throw Region_conflict(); + + case Range_allocator::ALLOC_OK: + r = local_addr; + break; + } + } else { + + /* + * Find optimal alignment for new region. First try natural alignment. + * If that is not possible, try again with successively less alignment + * constraints. + */ + size_t align_log2 = log2(size); + for (; align_log2 >= get_page_size_log2(); align_log2--) { + + /* + * Don't use an aligment higher than the alignment of the backing + * store. The backing store would constrain the mapping size + * anyway such that a higher alignment of the region is of no use. + */ + if (((dsc->map_src_addr() + offset) & ((1 << align_log2) - 1)) != 0) + continue; + + /* try allocating the align region */ + if (_map.alloc_aligned(size, &r, align_log2)) + break; + } + + if (align_log2 < get_page_size_log2()) { + _map.free(r); + throw Region_conflict(); + } + } + + /* store attachment info in meta data */ + _map.metadata(r, Rm_region((addr_t)r, size, true, dsc, offset, this)); + Rm_region *region = _map.metadata(r); + + /* also update region list */ + Rm_region_ref *p; + try { p = new(&_ref_slab) Rm_region_ref(region); } + catch (Allocator::Out_of_memory) { + _map.free(r); + throw Out_of_metadata(); + } + + _regions.insert(p); + + /* inform dataspace about attachment */ + dsc->attached_to(region); + + if (verbose) + PDBG("attach ds %p (a=%lx,s=%zx,o=%lx) @ [%lx,%lx)", + dsc, dsc->phys_addr(), dsc->size(), offset, (addr_t)r, (addr_t)r + size); + + /* check if attach operation resolves any faulting region-manager clients */ + for (Rm_faulter *faulter = _faulters.first(); faulter; ) { + + /* remeber next pointer before possibly removing current list element */ + Rm_faulter *next = faulter->next(); + + if (faulter->fault_in_addr_range((addr_t)r, size)) { + _faulters.remove(faulter); + faulter->continue_after_resolved_fault(); + } + + faulter = next; + } + + return r; +} + + +static void unmap_managed(Rm_session_component *session, Rm_region *region, int level) +{ + for (Rm_region *managed = session->dataspace_component()->regions()->first(); + managed; + managed = managed->List::Element::next()) { + + if (verbose) + PDBG("(%d: %p) a=%lx,s=%lx,off=%lx ra=%lx,s=%lx,off=%lx sub-session %p", + level, session, managed->base(), (long)managed->size(), managed->offset(), + region->base(), (long)region->size(), region->offset(), managed->session()); + + if (managed->base() - managed->offset() >= region->base() - region->offset() + && managed->base() - managed->offset() + managed->size() + <= region->base() - region->offset() + region->size()) + unmap_managed(managed->session(), managed, level + 1); + + /* Found a leaf node (here a leaf is an Rm_session whose dataspace has no regions) */ + if (!managed->session()->dataspace_component()->regions()->first()) + for (Rm_client *rc = managed->session()->clients()->first(); + rc; rc = rc->List::Element::next()) + rc->unmap(region->dataspace()->core_local_addr() + region->offset(), + managed->base() + region->base() - managed->offset(), region->size()); + } +} + + +void Rm_session_component::detach(Local_addr local_addr) +{ + /* serialize access */ + Lock::Guard lock_guard(_lock); + + /* read meta data for address */ + Rm_region *region = _map.metadata(local_addr); + + if (!region) { + PDBG("no attachment at %p", (void *)local_addr); + return; + } + + Dataspace_component *dsc = region->dataspace(); + if (!dsc) + PWRN("Rm_region of %p may be inconsistent!", this); + + if (verbose) + PDBG("detach ds %p (a=%lx,s=%zx,o=%lx) at [%lx,%lx)", + dsc, dsc->phys_addr(), dsc->size(), region->offset(), + region->base(), region->base() + region->size()); + + /* inform dataspace about detachment */ + dsc->detached_from(region); + + /* + * Deallocate region on platforms that support unmap + * + * On platforms without support for unmap (in particular NOVA 0.1), the + * same virtual address must not be reused. Hence, we never mark used + * regions as free. + * + * We unregister the region from region map prior unmapping the pages to + * make sure that page faults occurring immediately after the unmap + * refer to an empty region not to the dataspace, which we just removed. + */ + if (platform()->supports_unmap()) + _map.free(local_addr); + + /* + * This function gets called from the destructor of 'Dataspace_component', + * which iterates through all regions the dataspace is attached to. One + * particular case is the destruction of an 'Rm_session_component' and its + * contained managed dataspace ('_ds') member. The type of this member is + * derived from 'Dataspace_component' and provides the 'sub_rm_session' + * function, which can normally be used to distinguish managed dataspaces + * from leaf dataspaces. However, at destruction time of the '_dsc' base + * class, the vtable entry of 'sub_rm_session' already points to the + * base-class's function. Hence, we cannot check the return value of this + * function to determine if the dataspace is a managed dataspace. Instead, + * we introduced a dataspace member '_managed' with the non-virtual accessor + * function 'is_managed'. + */ + + /* + * Go through all RM clients using the RM session. For each RM client, we + * need to unmap the referred region from its virtual address space. + */ + for (Rm_client *rc = _clients.first(); rc; rc = rc->List::Element::next()) { + + /* + * XXX Unmapping managed dataspaces on kernels, which take a core- + * local virtual address as unmap argument is not supported yet. + * This is the case for Fiasco, Pistachio, and NOVA. On those + * kernels, the unmap operation must be issued for each leaf + * dataspace the managed dataspace is composed of. For kernels with + * support for directed unmap (OKL4 or Codezero), unmap can be + * simply applied for the contiguous virtual address region in the + * client. + */ + if (!platform()->supports_direct_unmap() + && dsc->is_managed() && dsc->core_local_addr() == 0) { + PWRN("unmapping of managed dataspaces not yet supported"); + break; + } + + rc->unmap(dsc->core_local_addr() + region->offset(), + region->base(), region->size()); + } + + /* + * If RM session is used as nested dataspace, unmap this + * dataspace from all RM sessions. + */ + unmap_managed(this, region, 1); + + /* update region list */ + Rm_region_ref *p = _regions.first(); + for (; p; p = p->next()) + if (p->region() == region) break; + + if (p) { + _regions.remove(p); + destroy(&_ref_slab, p); + } +} + + +Pager_capability Rm_session_component::add_client(Thread_capability thread) +{ + /* serialize access */ + Lock::Guard lock_guard(_lock); + + /* lookup thread and setup correct parameters */ + Cpu_thread_component *cpu_thread = dynamic_cast + (_thread_ep->obj_by_cap(thread)); + if (!cpu_thread) throw Invalid_thread(); + + /* determine identification of client when faulting */ + unsigned long badge = cpu_thread->platform_thread()->pager_object_badge(); + + Rm_client *cl; + try { cl = new(&_client_slab) Rm_client(this, badge); } + catch (Allocator::Out_of_memory) { throw Out_of_memory(); } + + _clients.insert(cl); + + return Pager_capability(_pager_ep->manage(cl)); +} + + +bool Rm_session_component::reverse_lookup(addr_t dst_base, + Fault_area *dst_fault_area, + Dataspace_component **src_dataspace, + Fault_area *src_fault_area) +{ + /* serialize access */ + Lock::Guard lock_guard(_lock); + + /* rm-session-relative fault address */ + addr_t fault_addr = dst_fault_area->fault_addr() - dst_base; + + /* lookup region */ + Rm_region *region = _map.metadata((void*)fault_addr); + if (!region) + return false; + + /* request dataspace backing the region */ + *src_dataspace = region->dataspace(); + if (!*src_dataspace) + return false; + + /* constrain destination fault area to region */ + dst_fault_area->constrain(dst_base + region->base(), region->size()); + + /* calculate source fault address relative to 'src_dataspace' */ + addr_t src_fault_offset = fault_addr - region->base() + region->offset(); + + addr_t src_base = (*src_dataspace)->map_src_addr(); + *src_fault_area = Fault_area(src_base + src_fault_offset); + + /* constrain source fault area by the source dataspace dimensions */ + src_fault_area->constrain(src_base, (*src_dataspace)->size()); + + return src_fault_area->valid() && dst_fault_area->valid(); +} + + +void Rm_session_component::fault(Rm_faulter *faulter, addr_t pf_addr, + Rm_session::Fault_type pf_type) +{ + + /* serialize access */ + Lock::Guard lock_guard(_lock); + + /* remeber fault state in faulting thread */ + faulter->fault(this, Rm_session::State(pf_type, pf_addr)); + + /* enqueue faulter */ + _faulters.insert(faulter); + + /* issue fault signal */ + _fault_notifier.submit(); +} + + +void Rm_session_component::discard_faulter(Rm_faulter *faulter) +{ + /* serialize access */ + Lock::Guard lock_guard(_lock); + + _faulters.remove(faulter); +} + + +void Rm_session_component::fault_handler(Signal_context_capability handler) +{ + _fault_notifier.context(handler); +} + + +Rm_session::State Rm_session_component::state() +{ + /* serialize access */ + Lock::Guard lock_guard(_lock); + + /* pick one of the currently faulted threads */ + Rm_faulter *faulter = _faulters.first(); + + /* return ready state if there are not current faulters */ + if (!faulter) + return Rm_session::State(); + + /* return fault information regarding the first faulter of the list */ + return faulter->fault_state(); +} + + +void Rm_session_component::dissolve(Rm_client *cl) +{ + Lock::Guard lock_guard(_lock); + _pager_ep->dissolve(cl); + _clients.remove(cl); + destroy(&_client_slab, cl); +} + + +Rm_session_component::Rm_session_component(Rpc_entrypoint *ds_ep, + Rpc_entrypoint *thread_ep, + Allocator *md_alloc, + size_t ram_quota, + Pager_entrypoint *pager_ep, + addr_t vm_start, + size_t vm_size) +: + _ds_ep(ds_ep), _thread_ep(thread_ep), + _md_alloc(md_alloc, ram_quota), + _client_slab(&_md_alloc), _ref_slab(&_md_alloc), + _map(&_md_alloc), _pager_ep(pager_ep), + _ds(this, vm_size), _ds_cap(ds_ep->manage(&_ds)) +{ + /* configure managed VM area */ + _map.add_range(vm_start, vm_size); +} + + +Rm_session_component::~Rm_session_component() +{ + _lock.lock(); + + /* revoke dataspace representation */ + _ds_ep->dissolve(&_ds); + + /* remove all faulters with pending page faults at this rm session */ + while (Rm_faulter *faulter = _faulters.first()) { + _lock.unlock(); + faulter->dissolve_from_faulting_rm_session(); + _lock.lock(); + } + + /* remove all clients */ + while (Rm_client *cl = _client_slab.first_object()) { + _pager_ep->dissolve(cl); + _lock.unlock(); + cl->dissolve_from_faulting_rm_session(); + _lock.lock(); + _clients.remove(cl); + destroy(&_client_slab, cl); + } + + /* detach all regions */ + while (Rm_region_ref *r = _ref_slab.first_object()) { + _lock.unlock(); + detach((void *)r->region()->base()); + _lock.lock(); + } + + _lock.unlock(); +} diff --git a/base/src/core/rom_session_component.cc b/base/src/core/rom_session_component.cc new file mode 100644 index 000000000..13e17885d --- /dev/null +++ b/base/src/core/rom_session_component.cc @@ -0,0 +1,42 @@ +/* + * \brief Core implementation of the ROM session interface + * \author Norman Feske + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include + +using namespace Genode; + + +Rom_session_component::Rom_session_component(Rom_fs *rom_fs, + Rpc_entrypoint *ds_ep, + const char *args) +: + _rom_module(_find_rom(rom_fs, args)), + _ds(_rom_module ? _rom_module->size() : 0, + _rom_module ? _rom_module->addr() : 0, false), + _ds_ep(ds_ep) +{ + /* ROM module not found */ + if (!_rom_module) + throw Root::Invalid_args(); + + _ds_cap = static_cap_cast(_ds_ep->manage(&_ds)); +} + + +Rom_session_component::~Rom_session_component() +{ + _ds_ep->dissolve(&_ds); +} diff --git a/base/src/core/signal_session_component.cc b/base/src/core/signal_session_component.cc new file mode 100644 index 000000000..55c78e762 --- /dev/null +++ b/base/src/core/signal_session_component.cc @@ -0,0 +1,101 @@ +/* + * \brief Implementation of the SIGNAL interface + * \author Norman Feske + * \date 2009-08-11 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include + +using namespace Genode; + + +/****************************** + ** Signal-session component ** + ******************************/ + +Signal_session_component::Signal_session_component(Rpc_entrypoint *source_ep, + Rpc_entrypoint *context_ep, + Allocator *context_md_alloc, + size_t ram_quota) +: + _source_ep(source_ep), + _context_ep(context_ep), + _source(source_ep), + _source_cap(_source_ep->manage(&_source)), + _md_alloc(context_md_alloc, ram_quota), + _contexts_slab(&_md_alloc) +{ } + + +Signal_session_component::~Signal_session_component() +{ + /* free all signal contexts */ + while (Signal_context_component *r = _contexts_slab.first_object()) + free_context(r->cap()); + + /* remove _signal_source from entrypoint */ + _source_ep->dissolve(&_source); +} + + +Signal_source_capability Signal_session_component::signal_source() +{ + return _source_cap; +} + + +Signal_context_capability Signal_session_component::alloc_context(long imprint) +{ + Signal_context_component *context; + + try { context = new (&_contexts_slab) + Signal_context_component(imprint, &_source); } + + catch (Allocator::Out_of_memory) { throw Out_of_metadata(); } + + /* return unique capability for the signal context */ + return _context_ep->manage(context); +} + + +void Signal_session_component::free_context(Signal_context_capability context_cap) +{ + Signal_context_component *context; + context = dynamic_cast + (_context_ep->obj_by_cap(context_cap)); + + if (!context) { + PWRN("specified signal-context capability has wrong type"); + return; + } + + _context_ep->dissolve(context); + destroy(&_contexts_slab, context); +} + + +void Signal_session_component::submit(Signal_context_capability context_cap, + unsigned cnt) +{ + Signal_context_component *context; + context = dynamic_cast + (_context_ep->obj_by_cap(context_cap)); + + if (!context) { + PWRN("invalid signal-context capability"); + return; + } + + context->source()->submit(context, _ipc_ostream, cnt); +} diff --git a/base/src/core/signal_source_component.cc b/base/src/core/signal_source_component.cc new file mode 100644 index 000000000..29c6b700e --- /dev/null +++ b/base/src/core/signal_source_component.cc @@ -0,0 +1,84 @@ +/* + * \brief Implementation of the SIGNAL interface + * \author Norman Feske + * \date 2009-08-11 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +using namespace Genode; + + +/***************************** + ** Signal-source component ** + *****************************/ + +void Signal_source_component::submit(Signal_context_component *context, + Ipc_ostream *ostream, + int cnt) +{ + /* + * If the client is blocking at the signal source (indicated by + * the valid reply capability), we wake him up. + */ + if (_reply_cap.valid()) { + + *ostream << Signal(context->imprint(), context->cnt()); + _entrypoint->explicit_reply(_reply_cap, 0); + + /* + * We unblocked the client and, therefore, can invalidate + * the reply capability. + */ + _reply_cap = Untyped_capability(); + } + + /* + * If the client does not block in 'wait_for_signal', the + * signal will be delivered as result of the next + * 'wait_for_signal' call. + */ + context->increment_signal_cnt(cnt); + + if (!context->is_enqueued()) + _signal_queue.enqueue(context); +} + + +Signal_source::Signal Signal_source_component::wait_for_signal() +{ + /* keep client blocked */ + if (_signal_queue.empty()) { + + /* + * Keep reply capability for outstanding request to be used + * for the later call of 'explicit_reply()'. + */ + _reply_cap = _entrypoint->reply_dst(); + _entrypoint->omit_reply(); + return Signal(0, 0); /* just a dummy */ + } + + /* dequeue and return pending signal */ + Signal_context_component *context = _signal_queue.dequeue(); + Signal result(context->imprint(), context->cnt()); + context->reset_signal_cnt(); + return result; +} + + +Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) +: _entrypoint(ep) { } + diff --git a/base/src/core/x86/io_port_session_component.cc b/base/src/core/x86/io_port_session_component.cc new file mode 100644 index 000000000..4b879e19b --- /dev/null +++ b/base/src/core/x86/io_port_session_component.cc @@ -0,0 +1,138 @@ +/* + * \brief Core implementation of the IO_PORT session interface + * \author Christian Helmuth + * \date 2007-04-17 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +using namespace Genode; + + +static const bool verbose = false; + + +/************** + ** Port API ** + **************/ + +unsigned char Io_port_session_component::inb(unsigned short address) +{ + /* check boundaries */ + if (!_in_bounds(address, sizeof(unsigned char))) return 0; + + unsigned char v; + asm volatile ("inb %w1, %b0" : "=a" (v) : "Nd" (address)); + return v; +} + + +unsigned short Io_port_session_component::inw(unsigned short address) +{ + /* check boundaries */ + if (!_in_bounds(address, sizeof(unsigned short))) return 0; + + unsigned short v; + asm volatile ("inw %w1, %w0" : "=a" (v) : "Nd" (address)); + return v; +} + + +unsigned Io_port_session_component::inl(unsigned short address) +{ + /* check boundaries */ + if (!_in_bounds(address, sizeof(unsigned))) return 0; + + unsigned v; + asm volatile ("inl %w1, %0" : "=a" (v) : "Nd" (address)); + return v; +} + + +void Io_port_session_component::outb(unsigned short address, unsigned char value) +{ + /* check boundaries */ + if (!_in_bounds(address, sizeof(unsigned char))) return; + + asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (address)); +} + + +void Io_port_session_component::outw(unsigned short address, unsigned short value) +{ + /* check boundaries */ + if (!_in_bounds(address, sizeof(unsigned short))) return; + + asm volatile ("outw %w0, %w1" : : "a" (value), "Nd" (address)); +} + + +void Io_port_session_component::outl(unsigned short address, unsigned value) +{ + /* check boundaries */ + if (!_in_bounds(address, sizeof(unsigned))) return; + + asm volatile ("outl %0, %w1" : : "a" (value), "Nd" (address)); +} + + +/****************************** + ** Constructor / destructor ** + ******************************/ + +Io_port_session_component::Io_port_session_component(Range_allocator *io_port_alloc, + const char *args) +: _io_port_alloc(io_port_alloc) +{ + /* parse for port properties */ + unsigned base = Arg_string::find_arg(args, "io_port_base").ulong_value(0); + unsigned size = Arg_string::find_arg(args, "io_port_size").ulong_value(0); + + /* allocate region (also checks out-of-bounds regions) */ + switch (io_port_alloc->alloc_addr(size, base)) { + + case Range_allocator::RANGE_CONFLICT: + PERR("I/O port [%x,%x) not available", base, base + size); + throw Root::Invalid_args(); + + case Range_allocator::OUT_OF_METADATA: + PERR("I/O port allocator ran out of meta data"); + + /* + * Do not throw 'Quota_exceeded' because the client cannot do + * anything about the meta data allocator of I/O ports. + */ + throw Root::Invalid_args(); + + case Range_allocator::ALLOC_OK: break; + } + + if (verbose) + PDBG("I/O port: [%04x,%04x)", base, base + size); + + /* store information */ + _base = base; + _size = size; +} + + +Io_port_session_component::~Io_port_session_component() +{ + if (verbose) + PDBG("I/O port: [%04x,%04x)", _base, _base + _size); + + _io_port_alloc->free(reinterpret_cast(_base)); +} diff --git a/base/src/platform/_main.cc b/base/src/platform/_main.cc new file mode 100644 index 000000000..364eeea6b --- /dev/null +++ b/base/src/platform/_main.cc @@ -0,0 +1,259 @@ +/* + * \brief Startup code + * \author Christian Helmuth + * \author Christian Prochaska + * \date 2006-04-12 + * + * The startup code calls constructors for static objects before calling + * main(). Furthermore, this file contains the support of exit handlers + * and destructors. + * + * Some code within this file is based on 'atexit.c' of FreeBSD's libc. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* platform-specific local helper functions */ +#include <_main_helper.h> +#include <_main_parent_cap.h> + + +using namespace Genode; + +extern int main(int argc, char **argv); +extern void init_exception_handling(); /* implemented in base/cxx */ + +enum { ATEXIT_SIZE = 256 }; + + +/*************** + ** C++ stuff ** + ***************/ + +enum Atexit_fn_type { ATEXIT_FN_EMPTY, ATEXIT_FN_STD, ATEXIT_FN_CXA }; + +struct atexit_fn +{ + Atexit_fn_type fn_type; + union + { + void (*std_func)(void); + void (*cxa_func)(void *); + } fn_ptr; /* function pointer */ + void *fn_arg; /* argument for CXA callback */ + void *fn_dso; /* shared module handle */ +}; + +static struct atexit +{ + int index; + struct atexit_fn fns[ATEXIT_SIZE]; +} _atexit; + + +static Lock *atexit_lock() +{ + static Lock _atexit_lock; + return &_atexit_lock; +} + + +static int atexit_register(struct atexit_fn *fn) +{ + atexit_lock()->lock(); + + if (_atexit.index >= ATEXIT_SIZE) { + PERR("Cannot register exit handler - ATEXIT_SIZE reached"); + atexit_lock()->unlock(); + return -1; + } + + _atexit.fns[_atexit.index++] = *fn; + + atexit_lock()->unlock(); + return 0; +} + + +/** + * Register a function to be performed at exit + */ +int genode_atexit(void (*func)(void)) +{ + struct atexit_fn fn; + int error; + + fn.fn_type = ATEXIT_FN_STD; + fn.fn_ptr.std_func = func;; + fn.fn_arg = 0; + fn.fn_dso = 0; + + error = atexit_register(&fn); + return (error); +} + + +/** + * Register a function to be performed at exit or when an shared object + * with given dso handle is unloaded dynamically. + * + * This function is called directly by compiler generated code, so + * it needs to be declared as extern "C" and cannot be local to + * the cxx lib. + */ +int genode___cxa_atexit(void (*func)(void*), void *arg, void *dso) +{ + struct atexit_fn fn; + int error; + + fn.fn_type = ATEXIT_FN_CXA; + fn.fn_ptr.cxa_func = func;; + fn.fn_arg = arg; + fn.fn_dso = dso; + + error = atexit_register(&fn); + return (error); +} + + +/* + * Call all handlers registered with __cxa_atexit for the shared + * object owning 'dso'. Note: if 'dso' is NULL, then all remaining + * handlers are called. + */ +void genode___cxa_finalize(void *dso) +{ + struct atexit_fn fn; + int n = 0; + + atexit_lock()->lock(); + + for (n = _atexit.index; --n >= 0;) { + if (_atexit.fns[n].fn_type == ATEXIT_FN_EMPTY) + continue; /* already been called */ + if (dso != 0 && dso != _atexit.fns[n].fn_dso) + continue; /* wrong DSO */ + fn = _atexit.fns[n]; + + /* + * Mark entry to indicate that this particular handler + * has already been called. + */ + _atexit.fns[n].fn_type = ATEXIT_FN_EMPTY; + atexit_lock()->unlock(); + + /* call the function of correct type */ + if (fn.fn_type == ATEXIT_FN_CXA) + fn.fn_ptr.cxa_func(fn.fn_arg); + else if (fn.fn_type == ATEXIT_FN_STD) + fn.fn_ptr.std_func(); + atexit_lock()->lock(); + } + + atexit_lock()->unlock(); +} + + +/** + * Terminate the process. + */ +void genode_exit(int status) +{ + /* inform parent about the exit status */ + env()->parent()->exit(status); + + /* + * Call destructors for static objects. + * + * It happened that a function from the dtors list (namely + * __clean_env_destructor() from the libc) called another function, which + * depended on the Genode environment. Since the Genode environment gets + * destroyed by genode___cxa_finalize(), the functions from the dtors list + * are called before genode___cxa_finalize(). + * + */ + void (**func)(); + for (func = &_dtors_start; func != &_dtors_end; (*func++)()); + + /* call all handlers registered with atexit() or __cxa_atexit() */ + genode___cxa_finalize(0); + + /* + * Wait for destruction by the parent who was supposed to be notified by + * the destructor of the static Genode::Env instance. + */ + sleep_forever(); +} + + +/** + * Dummy default arguments for main function + */ +static char argv0[] = { '_', 'm', 'a', 'i', 'n', 0}; +static char *argv[1] = { argv0 }; + + +/** + * Arguments for main function + * + * These global variables may be initialized by a constructor provided by an + * external library. + */ +char **genode_argv = argv; +int genode_argc = 1; + + +/** + * C entry function called by the crt0 startup code + */ +extern "C" int _main() +{ + main_thread_bootstrap(); + + /* initialize exception handling */ + init_exception_handling(); + + /* + * Trigger first exception. This step has two purposes. + * First, it enables us to detect problems related to exception handling as + * early as possible. If there are problems with the C++ support library, + * it is much easier to debug them at this early stage. Otherwise problems + * with half-working exception handling cause subtle failures that are hard + * to interpret. + * + * Second, the C++ support library allocates data structures lazily on the + * first occurrence of an exception. This allocation traverses into + * Genode's heap and, in some corner cases, consumes several KB of stack. + * This is usually not a problem when the first exception is triggered from + * the main thread but it becomes an issue when the first exception is + * thrown from the context of a thread with a specially tailored (and + * otherwise sufficient) stack size. By throwing an exception here, we + * mitigate this issue by eagerly performing those allocations. + */ + try { throw 1; } catch (...) { } + + /* call constructors for static objects */ + void (**func)(); + for (func = &_ctors_end; func != &_ctors_start; (*--func)()); + + /* now, it is save to call printf */ + + /* call real main function */ + int ret = main(genode_argc, genode_argv); + + genode_exit(ret); + + /* not reached */ + return ret; +} diff --git a/base/src/platform/_main_parent_cap.h b/base/src/platform/_main_parent_cap.h new file mode 100644 index 000000000..e986b3614 --- /dev/null +++ b/base/src/platform/_main_parent_cap.h @@ -0,0 +1,33 @@ +/* + * \brief Obtain parent capability + * \author Norman Feske + * \date 2010-01-26 + * + * This implementation is used on platforms that rely on global IDs (thread + * IDs, global unique object IDs) as capability representation. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__MAIN_PARENT_CAP_H_ +#define _PLATFORM__MAIN_PARENT_CAP_H_ + +namespace Genode { + + /** + * Return constructed parent capability + */ + Parent_capability parent_cap() + { + Parent_capability cap; + memcpy(&cap, (void *)&_parent_cap, sizeof(cap)); + return Parent_capability(cap); + } +} + +#endif /* _PLATFORM__MAIN_PARENT_CAP_H_ */ diff --git a/base/src/platform/arm/crt0.s b/base/src/platform/arm/crt0.s new file mode 100644 index 000000000..cb01f8a5e --- /dev/null +++ b/base/src/platform/arm/crt0.s @@ -0,0 +1,37 @@ +/** + * \brief Startup code for Genode applications on ARM + * \author Norman Feske + * \date 2007-04-28 + */ + +/* + * Copyright (C) 2007-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/*--- .text (program code) -------------------------*/ +.section ".text.crt0" + + .globl _start +_start: + + ldr sp, .initial_sp + b _main + +.initial_sp: .word _stack_high + + .globl __dso_handle +__dso_handle: + .long 0 + +/*--- .bss (non-initialized data) ------------------*/ +.section ".bss" + + .globl _stack_low +_stack_low: + .space 64*1024 + .globl _stack_high +_stack_high: + diff --git a/base/src/platform/genode.ld b/base/src/platform/genode.ld new file mode 100644 index 000000000..82cd58222 --- /dev/null +++ b/base/src/platform/genode.ld @@ -0,0 +1,117 @@ +/* + * \brief Linker script for Genode programs + * \author Christian Helmuth + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +ENTRY(_start) + +PHDRS +{ + ro PT_LOAD; + rw PT_LOAD; +} + +SECTIONS +{ + .text : { + /* begin of program image (link address) */ + _prog_img_beg = .; + + *(.init) + *(.text .text.* .gnu.linkonce.t.*) + *(.fini) + *(.rodata .rodata.* .gnu.linkonce.r.*) + + . = ALIGN(0x08); + + _ctors_start = .; + KEEP (*(.ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.init_array)) /* list of constructors specific for ARM eabi */ + _ctors_end = .; + _dtors_start = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + _dtors_end = .; + } : ro = 0x90909090 + + /* Linux: exception section for uaccess mechanism */ + __ex_table : { *(__ex_table) } + + .eh_frame_hdr : { *(.eh_frame_hdr) } + + . = ALIGN(0x1000); + + _prog_img_data = .; + + .data : { + /* + * Leave space for parent capability parameters at start of data + * section. The protection domain creator is reponsible for storing + * sane values here. + */ + _parent_cap = .; + _parent_cap_thread_id = .; + LONG(0xffffffff); + _parent_cap_local_name = .; + LONG(0xffffffff); + + /* + * Platform-specific entry for Fiasco.OC. + * + * PIC-code compiled for Fiasco.OC, needs some PIC-compatible + * way to enter the kernel, the fixed address of the kernel + * entry code address needs to be found here. + */ + __l4sys_invoke_indirect = .; + LONG(0xeacff000); + + *(.data .data.* .gnu.linkonce.d.*) + } : rw + + /* exception frames for C++ */ + .eh_frame : { + __eh_frame_start__ = .; + KEEP (*(.eh_frame)) + LONG(0) + } : rw + + .gcc_except_table : { + KEEP(*(.gcc_except_table)) + KEEP(*(.gcc_except_table.*)) + } + + .dynamic : { *(.dynamic) } + + /* .ARM.exidx is sorted, so has to go in its own output section */ + __exidx_start = .; + .ARM.exidx : { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } + __exidx_end = .; + + .ARM.extab : { + *(.ARM.extab*) + } : rw + + .bss : { + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } + + /* end of program image -- must be after last section */ + _prog_img_end = .; + + /DISCARD/ : { + *(.note) + *(.note.ABI-tag) + *(.comment) + } +} diff --git a/base/src/platform/x86_32/crt0.s b/base/src/platform/x86_32/crt0.s new file mode 100644 index 000000000..577a82d15 --- /dev/null +++ b/base/src/platform/x86_32/crt0.s @@ -0,0 +1,61 @@ +/** + * \brief Startup code for Genode applications + * \author Christian Helmuth + * \date 2009-08-12 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/*--- .text (program code) -------------------------*/ + .text + .global _start + +_start: + mov %esp, __initial_sp + + /* XXX Switch to our own stack. */ + leal _stack_high, %esp + + /* Clear the base pointer so that stack backtraces will work. */ + xor %ebp,%ebp + + /* Jump into init C code */ + call _main + + /* We should never get here since _main does not return */ +1: int $3 + jmp 2f + .ascii "_main() returned." +2: jmp 1b + + +/*--------------------------------------------------*/ + .data + .globl __dso_handle +__dso_handle: + .long 0 + + .globl __initial_sp +__initial_sp: + .long 0 + +/*--- .eh_frame (exception frames) -----------------*/ +/* + .section .eh_frame,"aw" + .global __EH_FRAME_BEGIN__ +__EH_FRAME_BEGIN__: +*/ + +/*--- .bss (non-initialized data) ------------------*/ + .bss + .p2align 4 + .global _stack_low +_stack_low: + .space 64*1024 + .global _stack_high +_stack_high: diff --git a/base/src/platform/x86_64/crt0.s b/base/src/platform/x86_64/crt0.s new file mode 100644 index 000000000..c19b325bb --- /dev/null +++ b/base/src/platform/x86_64/crt0.s @@ -0,0 +1,65 @@ +/** + * \brief Startup code for Genode 64Bit applications + * \author Sebastian Sumpf + * \date 2011-05-11 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/*--- .text (program code) -------------------------*/ + .text + .global _start + +_start: + + movq __initial_sp@GOTPCREL(%rip), %rax + movq %rsp, (%rax) + + /* XXX Switch to our own stack. */ + leaq _stack_high@GOTPCREL(%rip),%rax + movq (%rax), %rsp + + /* Clear the base pointer so that stack backtraces will work. */ + xorq %rbp,%rbp + + /* Jump into init C code */ + call _main + + /* We should never get here since _main does not return */ +1: int $3 + jmp 2f + .ascii "_main() returned." +2: jmp 1b + + +/*--------------------------------------------------*/ + .data + .p2align 8 + .globl __dso_handle +__dso_handle: + .quad 0 + + .globl __initial_sp +__initial_sp: + .quad 0 + +/*--- .eh_frame (exception frames) -----------------*/ +/* + .section .eh_frame,"aw" + .global __EH_FRAME_BEGIN__ +__EH_FRAME_BEGIN__: +*/ + +/*--- .bss (non-initialized data) ------------------*/ + .bss + .p2align 8 + .global _stack_low +_stack_low: + .space 64*1024 + .global _stack_high +_stack_high: diff --git a/base/src/test/rm_fault/main.cc b/base/src/test/rm_fault/main.cc new file mode 100644 index 000000000..9d34ca0f3 --- /dev/null +++ b/base/src/test/rm_fault/main.cc @@ -0,0 +1,213 @@ +/* + * \brief Test program for raising and handling region-manager faults + * \author Norman Feske + * \date 2008-09-24 + * + * This program starts itself as child. When started, it first determines + * wheather it is parent or child by requesting its own file from the ROM + * service. Because the program blocks all session-creation calls for the + * ROM service, each program instance can determine its parent or child + * role by the checking the result of the session creation. + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + +/*********** + ** Child ** + ***********/ + +enum { MANAGED_ADDR = 0x10000000 }; + + +void read_at(addr_t addr) +{ + printf("perform read operation at 0x%p\n", (void *)addr); + int value = *(int *)addr; + printf("read value %x\n", value); +} + +void modify(addr_t addr) +{ + printf("modify memory at 0x%p to %x\n", (void *)addr, ++(*(int *)addr)); +} + +void main_child() +{ + printf("child role started\n"); + + /* perform illegal access */ + read_at(MANAGED_ADDR); + + while (true) + modify(MANAGED_ADDR); + + printf("--- child role of region-manager fault test finished ---\n"); +} + + +/************ + ** Parent ** + ************/ + +class Test_child : public Child_policy +{ + private: + + enum { STACK_SIZE = 8*1024 }; + + /* + * Entry point used for serving the parent interface + */ + Rpc_entrypoint _entrypoint; + + Child _child; + + Parent_service _log_service; + + public: + + /** + * Constructor + */ + Test_child(Genode::Dataspace_capability elf_ds, + Genode::Ram_session_capability ram, + Genode::Cpu_session_capability cpu, + Genode::Rm_session_capability rm, + Genode::Cap_session *cap) + : + _entrypoint(cap, STACK_SIZE, "child", false), + _child(elf_ds, ram, cpu, rm, &_entrypoint, this), + _log_service("LOG") + { + /* start execution of the new child */ + _entrypoint.activate(); + } + + Rm_session_capability rm_session_cap() { return _child.rm_session_cap(); } + + + /**************************** + ** Child-policy interface ** + ****************************/ + + const char *name() const { return "rmchild"; } + + Service *resolve_session_request(const char *service, const char *) + { + /* forward log-session request to our parent */ + return !strcmp(service, "LOG") ? &_log_service : 0; + } + + void filter_session_args(const char *service, + char *args, size_t args_len) + { + /* define session label for sessions forwarded to our parent */ + Arg_string::set_arg(args, args_len, "label", "child"); + } +}; + + +void main_parent(Dataspace_capability elf_ds) +{ + printf("parent role started\n"); + + /* create environment for new child */ + static Ram_connection ram; + static Cpu_connection cpu; + static Rm_connection rm; + static Cap_connection cap; + + /* transfer some of our own ram quota to the new child */ + enum { CHILD_QUOTA = 1*1024*1024 }; + ram.ref_account(env()->ram_session_cap()); + env()->ram_session()->transfer_quota(ram.cap(), CHILD_QUOTA); + + static Signal_receiver fault_handler; + + /* register fault handler */ + static Signal_context signal_context; + rm.fault_handler(fault_handler.manage(&signal_context)); + + /* create child */ + static Test_child child(elf_ds, ram.cap(), cpu.cap(), rm.cap(), &cap); + + /* allocate dataspace used for creating shared memory between parent and child */ + Dataspace_capability ds = env()->ram_session()->alloc(4096); + volatile int *local_addr = env()->rm_session()->attach(ds); + + for (int i = 0; i < 4; i++) { + + printf("wait for region-manager fault\n"); + fault_handler.wait_for_signal(); + printf("received region-manager fault signal, request fault state\n"); + + Rm_session::State state = rm.state(); + + printf("rm session state is %s, pf_addr=0x%p\n", + state.type == Rm_session::READ_FAULT ? "READ_FAULT" : + state.type == Rm_session::WRITE_FAULT ? "WRITE_FAULT" : + state.type == Rm_session::EXEC_FAULT ? "EXEC_FAULT" : "READY", + (void *)state.addr); + + /* ignore spuriuous fault signal */ + if (state.type == Rm_session::READY) { + PINF("ignoring spurious fault signal"); + continue; + } + + addr_t child_virt_addr = state.addr & ~(4096 - 1); + + /* allocate dataspace to resolve the fault */ + printf("attach dataspace to the child at 0x%p\n", (void *)child_virt_addr); + *local_addr = 0x1234; + + rm.attach_at(ds, child_virt_addr); + + /* wait until our child modifies the dataspace content */ + while (*local_addr == 0x1234); + + printf("child modified dataspace content, new value is %x\n", *local_addr); + + printf("revoke dataspace from child\n"); + rm.detach((void *)child_virt_addr); + } + + printf("--- parent role of region-manager fault test finished ---\n"); +} + + +/************************* + ** Common main program ** + *************************/ + +int main(int argc, char **argv) +{ + printf("--- region-manager fault test ---\n"); + + /* obtain own elf file from rom service */ + try { + static Rom_connection rom("test-rmfault"); + main_parent(rom.dataspace()); + } catch (Genode::Rom_connection::Rom_connection_failed) { + main_child(); + } + + return 0; +} diff --git a/base/src/test/rm_fault/target.mk b/base/src/test/rm_fault/target.mk new file mode 100644 index 000000000..394c004bb --- /dev/null +++ b/base/src/test/rm_fault/target.mk @@ -0,0 +1,3 @@ +TARGET = test-rmfault +SRC_CC = main.cc +LIBS = cxx env server signal process diff --git a/base/src/test/rm_nested/main.cc b/base/src/test/rm_nested/main.cc new file mode 100644 index 000000000..5ebd845e4 --- /dev/null +++ b/base/src/test/rm_nested/main.cc @@ -0,0 +1,123 @@ +/* + * \brief Testing nested region-manager sessions + * \author Norman Feske + * \date 2008-09-27 + * + * The program uses two threads. A local fault-handler thread waits for fault + * signals regarding a sub-region-manager session that is mapped into the local + * address space as a dataspace. If a fault occurs, this thread allocates a new + * dataspace and attaches it to the faulting address to resolve the fault. The + * main thread performs memory accesses at the local address range that is + * backed by the sub-region-manager session. Thereby, it triggers + * region-manager faults. + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + + +enum { + MANAGED_SIZE = 0x00010000, + PAGE_SIZE = 4096, +}; + + +/** + * Region-manager fault handler resolves faults by attaching new dataspaces + */ +class Local_fault_handler : public Thread<4096> +{ + private: + + Rm_session *_rm_session; + Signal_receiver *_receiver; + + public: + + Local_fault_handler(Rm_session *rm_session, Signal_receiver *receiver) + : + _rm_session(rm_session), _receiver(receiver) + { } + + void handle_fault() + { + Rm_session::State state = _rm_session->state(); + + printf("rm session state is %s, pf_addr=0x%lx\n", + state.type == Rm_session::READ_FAULT ? "READ_FAULT" : + state.type == Rm_session::WRITE_FAULT ? "WRITE_FAULT" : + state.type == Rm_session::EXEC_FAULT ? "EXEC_FAULT" : "READY", + state.addr); + + printf("allocate dataspace and attach it to sub rm session\n"); + Dataspace_capability ds = env()->ram_session()->alloc(PAGE_SIZE); + _rm_session->attach_at(ds, state.addr & ~(PAGE_SIZE - 1)); + + printf("returning from handle_fault\n"); + } + + void entry() + { + while (true) { + printf("fault handler: waiting for fault signal\n"); + Signal signal = _receiver->wait_for_signal(); + printf("received %d fault signals\n", signal.num()); + for (int i = 0; i < signal.num(); i++) + handle_fault(); + } + } +}; + + +int main(int argc, char **argv) +{ + printf("--- nested region-manager test ---\n"); + + /* + * Initialize sub-region-manager session and set up a local fault handler + * for it. + */ + static Rm_connection sub_rm(0, MANAGED_SIZE); + static Cap_connection cap; + static Signal_receiver receiver; + static Signal_context context; + sub_rm.fault_handler(receiver.manage(&context)); + static Local_fault_handler fault_handler(&sub_rm, &receiver); + fault_handler.start(); + + /* + * Attach sub-region-manager session as dataspace to the local address + * space. + */ + void *addr = env()->rm_session()->attach(sub_rm.dataspace()); + + printf("attached sub dataspace at local address 0x%p\n", addr); + Dataspace_client client(sub_rm.dataspace()); + printf("sub dataspace size is %u should be %u\n", client.size(), MANAGED_SIZE); + + /* + * Walk through the address range belonging to the sub-region-manager session + */ + char *managed = (char *)addr; + for (int i = 0; i < MANAGED_SIZE; i += PAGE_SIZE/16) { + printf("write to %p\n", &managed[i]); + managed[i] = 13; + } + + printf("--- finished nested region-manager test ---\n"); + return 0; +} diff --git a/base/src/test/rm_nested/target.mk b/base/src/test/rm_nested/target.mk new file mode 100644 index 000000000..68f4614f8 --- /dev/null +++ b/base/src/test/rm_nested/target.mk @@ -0,0 +1,4 @@ +TARGET = test-rmnested +REQUIRES = experimental +SRC_CC = main.cc +LIBS = cxx env thread signal diff --git a/base/src/test/sub_rm/config.h b/base/src/test/sub_rm/config.h new file mode 100644 index 000000000..12f4c4418 --- /dev/null +++ b/base/src/test/sub_rm/config.h @@ -0,0 +1,15 @@ +/* + * \brief Platform-specific policy for sub_rm test + * \author Norman Feske + * \date 2011-11-22 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +enum { attach_twice_forbidden = false }; +enum { support_attach_sub_any = true }; diff --git a/base/src/test/sub_rm/main.cc b/base/src/test/sub_rm/main.cc new file mode 100644 index 000000000..7681373be --- /dev/null +++ b/base/src/test/sub_rm/main.cc @@ -0,0 +1,191 @@ +/* + * \brief Basic test for manually managing a sub RM session + * \author Norman Feske + * \date 2011-11-21 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +/* platform-specific test policy */ +#include + +using namespace Genode; + + +static void fail(char const *message) +{ + PERR("FAIL: %s", message); + class Test_failed{}; + throw Test_failed(); +} + + +static char const *test_pattern() { + return "Pattern to verify dataspace content"; } + + +static char const *test_pattern_2() { + return "A second pattern to verify dataspace content"; } + + +static void fill_ds_with_test_pattern(char const *pattern, + Dataspace_capability ds, size_t offset) +{ + printf("fill dataspace with information\n"); + char *content = env()->rm_session()->attach(ds); + strncpy(content + offset, pattern, ~0); + env()->rm_session()->detach(content); +} + + +static void validate_pattern_at(char const *pattern, char const *ptr) +{ + if (strcmp(ptr, pattern) != 0) + fail("test pattern not found"); +} + + +int main(int, char **) +{ + printf("--- sub-rm test ---\n"); + + printf("create RM connection\n"); + enum { SUB_RM_SIZE = 1024*1024 }; + Rm_connection sub_rm(0, SUB_RM_SIZE); + + enum { DS_SIZE = 4*4096 }; + Ram_dataspace_capability ds = env()->ram_session()->alloc(DS_SIZE); + + /* + * Write test patterns to the start and the second page of the RAM ds + */ + fill_ds_with_test_pattern(test_pattern(), ds, 0); + fill_ds_with_test_pattern(test_pattern_2(), ds, 4096); + + if (!support_attach_sub_any) { + printf("attach RAM ds to any position at sub rm - this should fail\n"); + try { + sub_rm.attach(ds, 0, 0, false, (addr_t)0); + fail("sub rm attach_any unexpectedly did not fail"); + } + catch (Rm_session::Out_of_metadata) { + printf("attach failed as expected\n"); } + } + + printf("attach RAM ds to a fixed position at sub rm\n"); + + enum { DS_SUB_OFFSET = 4096 }; + if ((addr_t)sub_rm.attach_at(ds, DS_SUB_OFFSET, 0, 0) != DS_SUB_OFFSET) + fail("attach_at return-value mismatch"); + + printf("attach sub rm at local address space\n"); + + /* + * We use a fixed local address here because this makes the address space + * layout more predictable. I.e., this simplifies the validation of + * 'proc/pid/maps' after completing the test on Linux. + * + * Technically, this could let the test fail (if Linux decides to mmap the + * vdso page to this location. reason ... keeping fingers crossed. + */ + enum { LOCAL_ATTACH_ADDR = 0x60000000 }; + char *sub_rm_base = env()->rm_session()->attach_at(sub_rm.dataspace(), + LOCAL_ATTACH_ADDR); + + printf("validate pattern in sub rm\n"); + validate_pattern_at(test_pattern(), sub_rm_base + DS_SUB_OFFSET); + + /* + * Pre-populated sub rm session appeared correctly in the local address + * space. Now, test further populating the already attached sub rm session. + */ + + printf("attach RAM ds at another fixed position at sub rm\n"); + enum { DS_SUB_OFFSET_2 = 0x40000 }; + if ((addr_t)sub_rm.attach_at(ds, DS_SUB_OFFSET_2, 0, 0) != DS_SUB_OFFSET_2) + fail("attach_at return-value mismatch"); + + printf("validate pattern in second mapping in sub rm\n"); + validate_pattern_at(test_pattern(), sub_rm_base + DS_SUB_OFFSET_2); + + /* + * Try to cross the boundaries of the sub RM session. This should + * produce an error. + */ + try { + sub_rm.attach_at(ds, SUB_RM_SIZE - 4096, 0, 0); + fail("undetected boundary conflict\n"); + } + catch (Rm_session::Region_conflict) { + printf("attaching beyond sub RM boundary failed as expected\n"); } + + /* + * Check for working region - conflict detection + */ + printf("attaching RAM ds to a conflicting region\n"); + try { + sub_rm.attach_at(ds, DS_SUB_OFFSET + 4096, 0, 0); + fail("region conflict went undetected\n"); + } + catch (Rm_session::Region_conflict) { + printf("attaching conflicting region failed as expected\n"); } + + if (attach_twice_forbidden) { + /* + * Try to double-attach the same sub RM session. This should fail + */ + printf("attach sub rm again at local address space\n"); + try { + env()->rm_session()->attach(sub_rm.dataspace()); + fail("double attachment of sub RM session went undetected\n"); + } + catch (Rm_session::Out_of_metadata) { + printf("doubly attaching sub RM session failed as expected\n"); } + } + + /* + * Try attaching RAM ds with offset (skipping the first page of + * the RAM ds). We expect the second test pattern at the beginning + * of the region. The region size should be automatically reduced by one + * page. + */ + printf("attach RAM ds with offset\n"); + enum { DS_SUB_OFFSET_3 = 0x80000 }; + sub_rm.attach_at(ds, DS_SUB_OFFSET_3, 0, 4096); + validate_pattern_at(test_pattern_2(), sub_rm_base + DS_SUB_OFFSET_3); + + /* + * Add the size parameter to the mix, attaching only a window of two pages + * starting with the second page. + */ + printf("attach RAM ds with offset and size\n"); + enum { DS_SUB_OFFSET_4 = 0xc0000 }; + sub_rm.attach_at(ds, DS_SUB_OFFSET_4, 2*4096, 4096); + validate_pattern_at(test_pattern_2(), sub_rm_base + DS_SUB_OFFSET_4); + + /* + * Detach the first attachment (to be validated by the run script by + * inspecting '/proc/pid/maps' after running the test. + */ + sub_rm.detach((void *)DS_SUB_OFFSET); + + printf("--- end of sub-rm test ---\n"); + + /* + * Do not return from main function to allow the run script to inspect the + * memory mappings after completing the test. + */ + sleep_forever(); + return 0; +} diff --git a/base/src/test/sub_rm/target.mk b/base/src/test/sub_rm/target.mk new file mode 100644 index 000000000..c76642aa6 --- /dev/null +++ b/base/src/test/sub_rm/target.mk @@ -0,0 +1,6 @@ +TARGET = test-sub_rm +SRC_CC = main.cc +LIBS = env cxx + +# find 'config.h' depending on the current platform +REP_INC_DIR += src/test/sub_rm diff --git a/dde_ipxe/Makefile b/dde_ipxe/Makefile new file mode 100644 index 000000000..07e412eb7 --- /dev/null +++ b/dde_ipxe/Makefile @@ -0,0 +1,54 @@ +# +# \brief Fetch and patch iPXE source code +# \author Stefan Kalkowski +# \author Christian Helmuth +# \date 2011-08-12 +# + +VERBOSE ?= @ +ECHO = @echo +GIT_URL = git://git.ipxe.org/ipxe.git +GIT_REV = 174df77359f22f3be2169e9bb04e8018015b5e94 +CONTRIB_DIR = contrib +PATCH_FILE = patches/dde_ipxe.patch + +# +# Print help information by default +# +help: + $(ECHO) + $(ECHO) "Prepare the dde_ipxe repository" + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - fetch and patch iPXE source code" + $(ECHO) "clean - revert patch from iPXE souce code" + $(ECHO) "cleanall - remove iPXE souce code" + $(ECHO) "update-patch - updates patch for iPXE source code" + $(ECHO) + +$(CONTRIB_DIR)/.git: + $(VERBOSE)git clone $(GIT_URL) $(CONTRIB_DIR) + +prepare: $(CONTRIB_DIR)/.git clean + $(ECHO) "apply patch to '$(CONTRIB_DIR)/'" + $(VERBOSE)patch -p1 -d $(CONTRIB_DIR) -i $(realpath $(PATCH_FILE)) + $(ECHO) + $(ECHO) "Preparation completed!" + $(ECHO) "Hint: don't forget to put '$(shell pwd)' " + $(ECHO) " as a repository into your build.conf" + $(ECHO) + +update-patch: + $(ECHO) "producing a new diff and save it to '$(PATCH_FILE)'" + $(VERBOSE)(cd $(CONTRIB_DIR); LC_COLLATE=C git diff) > $(PATCH_FILE) || true +# $(VERBOSE)(cd $(CONTRIB_DIR); LC_COLLATE=C git diff) \ +# | sed "s/\(^--- [^\t]*\).*/\\1/" \ +# | sed "s/\(^+++ [^\t]*\).*/\\1/" \ +# > $(PATCH_FILE) || true + +clean: + $(VERBOSE)cd $(CONTRIB_DIR); git reset --hard $(GIT_REV) + $(VERBOSE)cd $(CONTRIB_DIR); git ls-files -o | xargs rm -rf + +cleanall: + $(VERBOSE)rm -rf $(CONTRIB_DIR) diff --git a/dde_ipxe/README b/dde_ipxe/README new file mode 100644 index 000000000..d9225e28d --- /dev/null +++ b/dde_ipxe/README @@ -0,0 +1,18 @@ +This repository contains the Device Driver Environment for the +"donator OS" iPXE available from http://ipxe.org/. + +For building DDE iPXE, you first need to fetch and patch the original +sources. The top-level makefile of this repository automates this +task. Just issue: + +! make prepare + +Now, you need to include the DDE iPXE repository into your Genode +build process. Just add the path to this directory to the +'REPOSITORIES' declaration of the 'etc/build.conf' file within your +build directory, for example + +! REPOSITORIES += $(GENODE_DIR)/dde_ipxe + +After successful build the DDE iPXE based ethernet driver is located +at 'bin/nic_drv'. diff --git a/dde_ipxe/include/dde_ipxe/nic.h b/dde_ipxe/include/dde_ipxe/nic.h new file mode 100644 index 000000000..e990e4585 --- /dev/null +++ b/dde_ipxe/include/dde_ipxe/nic.h @@ -0,0 +1,67 @@ +/* + * \brief DDE iPXE NIC API + * \author Christian Helmuth + * \date 2010-09-05 + * + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _DDE_IPXE__NIC_H_ +#define _DDE_IPXE__NIC_H_ + +/** + * Packet reception callback + * + * \param if_index index of the receiving network interface + * \param packet buffer containing the packet + * \param packet_len packet length + */ +typedef void (*dde_ipxe_nic_rx_cb)(unsigned if_index, const char *packet, unsigned packet_len); + +/** + * Register packet reception callback + * + * \param cb new callback function + * + * \return old callback function pointer + * + * This registers a function pointer as rx callback. Incoming ethernet packets + * are passed to this function. + */ +extern dde_ipxe_nic_rx_cb dde_ipxe_nic_register_rx_callback(dde_ipxe_nic_rx_cb cb); + +/** + * Send packet + * + * \param if_index index of the network interface to be used for sending + * \param packet buffer containing the packet + * \param packet_len packet length + * + * \return 0 on success, -1 otherwise + */ +extern int dde_ipxe_nic_tx(unsigned if_index, const char *packet, unsigned packet_len); + +/** + * Get MAC address of device + * + * \param if_index index of the network interface + * \param out_mac_addr buffer for MAC address (buffer size must be 6 byte) + * + * \return 0 on success, -1 otherwise + */ +extern int dde_ipxe_nic_get_mac_addr(unsigned if_index, char *out_mac_addr); + +/** + * Initialize network sub-system + * + * \return number of network devices + */ +extern int dde_ipxe_nic_init(void); + +#endif /* _DDE_IPXE__NIC_H_ */ diff --git a/dde_ipxe/lib/mk/dde_ipxe_nic.mk b/dde_ipxe/lib/mk/dde_ipxe_nic.mk new file mode 100644 index 000000000..303215670 --- /dev/null +++ b/dde_ipxe/lib/mk/dde_ipxe_nic.mk @@ -0,0 +1,49 @@ +LIB_DIR := $(REP_DIR)/src/lib/dde_ipxe +CONTRIB_DIR := $(REP_DIR)/contrib/src + +LIBS = dde_kit dde_ipxe_support + +SRC_C = nic.c dde.c dummies.c + +SRC_C += $(addprefix core/, iobuf.c) +SRC_C += $(addprefix arch/x86/core/, x86_string.c) +SRC_C += $(addprefix arch/i386/core/, rdtsc_timer.c) +SRC_C += $(addprefix net/, ethernet.c netdevice.c nullnet.c eth_slow.c) +SRC_C += $(addprefix drivers/bus/, pciextra.c) +SRC_C += $(addprefix drivers/net/, pcnet32.c) # TODO rtl8139.c eepro100.c virtio-net.c ns8390.c +SRC_C += $(addprefix drivers/net/e1000/, \ + e1000.c e1000_82540.c e1000_82541.c e1000_82542.c e1000_82543.c \ + e1000_api.c e1000_mac.c e1000_main.c e1000_manage.c e1000_nvm.c \ + e1000_phy.c) +SRC_C += $(addprefix drivers/net/e1000e/, \ + e1000e.c e1000e_80003es2lan.c e1000e_82571.c e1000e_ich8lan.c \ + e1000e_mac.c e1000e_main.c e1000e_manage.c e1000e_nvm.c \ + e1000e_phy.c) + +INC_DIR += $(LIB_DIR)/include \ + $(CONTRIB_DIR)/include $(CONTRIB_DIR) \ + $(CONTRIB_DIR)/arch/x86/include \ + $(CONTRIB_DIR)/arch/i386/include \ + $(CONTRIB_DIR)/arch/i386/include/pcbios + +CC_WARN = -Wall -Wno-address +CC_OPT += $(addprefix -fno-builtin-, putchar toupper tolower) +CC_OPT += -DARCH=i386 -DPLATFORM=pcbios -include compiler.h -DOBJECT=$(notdir $(*:.o=)) + +# +# Enable debugging of any iPXE object here via '-Ddebug_='. +# 'level' may be one of 1, 3, 7. +# +CC_OPT += -Ddebug_lib=7 +#CC_OPT += -Ddebug_e1000_main=7 -Ddebug_e1000_82540=7 -Ddebug_netdevice=7 +#CC_OPT += -Ddebug_e1000=7 -Ddebug_e1000_82540=7 -Ddebug_e1000_api=7 +#CC_OPT += -Ddebug_e1000_main=7 -Ddebug_e1000_manage=7 +#CC_OPT += -Ddebug_e1000_phy=7 +#CC_OPT += -Ddebug_netdevice=7 + + +vpath nic.c $(LIB_DIR) +vpath dde.c $(LIB_DIR) +vpath dummies.c $(LIB_DIR) + +vpath %.c $(CONTRIB_DIR) diff --git a/dde_ipxe/lib/mk/dde_ipxe_support.mk b/dde_ipxe/lib/mk/dde_ipxe_support.mk new file mode 100644 index 000000000..51b3ed9bf --- /dev/null +++ b/dde_ipxe/lib/mk/dde_ipxe_support.mk @@ -0,0 +1,5 @@ +LIB_DIR := $(REP_DIR)/src/lib/dde_ipxe + +SRC_CC = dde_support.cc + +vpath dde_support.cc $(LIB_DIR) diff --git a/dde_ipxe/patches/dde_ipxe.patch b/dde_ipxe/patches/dde_ipxe.patch new file mode 100644 index 000000000..c3880929d --- /dev/null +++ b/dde_ipxe/patches/dde_ipxe.patch @@ -0,0 +1,42 @@ +diff --git a/src/arch/i386/include/ipxe/rdtsc_timer.h b/src/arch/i386/include/ipxe/rdtsc_timer.h +index 472e140..d5095a7 100644 +--- a/src/arch/i386/include/ipxe/rdtsc_timer.h ++++ b/src/arch/i386/include/ipxe/rdtsc_timer.h +@@ -30,7 +30,10 @@ static inline __always_inline unsigned long + TIMER_INLINE ( rdtsc, currticks ) ( void ) { + unsigned long ticks; + +- __asm__ __volatile__ ( "rdtsc\n\t" ++ __asm__ __volatile__ ( ++ "mfence\n\t" ++ "rdtsc\n\t" ++ "mfence\n\t" + "shrdl %1, %%edx, %%eax\n\t" + : "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" ); + return ticks; +diff --git a/src/include/assert.h b/src/include/assert.h +index 40a00a2..8bae55c 100644 +--- a/src/include/assert.h ++++ b/src/include/assert.h +@@ -29,7 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); + * to the printf symbol. + */ + extern int __attribute__ (( format ( printf, 1, 2 ) )) +-assert_printf ( const char *fmt, ... ) asm ( "printf" ); ++assert_printf ( const char *fmt, ... ) asm ( "ipxe_printf" ); + + /** + * Assert a condition at run-time. +diff --git a/src/include/compiler.h b/src/include/compiler.h +index feea516..d5163b6 100644 +--- a/src/include/compiler.h ++++ b/src/include/compiler.h +@@ -271,7 +271,7 @@ REQUEST_EXPANDED ( CONFIG_SYMBOL ); + * to the printf symbol. + */ + extern int __attribute__ (( format ( printf, 1, 2 ) )) +-dbg_printf ( const char *fmt, ... ) asm ( "printf" ); ++dbg_printf ( const char *fmt, ... ) asm ( "ipxe_printf" ); + + extern void dbg_autocolourise ( unsigned long id ); + extern void dbg_decolourise ( void ); diff --git a/dde_ipxe/src/drivers/nic/main.cc b/dde_ipxe/src/drivers/nic/main.cc new file mode 100644 index 000000000..8c786aca3 --- /dev/null +++ b/dde_ipxe/src/drivers/nic/main.cc @@ -0,0 +1,136 @@ +/* + * \brief NIC driver based on iPXE + * \author Christian Helmuth + * \date 2011-11-17 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode */ +#include +#include +#include +#include +#include + +/* DDE */ +extern "C" { +#include +} + + +namespace Ipxe { + + class Driver : public Nic::Driver + { + public: + + static Driver *instance; + + static void dde_rx_handler(unsigned if_index, + const char *packet, + unsigned packet_len) + { + instance->rx_handler(packet, packet_len); + } + + private: + + Nic::Mac_address _mac_addr; + Nic::Rx_buffer_alloc &_alloc; + + public: + + Driver(Nic::Rx_buffer_alloc &alloc) + : _alloc(alloc) + { + PINF("--- init iPXE NIC"); + int cnt = dde_ipxe_nic_init(); + PINF(" number of devices: %d", cnt); + + PINF("--- init rx_callbacks"); + dde_ipxe_nic_register_rx_callback(dde_rx_handler); + + PINF("--- get MAC address"); + dde_ipxe_nic_get_mac_addr(1, _mac_addr.addr); + PINF(" %02x:%02x:%02x:%02x:%02x:%02x", + _mac_addr.addr[0], _mac_addr.addr[1], _mac_addr.addr[2], + _mac_addr.addr[3], _mac_addr.addr[4], _mac_addr.addr[5]); + } + + void rx_handler(const char *packet, unsigned packet_len) + { + void *buffer = _alloc.alloc(packet_len); + Genode::memcpy(buffer, packet, packet_len); + _alloc.submit(); + } + + + /*************************** + ** Nic::Driver interface ** + ***************************/ + + Nic::Mac_address mac_address() { return _mac_addr; } + + void tx(char const *packet, Genode::size_t size) + { + if (dde_ipxe_nic_tx(1, packet, size)) + PWRN("Sending packet failed!"); + } + + + /****************************** + ** Irq_activation interface ** + ******************************/ + + void handle_irq(int) { /* not used */ } + }; + + class Driver_factory : public Nic::Driver_factory + { + Nic::Driver *create(Nic::Rx_buffer_alloc &alloc) + { + Driver::instance = new (Genode::env()->heap()) Ipxe::Driver(alloc); + return Driver::instance; + } + + void destroy(Nic::Driver *) + { + Genode::destroy(Genode::env()->heap(), Driver::instance); + Driver::instance = 0; + } + }; + +} /* namespace Ipxe */ + + +Ipxe::Driver * Ipxe::Driver::instance = 0; + + +int main(int, char **) +{ + using namespace Genode; + + printf("--- iPXE NIC driver started ---\n"); + + /** + * Factory used by 'Nic::Root' at session creation/destruction time + */ + static Ipxe::Driver_factory driver_factory; + + enum { STACK_SIZE = 4096 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "nic_ep"); + + static Nic::Root nic_root(&ep, env()->heap(), driver_factory); + env()->parent()->announce(ep.manage(&nic_root)); + + sleep_forever(); + return 0; +} + diff --git a/dde_ipxe/src/drivers/nic/target.mk b/dde_ipxe/src/drivers/nic/target.mk new file mode 100644 index 000000000..30a700d96 --- /dev/null +++ b/dde_ipxe/src/drivers/nic/target.mk @@ -0,0 +1,3 @@ +TARGET := nic_drv +LIBS := cxx env signal server dde_ipxe_nic +SRC_CC = main.cc diff --git a/dde_ipxe/src/lib/dde_ipxe/dde.c b/dde_ipxe/src/lib/dde_ipxe/dde.c new file mode 100644 index 000000000..3a331ae42 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/dde.c @@ -0,0 +1,350 @@ +/* + * \brief DDE iPXE emulation implementation + * \author Christian Helmuth + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* DDE kit */ +#include +#include +#include +#include +#include +#include + +/* iPXE */ +#include +#include +#include +#include +#include +#include +#include + +#include "local.h" + +/********************************** + ** Memory pool in DDE kit slabs ** + **********************************/ + +enum { SLAB_128, SLAB_256, SLAB_512, SLAB_1024, SLAB_2048, SLAB_4096, SLAB_20480, NUM_SLABS }; + +static struct dde_kit_slab *slabs[NUM_SLABS]; + +static inline void *alloc_from_slab(size_t size) +{ + size_t *p = 0; + size_t alloc_size = size + sizeof(size_t); + + if (alloc_size <= 128) + p = dde_kit_slab_alloc(slabs[SLAB_128]); + else if (alloc_size <= 256) + p = dde_kit_slab_alloc(slabs[SLAB_256]); + else if (alloc_size <= 512) + p = dde_kit_slab_alloc(slabs[SLAB_512]); + else if (alloc_size <= 1024) + p = dde_kit_slab_alloc(slabs[SLAB_1024]); + else if (alloc_size <= 2048) + p = dde_kit_slab_alloc(slabs[SLAB_2048]); + else if (alloc_size <= 4096) + p = dde_kit_slab_alloc(slabs[SLAB_4096]); + else if (alloc_size <= 20480) + p = dde_kit_slab_alloc(slabs[SLAB_20480]); + else + LOG("allocation of size %d too big", size); + + if (p) { + *p = alloc_size; + p++; + } + + return p; +} + + +static inline void free_in_slab(void *p0) +{ + size_t *p = (size_t *)p0 - 1; + + if (*p <= 128) + dde_kit_slab_free(slabs[SLAB_128], p); + else if (*p <= 256) + dde_kit_slab_free(slabs[SLAB_256], p); + else if (*p <= 512) + dde_kit_slab_free(slabs[SLAB_512], p); + else if (*p <= 1024) + dde_kit_slab_free(slabs[SLAB_1024], p); + else if (*p <= 2048) + dde_kit_slab_free(slabs[SLAB_2048], p); + else if (*p <= 4096) + dde_kit_slab_free(slabs[SLAB_4096], p); + else if (*p <= 20480) + dde_kit_slab_free(slabs[SLAB_20480], p); + else + LOG("deallocation at %p not possible", p0); +} + + +void slab_init(void) +{ + slabs[SLAB_128] = dde_kit_slab_init(128); + slabs[SLAB_256] = dde_kit_slab_init(256); + slabs[SLAB_512] = dde_kit_slab_init(512); + slabs[SLAB_1024] = dde_kit_slab_init(1024); + slabs[SLAB_2048] = dde_kit_slab_init(2048); + slabs[SLAB_4096] = dde_kit_slab_init(4096); + slabs[SLAB_20480] = dde_kit_slab_init(20480); +} + + +/************ + ** stdlib ** + ************/ + +void *zalloc(size_t size) +{ + char *buf = alloc_from_slab(size); + + if (buf) + memset(buf, 0, size); + + return buf; +} + + +void * malloc(size_t size) +{ + return alloc_from_slab(size); +} + + +void free(void *p) +{ + free_in_slab(p); +} + + +/********************* + ** Time and Timers ** + *********************/ + +void udelay (unsigned long usecs) +{ + static int init = 0; + + extern void __rdtsc_udelay(unsigned long usecs); + + /* + * On first udelay, the rdtsc implementation is calibrated. Therefore, we + * force a delay of 10ms to get sane values. + */ + if (!init) { + __rdtsc_udelay(10000); + init = 1; + } else { + __rdtsc_udelay(usecs); + } +} + + +void mdelay (unsigned long msecs) +{ + dde_kit_thread_msleep(msecs); +} + + +int ipxe_printf(const char *format, ...) +{ + /* replace unsupported '%#' with 'x%' in format string */ + char *new_format = (char *)malloc(strlen(format) + 1); + if (!new_format) + return -1; + memcpy(new_format, format, strlen(format) + 1); + { + int off; + char *f; + for (off = 0, f = new_format; *f; off++, f++) + if (f[0] == '%' && f[1] == '#') { + f[0] = 'x'; + f[1] = '%'; + } + } + + va_list va; + + va_start(va, format); + dde_kit_vprintf(new_format, va); + va_end(va); + + free(new_format); + return 0; +} + + +/*********************************** + ** RAM and I/O memory management ** + ***********************************/ + +void iounmap(volatile const void *io_addr) +{ + LOG("io_addr = %p", io_addr); + /* XXX DDE kit always releases the whole region */ + dde_kit_release_mem((dde_kit_addr_t) io_addr, 1); +} + + +void * ioremap(unsigned long bus_addr, size_t len) +{ + LOG("bus_addr = %p len = %x", (void *)bus_addr, len); + dde_kit_addr_t vaddr; + + int ret = dde_kit_request_mem(bus_addr, len, 0, &vaddr); + + return ret ? 0 : (void *)vaddr; +} + + +unsigned long user_to_phys(userptr_t userptr, off_t offset) +{ + return dde_kit_pgtab_get_physaddr((void *)userptr) + offset; +} + + +userptr_t virt_to_user(volatile const void *addr) +{ + return trivial_virt_to_user(addr); +} + + +unsigned long phys_to_bus(unsigned long phys_addr) +{ + return phys_addr; +} + + +/******************* + ** PCI subsystem ** + *******************/ + +int pci_read_config_byte(struct pci_device *pci, unsigned int where, uint8_t *value) +{ + dde_kit_pci_readb(PCI_BUS(pci->busdevfn), PCI_SLOT(pci->busdevfn), PCI_FUNC(pci->busdevfn), + where, value); + + return 0; +} + + +int pci_read_config_word(struct pci_device *pci, unsigned int where, uint16_t *value) +{ + dde_kit_pci_readw(PCI_BUS(pci->busdevfn), PCI_SLOT(pci->busdevfn), PCI_FUNC(pci->busdevfn), + where, value); + + return 0; +} + + +int pci_read_config_dword(struct pci_device *pci, unsigned int where, uint32_t *value) +{ + dde_kit_pci_readl(PCI_BUS(pci->busdevfn), PCI_SLOT(pci->busdevfn), PCI_FUNC(pci->busdevfn), + where, value); + + return 0; +} + + +int pci_write_config_byte(struct pci_device *pci, unsigned int where, uint8_t value) +{ + dde_kit_pci_writeb(PCI_BUS(pci->busdevfn), PCI_SLOT(pci->busdevfn), PCI_FUNC(pci->busdevfn), + where, value); + + return 0; +} + + +int pci_write_config_word(struct pci_device *pci, unsigned int where, uint16_t value) +{ + dde_kit_pci_writew(PCI_BUS(pci->busdevfn), PCI_SLOT(pci->busdevfn), PCI_FUNC(pci->busdevfn), + where, value); + + return 0; +} + + +int pci_write_config_dword(struct pci_device *pci, unsigned int where, uint32_t value) +{ + dde_kit_pci_writel(PCI_BUS(pci->busdevfn), PCI_SLOT(pci->busdevfn), PCI_FUNC(pci->busdevfn), + where, value); + + return 0; +} + + +unsigned long pci_bar_start(struct pci_device *pci, unsigned int reg) +{ + /* + * XXX We do not check for 64-bit BARs here. + */ + + uint32_t val; + pci_read_config_dword(pci, reg, &val); + + if ((val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) + return val & PCI_BASE_ADDRESS_MEM_MASK; + else + return val & PCI_BASE_ADDRESS_IO_MASK; +} + + +/* drivers/bus/pci.c */ + +void adjust_pci_device ( struct pci_device *pci ) { + unsigned short new_command, pci_command = 0; + + pci_read_config_word(pci, PCI_COMMAND, &pci_command); + new_command = pci_command | PCI_COMMAND_MASTER | PCI_COMMAND_MEM | PCI_COMMAND_IO; + if (pci_command != new_command) { + LOG("PCI BIOS has not enabled device " FMT_BUSDEVFN "! " + "Updating PCI command %04x->%04x\n", PCI_BUS(pci->busdevfn), + PCI_SLOT(pci->busdevfn), PCI_FUNC (pci->busdevfn), + pci_command, new_command); + pci_write_config_word(pci, PCI_COMMAND, new_command); + } + + unsigned char pci_latency; + pci_read_config_byte ( pci, PCI_LATENCY_TIMER, &pci_latency); + if ( pci_latency < 32 ) { + LOG("PCI device " FMT_BUSDEVFN " latency timer is unreasonably " + "low at %d. Setting to 32.\n", PCI_BUS(pci->busdevfn), + PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ), + pci_latency ); + pci_write_config_byte ( pci, PCI_LATENCY_TIMER, 32); + } +} + + +/*********************** + ** Device management ** + ***********************/ + +int register_settings(struct settings *settings, struct settings *parent, + const char *name) +{ + return 0; +} + + +void unregister_settings(struct settings *settings) { } + + +void ref_increment(struct refcnt *refcnt) { } + + +void ref_decrement(struct refcnt *refcnt) { } diff --git a/dde_ipxe/src/lib/dde_ipxe/dde_support.cc b/dde_ipxe/src/lib/dde_ipxe/dde_support.cc new file mode 100644 index 000000000..a401e0399 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/dde_support.cc @@ -0,0 +1,86 @@ +/* + * \brief Functions not offered by Genode's DDE-kit + * \author Sebastian Sumpf + * \date 2010-10-21 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +} + +using namespace Genode; + +/******************************************* + ** Support for aligned memory allocation ** + *******************************************/ + +enum { BACKING_STORE_SIZE = 1024 * 1024 }; + + +Allocator_avl *allocator() +{ + static Allocator_avl _avl(env()->heap()); + return &_avl; +} + + +void __attribute__((constructor)) init() +{ + try { + Dataspace_capability ds_cap = env()->ram_session()->alloc(BACKING_STORE_SIZE); + addr_t base = (addr_t)env()->rm_session()->attach(ds_cap); + + /* add to allocator */ + allocator()->add_range(base, BACKING_STORE_SIZE); + + /* add to DDE-kit page tables */ + addr_t phys = Dataspace_client(ds_cap).phys_addr(); + dde_kit_pgtab_set_region_with_size((void *)base, phys, BACKING_STORE_SIZE); + } catch (...) { + PERR("Initialization of block memory failed!"); + } +} + + +extern "C" void *alloc_memblock(size_t size, size_t align) +{ + void *ptr; + allocator()->alloc_aligned(size, &ptr, log2(align)); + return ptr; +} + + +extern "C" void free_memblock(void *p, size_t size) +{ + allocator()->free(p, size); +} + + +/*********** + ** Timer ** + ***********/ + +extern "C" void timer2_udelay(unsigned long usecs) +{ + /* + * This function is called only once during rdtsc calibration (usecs will be + * 10000, see dde.c 'udelay'. We do not use DDE timers here, since Genode's + * timer connection is the precised one around. + */ + Timer::Connection timer; + timer.msleep(usecs / 1000); +} diff --git a/dde_ipxe/src/lib/dde_ipxe/dummies.c b/dde_ipxe/src/lib/dde_ipxe/dummies.c new file mode 100644 index 000000000..ec496966e --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/dummies.c @@ -0,0 +1,30 @@ +/* + * \brief DDE iPXE dummy implementations + * \author Christian Helmuth + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "local.h" + +int snprintf(char *buf, __SIZE_TYPE__ size, const char *fmt, ...) { TRACE; return 0; } + +void clear_settings() { TRACE; } +void netdev_settings_operations() { TRACE; } +void dbg_autocolourise(unsigned long id) { } +void dbg_decolourise() { } +void strerror() { TRACE; } +int strcmp(const char *s1, const char *s2) { TRACE; return 0; } + +/* for eepro100.c */ +void init_spi_bit_basher() { TRACE; } +void nvs_read() { TRACE; } +void threewire_detect_address_len() { TRACE; } +void threewire_read() { TRACE; } +void threewire_write() { TRACE; } diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/byteswap.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/byteswap.h new file mode 100644 index 000000000..f9e062300 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/byteswap.h @@ -0,0 +1 @@ +#include_next diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/compiler.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/compiler.h new file mode 100644 index 000000000..b6dc36ea1 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/compiler.h @@ -0,0 +1 @@ +#include_next diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/cpu.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/cpu.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/eltorito.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/eltorito.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/endian.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/endian.h new file mode 100644 index 000000000..6c660f7e2 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/endian.h @@ -0,0 +1 @@ +#include_next diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/errfile.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/errfile.h new file mode 100644 index 000000000..7cdaa750e --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/errfile.h @@ -0,0 +1 @@ +#include_next diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/io.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/io.h new file mode 100644 index 000000000..cd3b0c1d0 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/io.h @@ -0,0 +1 @@ +#include diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/nap.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/nap.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/pci_io.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/pci_io.h new file mode 100644 index 000000000..e0858af38 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/pci_io.h @@ -0,0 +1,3 @@ +#include /* some drivers do rely on this depency :-( */ + +#include diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/smbios.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/smbios.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/stdint.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/stdint.h new file mode 100644 index 000000000..ebdd8f3c2 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/stdint.h @@ -0,0 +1 @@ +#include_next diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/string.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/string.h new file mode 100644 index 000000000..64a5db17c --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/string.h @@ -0,0 +1 @@ +#include_next diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/timer.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/timer.h new file mode 100644 index 000000000..401ef0f08 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/timer.h @@ -0,0 +1,6 @@ +#include + +static inline unsigned long currticks ( void ) +{ + return __rdtsc_currticks(); +} diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/uaccess.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/uaccess.h new file mode 100644 index 000000000..cd3b0c1d0 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/bits/uaccess.h @@ -0,0 +1 @@ +#include diff --git a/dde_ipxe/src/lib/dde_ipxe/include/bits/umalloc.h b/dde_ipxe/src/lib/dde_ipxe/include/bits/umalloc.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/config/local/console.h b/dde_ipxe/src/lib/dde_ipxe/include/config/local/console.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/config/local/general.h b/dde_ipxe/src/lib/dde_ipxe/include/config/local/general.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/config/local/ioapi.h b/dde_ipxe/src/lib/dde_ipxe/include/config/local/ioapi.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/config/local/nap.h b/dde_ipxe/src/lib/dde_ipxe/include/config/local/nap.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/config/local/serial.h b/dde_ipxe/src/lib/dde_ipxe/include/config/local/serial.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/config/local/timer.h b/dde_ipxe/src/lib/dde_ipxe/include/config/local/timer.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/config/local/umalloc.h b/dde_ipxe/src/lib/dde_ipxe/include/config/local/umalloc.h new file mode 100644 index 000000000..e69de29bb diff --git a/dde_ipxe/src/lib/dde_ipxe/include/env_dde_kit.h b/dde_ipxe/src/lib/dde_ipxe/include/env_dde_kit.h new file mode 100644 index 000000000..5d7c9dc97 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/include/env_dde_kit.h @@ -0,0 +1,68 @@ +#ifndef __ENV_DDE_KIT_H__ +#define __ENV_DDE_KIT_H__ + +/* bits/io.h */ + +#include + +static inline uint8_t inb(volatile uint8_t *io_addr) +{ + return dde_kit_inb((dde_kit_addr_t) io_addr); +} + +static inline uint16_t inw(volatile uint16_t *io_addr) +{ + return dde_kit_inw((dde_kit_addr_t) io_addr); +} + +static inline uint32_t inl(volatile uint32_t *io_addr) +{ + return dde_kit_inl((dde_kit_addr_t) io_addr); +} + +static inline void outb(uint8_t data, volatile uint8_t *io_addr) +{ + dde_kit_outb((dde_kit_addr_t) io_addr, data); +} + +static inline void outw(uint16_t data, volatile uint16_t *io_addr) +{ + dde_kit_outw((dde_kit_addr_t) io_addr, data); +} + +static inline void outl(uint32_t data, volatile uint32_t *io_addr) +{ + dde_kit_outl((dde_kit_addr_t) io_addr, data); +} + + +static inline uint16_t readw(volatile uint16_t *io_addr) +{ + return *io_addr; +} + + +static inline uint32_t readl(volatile uint32_t *io_addr) +{ + return *io_addr; +} + + +static inline void writew(uint16_t data, volatile uint16_t *io_addr) +{ + *io_addr = data; +} + + +static inline void writel(uint32_t data, volatile uint32_t *io_addr) +{ + *io_addr = data; +} + + +static inline void mb(void) +{ + asm volatile ("lock; addl $0, 0(%%esp)" : : : "memory"); +} + +#endif /* __ENV_DDE_KIT_H__ */ diff --git a/dde_ipxe/src/lib/dde_ipxe/local.h b/dde_ipxe/src/lib/dde_ipxe/local.h new file mode 100644 index 000000000..71b144352 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/local.h @@ -0,0 +1,28 @@ +/* + * \brief DDE iPXE local helpers + * \author Christian Helmuth + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +#define FMT_BUSDEVFN "%02x:%02x.%x" + +#define LOG(fmt, ...) \ + do { \ + dde_kit_log(1, "\033[36m" fmt "\033[0m", ##__VA_ARGS__ ); \ + } while (0) + +#define TRACE dde_kit_printf("\033[35m%s not implemented\033[0m\n", __func__) + +#define ASSERT(x) dde_kit_assert(x) + +extern void slab_init(void); diff --git a/dde_ipxe/src/lib/dde_ipxe/nic.c b/dde_ipxe/src/lib/dde_ipxe/nic.c new file mode 100644 index 000000000..6d7213db0 --- /dev/null +++ b/dde_ipxe/src/lib/dde_ipxe/nic.c @@ -0,0 +1,337 @@ +/* + * \brief DDE iPXE NIC API implementation + * \author Christian Helmuth + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* DDE kit */ +#include +#include +#include +#include +#include +#include + +/* iPXE */ +#include +#include +#include +#include + +#include +#include "local.h" + +/** + * DDE iPXE mutual exclusion lock + */ +static struct dde_kit_lock *ipxe_lock; + +#define ENTER dde_kit_lock_lock(ipxe_lock) +#define LEAVE dde_kit_lock_unlock(ipxe_lock) + +/** + * Bottom-half activation semaphore + */ +static struct dde_kit_sem *bh_sema; + +/** + * Network device driven by iPXE + */ +static struct net_device *net_dev; + +/** + * RX callback function pointer + */ +static dde_ipxe_nic_rx_cb rx_callback; + +/** + * Known iPXE driver structures (located in the driver binaries) + */ +extern struct pci_driver + e1000_82540_driver, e1000_82541_driver, e1000_82542_driver, e1000_82543_driver, + e1000e_80003es2lan_driver, e1000e_82571_driver, e1000e_ich8lan_driver, +// ifec_driver, +// rtl8139_driver, +// nepci_driver, + pcnet32_driver; + +/** + * Driver database (used for probing) + */ +static struct pci_driver *pci_drivers[] = { + &e1000_82540_driver, &e1000_82541_driver, &e1000_82542_driver, &e1000_82543_driver, + &e1000e_80003es2lan_driver, &e1000e_82571_driver, &e1000e_ich8lan_driver, +// &ifec_driver, +// &rtl8139_driver, +// &nepci_driver, + &pcnet32_driver +}; + + +/** + * Update BARs of PCI device + */ +static void pci_read_bases(struct pci_device *pci_dev) +{ + uint32_t bar; + int reg; + + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + pci_read_config_dword(pci_dev, reg, &bar); + if (bar & PCI_BASE_ADDRESS_SPACE_IO) { + if (!pci_dev->ioaddr) { + pci_dev->ioaddr = bar & PCI_BASE_ADDRESS_IO_MASK; + + dde_kit_addr_t base = bar & PCI_BASE_ADDRESS_IO_MASK; + dde_kit_size_t size = pci_bar_size(pci_dev, reg); + dde_kit_request_io(base, size); + } + } else { + if (!pci_dev->membase) + pci_dev->membase = bar & PCI_BASE_ADDRESS_MEM_MASK; + /* Skip next BAR if 64-bit */ + if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) + reg += 4; + } + } +} + + +/** + * Probe one PCI device + */ +static int probe_pci_device(struct pci_device *pci_dev) +{ + int j; + for (j = 0; j < sizeof(pci_drivers)/sizeof(*pci_drivers); j++) { + struct pci_driver *driver = pci_drivers[j]; + + int i; + for (i = 0; i < driver->id_count; i++) { + struct pci_device_id *id = &driver->ids[i]; + if ((id->vendor != PCI_ANY_ID) && (id->vendor != pci_dev->vendor)) + continue; + if ((id->device != PCI_ANY_ID) && (id->device != pci_dev->device)) + continue; + pci_set_driver(pci_dev, driver, id); + + LOG("using driver %s", pci_dev->id->name); + int ret = driver->probe(pci_dev); + if (ret != 0) { + LOG("probe failed for %s", pci_dev->id->name); + continue; + } + return 0; + } + } + + LOG("no driver found"); + return -1; +} + + +enum { NO_DEVICE_FOUND = ~0U }; + +/** + * Scan the PCI bus + * + * \return PCI location of NIC found; NO_DEVICE_FOUND otherwise + */ +static unsigned scan_pci(void) +{ + int ret, bus = 0, dev = 0, fun = 0; + for (ret = dde_kit_pci_first_device(&bus, &dev, &fun); + ret == 0; + ret = dde_kit_pci_next_device(&bus, &dev, &fun)) { + + dde_kit_uint32_t class_code; + dde_kit_pci_readl(bus, dev, fun, PCI_CLASS_REVISION, &class_code); + class_code >>= 8; + if (PCI_BASE_CLASS(class_code) != PCI_BASE_CLASS_NETWORK) + continue; + + dde_kit_uint16_t vendor, device; + dde_kit_pci_readw(bus, dev, fun, PCI_VENDOR_ID, &vendor); + dde_kit_pci_readw(bus, dev, fun, PCI_DEVICE_ID, &device); + dde_kit_uint8_t rev, irq; + dde_kit_pci_readb(bus, dev, fun, PCI_REVISION_ID, &rev); + dde_kit_pci_readb(bus, dev, fun, PCI_INTERRUPT_LINE, &irq); + LOG("Found: " FMT_BUSDEVFN " %04x:%04x (rev %02x) IRQ %02x", + bus, dev, fun, vendor, device, rev, irq); + + struct pci_device *pci_dev = zalloc(sizeof(*pci_dev)); + ASSERT(pci_dev != 0); + + pci_dev->busdevfn = PCI_BUSDEVFN(bus, dev, fun); + pci_dev->vendor = vendor; + pci_dev->device = device; + pci_dev->class = class_code; + pci_dev->irq = irq; + + pci_read_bases(pci_dev); + + pci_dev->dev.desc.bus_type = BUS_TYPE_PCI; + pci_dev->dev.desc.location = pci_dev->busdevfn; + pci_dev->dev.desc.vendor = pci_dev->vendor; + pci_dev->dev.desc.device = pci_dev->device; + pci_dev->dev.desc.class = pci_dev->class; + pci_dev->dev.desc.ioaddr = pci_dev->ioaddr; + pci_dev->dev.desc.irq = pci_dev->irq; + + /* we found our device -> break loop */ + if (!probe_pci_device(pci_dev)) + return pci_dev->dev.desc.location; + + /* free device if no driver was found */ + free(pci_dev); + } + + return NO_DEVICE_FOUND; +} + + +/** + * IRQ handler registered at DDE kit + */ +static void irq_handler(void *p) +{ + ENTER; + + netdev_poll(net_dev); + dde_kit_sem_up(bh_sema); + + LEAVE; +} + + +/** + * Bottom-half handler executed in separate thread + * + * Calls RX callback if appropriate. + */ +static void bh_handler(void *p) +{ + while (1) { + dde_kit_sem_down(bh_sema); + + ENTER; + + struct io_buffer *iobuf; + while ((iobuf = netdev_rx_dequeue(net_dev))) { + LEAVE; + if (rx_callback) + rx_callback(1, iobuf->data, iob_len(iobuf)); + ENTER; + free_iob(iobuf); + } + + LEAVE; + } +} + + +/************************ + ** API implementation ** + ************************/ + +dde_ipxe_nic_rx_cb dde_ipxe_nic_register_rx_callback(dde_ipxe_nic_rx_cb cb) +{ + ENTER; + + dde_ipxe_nic_rx_cb old = rx_callback; + rx_callback = cb; + + LEAVE; + return old; +} + + +int dde_ipxe_nic_tx(unsigned if_index, const char *packet, unsigned packet_len) +{ + if (if_index != 1) + return -1; + + ENTER; + + struct io_buffer *iobuf = alloc_iob(packet_len); + + LEAVE; + memcpy(iob_put(iobuf, packet_len), packet, packet_len); + ENTER; + + netdev_poll(net_dev); + netdev_tx(net_dev, iob_disown(iobuf)); + + LEAVE; + return 0; +} + + +int dde_ipxe_nic_get_mac_addr(unsigned if_index, char *out_mac_addr) +{ + if (if_index != 1) + return -1; + + ENTER; + + out_mac_addr[0] = net_dev->hw_addr[0]; + out_mac_addr[1] = net_dev->hw_addr[1]; + out_mac_addr[2] = net_dev->hw_addr[2]; + out_mac_addr[3] = net_dev->hw_addr[3]; + out_mac_addr[4] = net_dev->hw_addr[4]; + out_mac_addr[5] = net_dev->hw_addr[5]; + + LEAVE; + return 0; +} + + +int dde_ipxe_nic_init(void) +{ + dde_kit_init(); + dde_kit_timer_init(0, 0); + dde_kit_pci_init(); + dde_kit_lock_init(&ipxe_lock); + + slab_init(); + + ENTER; + + /* scan all pci devices and drivers */ + unsigned location = scan_pci(); + if (location == NO_DEVICE_FOUND) + return 0; + + /* find and open iPXE NIC device */ + net_dev = find_netdev_by_location(BUS_TYPE_PCI, location); + if (netdev_open(net_dev)) { + LOG("opening device " FMT_BUSDEVFN " failed", + PCI_BUS(net_dev->dev->desc.location), + PCI_SLOT(net_dev->dev->desc.location), + PCI_FUNC(net_dev->dev->desc.location)); + return 0; + } + + /* initialize IRQ handler and enable interrupt/bottom-half handling */ + bh_sema = dde_kit_sem_init(0); + dde_kit_thread_create(bh_handler, 0, "bh_handler"); + int err = dde_kit_interrupt_attach(net_dev->dev->desc.irq, 0, + 0, irq_handler, 0); + if (err) { + LOG("attaching to IRQ %02x failed", net_dev->dev->desc.irq); + return 0; + } + netdev_irq(net_dev, 1); + + LEAVE; + + /* always report 1 device was found */ + return 1; +} diff --git a/demo/doc/demo.txt b/demo/doc/demo.txt new file mode 100644 index 000000000..53f41a0b2 --- /dev/null +++ b/demo/doc/demo.txt @@ -0,0 +1,216 @@ + + Introduction to Genode + + Genode Labs + +[image img/genode_logo] + +Genode is a construction kit for building special-purpose operating systems +out of a number of components such as device drivers, protocol +stacks, and applications. Those components are organized using only a few +yet powerful architectual prinicples, and thereby, allow for the +composition of a wide range of different systems. The live CD is meant to +showcase how far the concept scales as of today. + +The following introduction will provide you with hands-on experience with +the basics of Genode: + +* The creation and destruction of single processes as well as arbitrarily + complex sub systems +* The trusted-path facility of the Nitpicker secure GUI +* The assignment of resource quotas to sub systems +* The multiple instantiation of services +* The usage of run-time adaptable policy for routing client requests to + different services + + +The launchpad application starter +################################# + +[image img/launchpad 50%] Main window of the launchpad application + starter. + +Figure [img/launchpad] shows the main window of the launchpad application. It +consists of three areas. The upper area contains status information about +launchpad itself. The available memory quota is presented by a grey-colored +bar. The middle area of the window contains the list of available applications +that can be started by clicking on the application's name. Before starting an +application, the user can define the amount of memory quota to donate to the +new application by adjusting the red bar using the mouse. +[exec-once:launchpad(22M) - Start the launchpad by clicking on this link...] + +For a first test, you may set the memory quota of the program named scout to +10MB and then click its name. Thereupon, another instance of the scout text +browser will be started and the lower area of launchpad becomes populated with +status information about launchpad's children. Currently, launchpad has scout +as its only child. For each child, its name, its memory quota, and a kill +button are presented. After having started scout, you will further notice a +change of launchpad's own status information as the memory quota spent for +scout is not directly available to launchpad anymore. + +[image img/setup] Illustration of the system setup after having + started the scout tutorial browser. + +In Figure [img/setup], you see an illustration of the current setup (slightly +simplified, leaving out the main menu and the other parts of the live CD). At +the very bottom, there are the kernel, core, and init. Init has started the +framebuffer driver, the timer driver, the nitpicker GUI server, and launchpad +as it children. Launchpad, in turn, has started the second instance of scout as +its only child. You can get a further idea about the relationship between the +applications by pressing the 'ScrLock' key, which gets especially handled by +the nitpicker GUI server. We call this key the X-ray key because it makes the +identity of each window on screen visible to the user. Each screen region gets +labeled by its chain of parents and their grandparents respectively. During the +walk through the demo scenario, you may press the X-ray key at any time to make +the parent-child relationships visible on screen. + +By pressing the kill button (labeled with 'x') of the scout child in +launchpad's window, scout will disappear and launchpad regains its original +memory quota. Although killing a process may sound like a simple thing to do, +it is worthwhile to mention that scout was using a number of services, for +example core's LOG service, the nitpicker GUI service, and the timer service. +While using these services, scout made portions of its own memory quota +available to them. When scout was killed by launchpad, all those relationships +were gracefully reverted such that there is no resource leakage. + + +Recursive system structure +########################## + +[image img/x-ray] A second instance of launchad is used + to start the 'testnit' program, which manages three + colored windows. The identity of each screen regions + is unveiled by the X-ray mode of the nitpicker GUI + server. + +Thanks to the recursive structure of Genode, the mechanisms +that function for a single application are also applicable to +whole sub systems. +As a test, you may configure the launchpad application +entry within the launchpad window to 15MB and start +another instance of launchpad. +A new launchpad window will appear. Apart from the status +information at the upper part of its window, it looks +completely identical to the first instance. +You may notice that the displayed available quota of the +second launchpad instance is lower then the 15MB. The +difference corresponds to the application's static memory +usage including the BSS segment and the double-buffer +backing store. +With the new instance, you may start further applications, +for example by clicking on 'testnit.' +To distinguish the different instances of the applications +on screen, the X-ray key becomes handy again. +Figure [img/x-ray] shows a screenshot of the described setup +in X-ray mode. +Now, after creating a whole hierarchy of applications, +you can try killing the whole tree at once by clicking +the kill button of the launchpad entry in the original +launchpad window. +You will notice that whole sub system gets properly +destructed and the original system state is regained. + + +The flexibility of nested policies +################################## + +Beside providing the ability to construct and destruct +hierarchically structured sub systems, the recursive +system structure allows for an extremely flexible +definition and management of system policies that can +be implanted into each parent. +As an example, launchpad has a simple built-in policy of how +children are connected to services. + +If a child requests +a service, launchpad looks if such a service is provided +by any of the other children and, if so, a connection +gets established. If the service is not offered by any child, +launchpad delegates the request to its parent. +For example, a request for the 'LOG' service will always +end up at core, which implements the service by the +means of terminal (or kernel debug) output. +By starting a child that offers the same service interface, +however, we can shadow core's 'LOG' service by an alternative +implementation. +You can try this out by first starting 'testnit' and +observing its log output at the terminal window. When +started, 'testnit' tells us some status information. +By further starting the program called 'nitlog,' we create +a new 'LOG' service as a child of launchpad. On screen, this +application appears just as a black window that can be +dragged to any screen position with the mouse. +When now starting a new instance of 'testnit', launchpad +will resolve the request for the 'LOG' service by establishing +a connection to 'nitlog' instead of propagating the request +to its parent. Consequently, we can now observe the status +output of the second 'testnit' instance inside the 'nitlog' +window. + +The same methodology can be applied to arbitrarily complex +services. For example, you can create a new instance of +the framebuffer service by starting the 'liquid_fb' application. +This application provides the framebuffer service and, +in turn, uses the nitpicker GUI server to get displayed on +screen. Because any new requests for a framebuffer will now be +served by the 'liquid_fb' application, we can start another +instance of nitpicker. This instance uses 'liquid_fb' as its +graphics back end and, in turn, provides the GUI service. +Now, when starting another instance of scout, the new scout +window will appear within 'liquid_fb' too (Figure [img/liquid_fb]). + +[image img/liquid_fb] + Executing multiple instances of the nitpicker GUI server + in a nested way. + +The extremely simple example policy implemented in launchpad +in combination with the recursive system structure of Genode +already provides a wealth of flexibility without the need +to recompile or reconfigure any application. +The policy implemented and enforced by a parent may +also deny services for its children or impose other restrictions. +For example, the window labels presented in X-ray mode are +successively defined by all parents and grandparents that +mediate the request of an application to the GUI service. +The scout window as the parent of launchpad imposes its +policy of labeling the GUI session with the label _"launchpad"_. +Init as the parent of scout again overrides this label +with the name of its immediate child from which the GUI request +comes from. Hence the label becomes _"scout -> launchpad"_. + + +Where to go from here? +###################### + +Although this little demonstration scratches only the surface of +Genode, we hope that the power of its underlying design becomes +apparent. The most distinctive property of Genode, however, is its +extremely low complexity. The functionality of the complete demo +scenario is implemented in less than 20,000 lines of source code +(LOC), including the GUI and the demo applications. As a point of +reference, when relying on libpng for decompressing the images as seen +in the text browser, this number doubles. In fact, the complete base +OS framework accounts for less source-code complexity than the code +needed for decoding the PNG images. To these numbers, the complexity +of the used underlying kernel must be added, for example 10-20 KLOC +for an L4 microkernel (or far more than 500 KLOC when relying on the +Linux kernel). In combination with a microkernel, Genode enables the +implementation of security-sensitive applications with a trusted +computing base (TCB) of some thousands rather than millions of lines +of code. If using a hypervisor as kernel for Genode, this advantage +can further be combined with compatibility to existing applications +executed on virtual machines. + +More details, architectural and technical documents, our road +map, and the complete source code are available at [http://genode.org]. + +The development of the Genode OS Framework is conducted as +an open-source community project, coordinated by Genode Labs, +a company founded by the original authors of Genode. +If you are interested in supporting our project through +participation or funding, please consider joining our +community ([http://genode.org]) or contact Genode Labs +([http://www.genode-labs.com]). + +! info@genode-labs.com + diff --git a/demo/doc/img/genode_logo.png b/demo/doc/img/genode_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b1290f4d535a50af23709b5081510ee18fe194f7 GIT binary patch literal 17091 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 literal 0 HcmV?d00001 diff --git a/demo/doc/img/launchpad.png b/demo/doc/img/launchpad.png new file mode 100644 index 0000000000000000000000000000000000000000..ad874d193c8f5eb9bfd21feeebf5601f36304050 GIT binary patch literal 58017 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 =Jb|g_w92$ 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 TLgs1)1_lOCS3j3^P6H=O_8aV?JUmt{e-&C8 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

+CNe&IV$Jzu>;BXQzGO~4^k&!JJ-4Sa7Tmp; zXI=EGTpe0fwgkMx_2Kt8S1r8)SsU;vHNGWrRa}%uiyU-585Y79Xfeo`(g=s@db>gEsE~{ zRIjXF@q7|vfaAZ^336iojhq5vo_w-rA~q>%JQn%m|E@oG;+_k~+ui={bXj5gNxv!m zr9(WY0n;hd&Oet;4q10+9e=j$Q{D5siA*1#znFb&m$t78M|Z=lts7^vU0&)pO{8@G z(Fqe@)X%%z@n-6t_pN^mV%O`5|8ljSbILmXIiO={Gy3_WI^8ccq=gS%l zI}e4Zgmfin`@cJJq+Nph=hr7GhZ#P(C&ZtLUwF!OrtD&gC+Zs~o0Tuw(0{``;&iHo zs&}v?lcBl9ltp{ySPAP-Dz#4I3@qKB&3B-3QOgR6_e(9cf7;COo6zz~uIS$6-*b~3 zm%4DSex7B>|MdCUuOGcNw41L8sa*1ZBoZ`lGgFsJ?9s^!Jy?uYg|EJ;dHPvv*1WJ8 z6-PfWx9UvuY=TToz5X_>@!wCz_cs-{{{GMY>*L#p|6|LRJ!hS`d=iJg)%D5Wzm|Tl z=wdRT?ptTN`Qf1l`=`bHdiZ!r7~hN7=K5o^r;Dtq@|bdB|Dw;v-3e=!FdbjezA|P; zn8`0e(UiUGBZR!ay$aZ+ec{XV87z`(gN!93_G&$e&%CO+cCWfvxa$Kq5!08ZN2VRE zFBFV4ObF)i>nmA2$y{~L@~_WNm1}rAq!=-k%4^26oDNRWo$uhZqnSzdpYZ#qCTXoP zLeDHbUGIlppUnPKR4T9cr$OvohcBt;;HvUWdAs+3wAJvdZD#M0N2Uhxi^@-E#i=(cA6( zQB>+D^d>3aCnvN@n7VV{Xqr?*HG<9bI5uQ`>fx#um5K|o{b5p z)RKAl@>BiK^Li1hZ5wZ}`~G{LUgojo&+>EOf8Iv0#RoIaF#h`Z$FEth=W#dbU;DA& z`M{5`aMzpl>+Rwt*ZBXfal8HOt@dQb&76(Je`We=l+I|pzdPH&T8;UiINLw&s=)mU zfz^8>d|22VmVPU8_@g>ESkEi8sByCj^HNv8g41e=pQc`G?__+yzhDvrm#5>(pXui` zPex9dKl_5`+4`86gGkEV zTcS(mFa7y^*0cRVhGnTjf9C)H1#{xq1g zIVyoK@Kawvg^)pXt=O4+k$2ReFW{3BE;jWRz4bbG=lc zG;z{*UdBbM3#a%`=KQL5wxTmvC+ZRdYtJm}51UU-dwHHEvtdoVKjY8z+Dj`k5`D^S zw;s95d7gXgv7Wb%T!Pm*CsY|VEHX*0`lIk!r6J{6hf0svA3w$a4ks_nh@Lzl?dJ;1 z!{IqAr8O2!m^#PlL^e-j&Ab`AmfbKBJG!@f*;1Q@AKhIa&u9Iv#o1uAvf$j;Snwps z-p~jCjc5Ho8@MT6;Lq-a6?wnZng2#L_@%tc4fSS|S$?1U;EX@w_p7&U?zXnt_``bp z(sO<+dzyE%@4a02eBIYe3199!IqmHu9Dj(bc&}!`3Xn|NJWa+f{sWVtwlz zc99;|trzv5B{wKOv)g$?a}q<0`-#{K%`IORr!aV|*w^;wy2<|&>z2Fx^HjWUde%Oi zhasDD^Q<&$HLfE~mhNq7o6Usp%?K$zzj>eh%`-bDq^WI@Vx7le^!0$tU!MzK>J?Aw zPpIcOYv`XbEwgHdkMb9pvp=4cUpX}Y)pdiX;uGvAXPxZYle5&1>*f3qO&`BwRhG4p ztdet@ig}Y}Ps!k%_01+g%rX7p`+)3ft+PzUPvQgDWHzmuKh=zfhhgdIb&CIE)Pk05 zXEa{$*;l&-`a8IkDmNnpsOYHkSNW&tIVK6X6`%f6E~$ z_to@Ie2aQgr}a#!-J;h&)A8Rha^sCi!KD+kr)6YkFt%29Wyc=BIS~Mr?CeE96<%jXv>_5hqv%IdH3ANgp z2bvYAZhvd8Cp|s*?c?{2_s+%e_AEAjEf~uFe>!VRi0$;14(C`am&{43kJ!D?co=|6!5;kDSyA{e)so@qGvWSiUsE$6W_+T-|@qLzAgW}oBt=@bzjgn<@AF4 zy8A9CnHw<}S!HdszpzBkVunBKFZ)NkcK7{hzbO8t&`uE8v_Qxb#Zb{oYEkn#P zuJYAV#gx91RY!ku`j*UlqqpLv@T~fYOo1or-`p3yY&JWq>E$UQ9SNfokxJ_t*YX}K zOgwW%>+O>hpEJ+yzoHU;R>OI=pQpHyr{g>o?zf=||3kbqyO#Ln%oh$74%xQO<)83x zk1HM1Z!(_KnmfU7h07EB?}x$umNg5l_H}aQw`bY&eXf3H8GL5%X8)&`?(TncG$Io`f3Nu`P%bv4Xw*K# z!skH8f1R$mc3tQ0t}GE>dF#z&slQI2*Y`3!j;dn%+0WAuV{Q46*I?yOb}!`vJ8m3W z^UP@fGbv$3e?Fg?QGsXW>a{%TmpnKZvq%iP*jj>XE(a-Xrt?pZ2xv*vgGd^J1!Pv^OB`X?g2k9=*JIg9_?r61iX zQ>HJn5DsLT`0?&R4z}4oaWSe4|9Nh2<$2&=P|o+8Im6T7eZ;XmqYLvmyz>6-I`;Vf zw&m5=3iTSwg)jWlU;VQ-_5SY2^&9{82hYDQ{C4-2^DJ-eb$G$601af=N@ClW&#dJ= z%#h3zw%WAEP~3OJdjGKrv81uY5o2u z5zn`a?_bnDVWE?;{F!w@nf4FY=t?joSWMA!Hd^E+FV^Oh#87c|zv^sOp2kXTanIBb z=dbNLntx;eR)b>)qE?&!n=?UCSdzhSex{E@j}}`)BT|K!tC%+JuSQ+P z%t?1JCz^ldgW6=5;Bntl?Q0$0IY5s>+#jgkxC5(n=K%=%tto zAFD5J^pUxovzn82rbxg^cE6HWi)sTheIe9gEY5G{YE%uwY^TnT~ zol94kd#w2^_sVw4uF^}Kswb6Jv7XENml%1i@K%QZv@V`A6E8;1@3GC_J7NF)_-32u zMLjQH$^P8qV#+VxY<0MO=bY`mY3q)DHf7oNRb~6vGuuxTY`)HNz!cO^D7Ri_y`}!v z;@8$D`%Uyu+03i=d8k>#s1&Kfa&MCC2db<-S_^!j)zU{(dR9tHQ(;l#V|c!4fp=)ZMS>b_9&h6 z7FFl(ESRKu=JUlvyKS>yKKkvT?$@67FG@++!_i)Kik%wsqwtj+eXE!L_!N{R`gaS@ zFUyagCpvzrE1V*!k=m`rmS*z*Qm347-v1T5b(Je+&aj@|$-UEQKg-@~J$<$Frtj`= z`lhw|;=l7N=gxioc4d)Xu>EP*TD~n|$7U_L?4S9sd)NJ*lSdrR_Gt5GtuAizN&E0p z#hD|odr7Oq+bMw(;gjs<1TKzzy+$>7@2rH02gSI{dM<2G`Bn8R$mGmpk(RJp9u370 zmn@32;-)pOYF+!TyH#^u(YEQuo@;BhZv7X@etqlXwxH*+tf!yYiN3V8^O(-KgzF3A zh95nX@?FxJri%y0#pzEL-@uol`D@makB4sVJi*1e(W=F#ZdK*|CAE@WLhVk>ai0Exl2Fhf7$}62!A)0Ki^=_V4EcLptjFbOVD8JT$eZE%Y_+h zQZ6+4z3Ub$NfdgpeeN7NbLlty)nEP!`Ce|2lsJCT|I}r*VBgCLJ7XSYerHMRP&qL3 zQpah>6*Fyp99yQlF81k3eJ!NGbIgE8!c9HX@L0l3RjYa5syCE&uiE!k)^|qZME5t} zf4_0`$(Y3!d;L*?MS_g(tYZsaTWv^v^UYu3qvTHqxweCMCmU~D_c~#1>((-}gC^IV zR$mp`y?pNVZKuu~_87j{Eq83k(fb+hHb0Bm8`izOuE-}M{6tx;iAiF$nP zrsywQ7G?&uy_0mGGHgAnao&L8M8i!(Mhm6?Hl2U8*%+#O@}3!7eGsK!pfiIj@qgu} zRToOmDf~0l%3Z<}{)FSrZCe%Qh~tW%FX!@Vo@VyF#2rwZAHec@qkT}~^Z<9R>@^*o zLh)M__B}hb_FlTyB=J`k##cUlk_h3Q!8M<4;uWsHdwd(=%;~#E(^zeGJ{Zo`fB1 zNxB%m#8K|FnU~3$897=)I~>IVybqju&g9#pe{uiY6Pf2uG}j!ycQ-zyTJZ0cRZnhJ zHUw{#^3~OxR+%D_>z^8=IDdT>kNVUXQ_ko%|F|>T>DT4eyYESD?tRhwr*r*nP&uNv z+t`us!Ubs+#=aPqKd-+^_4h~T$eG{B+rFX9<+4e{`uu{=*Ju9-?fh~7PwCn?#s`JF zkDjgSJ^NT$?%(@r`71`h_8otFHyePYE5|dmfY7G8H>*f z@rdO}{$9@fYbp1X`DOk$a?B#uUX$Z2oL_k(hav6G-Mb5Oqj=gMmS~+6;E|MRJN#2l z)V1JV!uGp~62~LfdDT@t5uAOPt!&GV$ong@-|m*nz5M9oN5S4gPNi&}Hplta(PDqk z*gn|3uQ=`PhKgIZE6?YeNgEj3-K%xE|E=PV{k$_0GmkA;$o4z&zfz>{LVnRL*E)n2 zt!dV4UR@Iytt1+$q{y!`G3I~viTVyB&sRA!({0#=jFO7q?q>)Lw7iotH#KC=xrt9$ zcK+Mxl-*#iEwutR~t-YN>pZL}0yJ)W#jL2NJV#X)eNKRML^TIW+jKxB4G6$}$ zlb>|ul-kK@C1NGtPx-Okd#FD3#;MKoHz_5_S)@Of+?2#Ux9sP1k4le4&l)#dwW%7` z-}X!nUG(`{NbBzZS4=FC?tnv*3Se;1r@Uw`cES>Anj?;e(DUVd13TmS8oEC(+C zuQ~MhFK7>R%fIt{cV+(BJvcr6{Esg$g{PesZA&~bef|8o=l;cCs(bf-|H62^k1roD z-|Ti+^VR>ue{Fq|lM)i%u6V_Bp0#(*#W(zi6}=f$*dN^b7bpH_zE|L!&vicwo!NHZ z<;!VIl-bCW!C{=h@z}y@imSrSGY2es-Zkb39ol&R{YA4~=dJg%?0B$0bK~u|8}8<9 zjJq!~KXKc~---=t&JnudH{@lw@_G&y?fmjQRd#lRVNx8-*Kt?U1L`JN%`uQ+MH-)l3aHk7_$bY@#D zd2y@kx8Hk{4JuL=XG%3RCQ4j1%S~}MV7?-dcisHP+j0s1b-LGHm!^2{NH)G^xAD`C z${y~`sry}S@7iV|!zT0Xch;oU40D!EsPEov^1q95!t0flv%FTlTod*A{g(Ajj0Jtq z=1;6Ue%j>!PPWZrHBA*l3)ivz+{(IZM%tREpLzdpI^Z%-J+-srPLICIzE#~1|NO~h zYMqk9HqEQ#MQzfoTl3}p16vl}WLo~}+Vw3@iyq3%m()67pqa78q)6_>Gu1;=mc=Tx z8&*6tUY2vJ^n7E=B1rmw#`q)z;C!t}f4CwU2>S(y&I9(%EQvbNG9#}ea) zoSLgQ481<{i@d0s7?Bc^!W_fnbIMp}(;Lq8AjUHbo@DXt5{BGeHp9$H^P5#}iQ=aUwA*w4&C1hnB!x^usDUnXIf7W>%sekt~sCpjjvd(WC z5hgon>DT$cwL33av_1AU zldpx?(Z7FfckR}#fHPtSOfQxctj`6#_Pt}AE&IE`V!``eCt191 zZ{4wXXYccGZ~sZ(+SdELR)Rl5Z+eqm?&`N2?B5vnDeqe6oNkq~J=!E!Sp3v66TLu& z2d}5==V_+u%9-&;eAAAY|AnK`?f&<5x^nL>n)v?t{`Gdaf>(&d>#tIeEgrp|zW&8@ z+iR=0W_Qn7%h+cXoB1zfM|E-R=Xlp(&dYAepcv76l4i6GU$} zwM=Kcu4;by?PTS@0X~|ULMv_AY*sI7JC?3io7ty)v@dY(1g+2W_xsOLWzK#!i{-D) z)Z|1ypOdEl9{yYD#d>7&nbkFG#GbX>+!HY6<(UZPQ|}c|pH=C#KRRhkuvC4=ldWe> z{!jYpyu*m$pX9b|$piCqCO^&l*3R7a{_tv_Pg&6%d!Ae?o$JnZUgNrY$$#(0TkY@H zUekWV>@=g2#pdnzh_!b=`o~^>cHjKQ`|nCW#C19kGoF6BYDb&Q(%5y=t~S{JxAd9x z(>^7ksU|^6X*xs3oz=guhx`wGe6KjQ{LVeM^{v|-D?+Us%YPmhUO)YBa#`(`Z0j}G zS9ct_eNi`W{=9ELzP*)x_x^q2&0PwXSA^9LJ=dTAaYH48;KO&1=lbiou6?-9#jFnEqnc~yqbN2w_nfW7e3ej)K=ZGOS-B5*jCzIy*cx@n1c9*_65IB zKi7XZ!>;1|?FE_AHnQ^)q$Y3GU!DE;%TwWZ^6wm7epGzF_*MFnhGT|(`MyO-!5QqQ z{fr(*^6g$e_xpl`dR@E99rqufp00nRb=!{J%oBIdOO$a?+j=x@x8r^5dZtf3yvaU3 zI$L62FVXVt){oz}WM}hE2@RI6gL`XqPn8LJB`-TO!C&vqs?rOQyB965y0WNdTFa}= z3~Q_QI2``oz3>2!!4v_jbyJ?4YTi1zbh>)aX|5;JPp8c0(OI?hf5hUsp=&d8)TgKw z#i*62MY6?&{lf$S(QA z`HyQ(94-=E8mfA?Xu-aq<95zxSLaUOUYq;B>sQ|6=ROXRhiycoOlAbNO`7v>YuA#l zhP#LS)Y=YP9zP`;k{;f&?S%Ia(c{dku4<~?Zcm$Gbn)(d+lKQiS>!hx+z~!0bZx_y zq_zobE{3~JnYK16`HAV>=kMcnCTN~wy*9aIRqc}c!$+Fh)L*wUc^z`PAjhDTJ9E?g zq<5~pcUjI}dN9T9!5!;E_J2Io(n2mVW!-7}xolPl?}_r#)akx+*ZhurXD;wf-#2FY zvpCl|9Zvj5$eX-~g;u^rG)-RbzNz|qq7B!hwK+5LvRYu3*0h_nvdAM9;pV`um1-#@u8 z3v*^%?Ax(>_uSYieskx~=_qCqn87xS?=i#a9yZMl-K8@))%VSvmei&Z!DxAFTV~h$ zt2gGoy7_UHoWVl=Jq6jb*k+$)lb$d5|I5e8i+BDmlReJp`#R%A&drO_kCX0PJk?&P zA=mVC+wHdrv)E4G3k^C^%j9(7PMI!S^t*fsHusO|T^jo`d2jp>a#+0h^7HPK>+kPN z+LC-HeEXXV+dEeZF+IDs<)2-fQP9(C<$H~;O|CUiVZL=C?83UU4>vFVF#AT1*^BAt zYCams#1v`P2Djk~($1jnEp?)rZ$ zPPezOI-7NHnbV(%ng7&Z&J+!Qt@&hs)c**bZ$)qGS%2!sKc85?GWvPf#(A?mc9i7m z-&vI1cv)g{@|E(siddkHpdG_v!(D~nI{hxI$Ub4~4Wv=(+Ba33Y+Mm|k z|NH-Db@U65NvghIW|y<@m=qW&Bs6dfsYQVAz+mEL5#!(zt5|T5nN`{*V8en13l5T3DS;u@_F5Y!V@Tmbv!L@$J74K=US*G8^Kts;m>>Kp4DxdyYeFyjroa`(SIViTGCY|cx?Kh96dzWV&~f-WDuoigGK4~lBkI1GZT)?HiO zdUO-Bn#JAHJd1DhbAPBl;tu{EvhUi8@^9y@Tlk!1jxA}vK2x%a^-k^1!|&tkk8ihE zVNQs#WM;LovD@{a;Z}j(4rZOd^QGr+|9?I1^qmt5Z)UJFnwU;Ky=+74ml`>yfbD$u zHarMcsQFpcuE_Udx9Bc`4}TxlUF!b+;qR*jIL+&sAr_cC^RzBtxieQICtagLG$ zZ|~~%9nLU#DJfDG+rIp{`qN$V9aEkk%(7MEDTy^NySn^bZIWDqX%jOiM^#PTn)g3n zI=i`)EIfSouB_Q@-d`7&8d@ z(%;wLop6|v_g``6kN*F1J0b5Ozx3jAnYWv#hs~{hTXtY)n#`7$|7H~` zUS`z#wTIbd#aX?H(!MXs`f_JEuAW@G?%Dmak3QjN&o<_lY*Y08lM#2>jxY7%mDPum zm>56p_jtx+StM2#^G3X9`dzb-jRg-L%}%^&vZrlEc1r{K!Ozsl5i4X4)J zg{JE>-(*D}a5iR<3Aiyayfc2@-T6D&7KCK~jlR8vDd~qoo~pT5;g(~2Hcj1d@Y$^E z=hvS!K3@FY{q1Foe|r)*d=%Wjt=eJ0@K5ZxE7OPI!^TP)R|5`&X~+Kgti4_Hb9jl- zwe>qQqoV!@-#M}W@4B+9^_6v!Uk{%{zuxBib`U$wve_nzIC zYc~7Y>p8Czs!sjTZ)R}NQ!2S5eO<)hv-8^xNUXha=9M-Z*~-!=!04?H68#t@lV=y- zw#*A$bL3aVMunauN6HTQ&zrxQv;WQf`xdoMZ1H=1>ll@THVSihADVT#@o0VF#I*ss znH%1VYh=h)zWy9kyLQSFRZ+bhNz2Q7XC3d{dcUdCzFLoQVb!^?_xgALS!T^O+c=3W z^(aels?Ce$H_I01?^>Pmo$<+rf+to}&nS7^PrzV|Uq|&awF>VXLRUUhqF$F7|rj_WQlAt1X^B z{@C$+dU}q!ZCz33|8H++|F$yUz8#b}Ia=P9%|3f}-n@CBlRc#jeryk1{rBx{^E-F% zCjNf^`1hS<*A=CBuQ15)RomFwKfe9{_uI?I9zJ`<#{d2MV(Dt0goVM&XLjse)850} zcevs4!-m_lXK(sALwC#9Ee~hD|9|3ffyIM|znLW*#F@fgo9?>Mtem|4{==9543#g| z?mv{acV^dv<$+3vk}ux+{`ZALzTJt#hnd?F51c)l@$33`+kG`ZnUtjd_nta`BB^p~ zR4RK-OJLRnhmNyV$~Tx9zw>{WXyafis4h465)PBzW3&Ik$A^aVA6M;VW3R0{uYdk& zt&BtO!B>BnSih{cQ|JHkHzViAhYu4}8LnCweE3k%|Ip&Wvu|#-`P-`x{@wmQV*T}q zwc$UXUs7}4C$BdBfr(iE`TpRA@r$Osd;i|uAaU3JhKX}O{Q1(;{+r!&U+EpiQ_HH_ z7$;bJoUFBv`d)wTOGK0N^luJ~Stb7_|K4BpJAL{;4QsWfpWm1I1?+p@z_jy(-NeIh zN|)^Y9ap9`+hVRecb+OTM` zCntZ~OckG>Oo9~!JhDfBObuZuv;OayD-dDKUnIj|{n_=}jfHHgSLF^#@U5y=dsW}; zrJulBa;EIx`5pDD78l+tf!Duh*Bm?h^vBo!`j`JU{#$?A;7`o)59e*?xqgo- z+cf>cKg~wAxa;-Ls{e4^`SU&7=I`;%xBu?{D{pT3%Kx9af!jx0yJ<65?^nLK`mz4kc^9qdd zy&LQ6A3S@;X8!*%8^gmN=auE;-fgt+xqI_!R`2ZD**X7muUO>&&uyOm|IXg|j0!V& ze*fQk`10k;H*UtR-#Clsan`P7?#A5`eavOMWv{(1)ywfOyOkaX9j*AiYV+%+bz8qY=gfHWyU6Cz@4tUHW#@D46Idac z`uty9pZ)h0&R6R6EljFD?JnC?QF8aq-i;hftTSquo)jEY>Q7&N`fx^MH2*`K_(Nl|KR zZTyv5smSGSb1Lg1#r2hLO=+%nu64g^tM|_Ophnj+3m&b$EB|e7W;+#r^`5AI@bZ_R zQzzcxx!@im-nsevL|M1*`_}$dI>8jA{x04!K87LfS>5#S?+uQao&G$BSLkemoA>

gJp~iddK@2 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ℑKF2x>kE;Z_3v5 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 Gp$Py;UTcW} literal 0 HcmV?d00001 diff --git a/demo/doc/img/liquid_fb_small.png b/demo/doc/img/liquid_fb_small.png new file mode 100644 index 0000000000000000000000000000000000000000..3b1bc4bda051924d0b30e2ca3c9eb82af5a25899 GIT binary patch literal 288612 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%0Df(WxaEG^BwjF3>OpPQ`+`2@=Pqa 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

Snn;%G+UtHON^rIdePd~`y zfAjxL&?s^&i+IlAz@Nq9FWP$>{xca)VYO~QsU;htY3U;JLVl_G%>x`$|L^@{rO9wd ziT8ldq`wSBvBwx^Z2V-U@Ay#7_4c#3Z9Ae{-#_PNz1o%P!ko0rSj^#6upC1o*M<1} zXLbgD((jlU#V!e?m0Z!rZ&1Z>kn3-O<(L zpXAL_&FHMfRk22F0%yn7E`)~$}CY%j<{-*GN?%k+w3@-|{F}P-&%m}zW#od>| zw)NsB*)?-_**4g%?EWlrVnRtMBZt7E+suhAH!m|tN1xZf!Tp`_iiZ&6g)qBNjkl}W zI1X--57Aj&z|5I8nTvU)>$VW)=WSOC)-`{o9bsoo)aoqHzVVx zbV#YN{0_!%I#W|jS^m#G;53tCnYi~=-3cb87o^yiZGEH0w`Sv;Lrgy>nb|Ozc*(LJ zxYWvMF=xI*fa!CKhEMFDf|d5gO^_-oIJic&kllg(_FV?|*llvmOV3#%4@V1u-^`z!}#F-v;U{!S+DTEZd@(-A)r1_ zdV88cZ@;q8-oJ`9)VT%E-E=K!N7@3JCi6mnm1tTDX4)HcB1 z?NsmtCn2@Hj;}mrGuf(CORrQK?5+&m$NbZw?1WCjik45x3PoO%6eo!8c&~Ek>)P#V zvHzvke9YQW#iyj@^0q^UQL&!YpCgm0YOdxY^#ez@>%ZG4$9R3;4!hUal25FdanUqe z(ZKEKt}f>OH7;|RUh%5?n6gTj26Q<-2%LAjn&XSzb6#2QeT)5k9>_f@{a*X%uSnCk zomy;v874X(+UBy%Vd0A(C!CHxEPM6#>~`%#&0ghpyKIHE_QYE*`s6qvdxvjiz{(yQwn^QXeh-{rr5Rt)qN!z4M!8PO;pj?H4vBEIlW`i?cN zZ*E2UJrgWwO7?cW-t@qsAg;jl!@n0_tLqy+bUh3{6z%wE+3~OM>v^`v?-wk})wuE^ zAU#J{{>3Yk<=Q*icISFK-g~`IdtYq9+w-Tz^?aK~#pgx?A~FQ+dd}340hF zVy|6dXF5E0Khu8a2X7BZ{ZCT3Y`HqV{m6Qu<*7VPzt!c;s!KQ**?0XoEz7vExBE@+ zY99Vmb~Ya#$W_e$@$0vlb=MW`*8KSOLubSM(zBai zAO71~Zf3KBp)*%_f87_&7P-zDv3b%fZfe~J8y*H)LJ7z)b| z89RA7UMtOif5-ko;rrjepkeR|+&WZ5wBADLZsoe{7Fm91UTfx{ghV zbyE=AM4nxHUu8!=P+7gojd^9rG~I->Q&416jt`SFGjtxTGCg3)7ui4Q_%dCV|BU~y%u(r>CHPfknbwZ*>QfQh53E~0 zJ?i>_{a1fpxzoPnp62>x>skLV+QXAB7m+<>e({5w(z}kxGMtY&&v8E0pzxgEOjgU< z&GVj2Yv0qjPi*ILg>sj3Uv(PS1Y3%}y(ut7;K~_e*)`T>ystHO#Pk2USOaJ{)^nWL-_0P# z_kpK8L^hy*=E{8xek%fH*y93T>x9H0euWDmZ44(q zPujoLT8iBvHst*#i8m>`_a9;Io}0+Xec;opY=)34$qW(`=f9dD`qTD>+Y!duSNL8r z-MqqjA#s;YgY5ER#y1b2Fm}w2KgqP&S(iVAIeW{!MemsQzn;5GZ$gsoruhwBPxN>X zcs)?L-%vV1W>;g?pH+XDx{WlXar~A+aUMi z^@3BAj>}G%c+H@c-D2sr0O>EHRfU3YLPA@D7f8KyePt3b|N2huIggiV@n$v0z5B#) zHY3fHeb?ogj0(q}tjJ;4S<|%rIh!l@v1#+GZCsu*$=y6Gx?p`pL34|z`=tY#`kra} zCpdF{N{6ib5>VN2v{XkS<}ULDmun0&%$`*=mgyMVA#q&QSj&a)!ZSMvh*1ahOlnfc5^Lbh-xXLLzQJTLrT97 zQ^3bvf(=)1{gV<1o4|VG$wHEfR z_Db-*T)gJ{fhk6%KHdh}SGaF6uSu}CcC20ht&opHHRLV(t|c{v&-tJ8J{M*1^(?;; z`x6O%Rc;?kDG$j9sxvk99TRqnOv_bpe_9^a^fMyOP;N_JijpnLp(#xU)t`|3vkr$YqTslT=o6b;Kx&a~Op0WqziocaP0e(UtL2>zhQ0 z8T~1%|B0yt+iuZ!ICgf8SEQXs?A#LFOpYUe#XbjJvE!)JPjNb7J>jxt^``B@cf59g ze%qR^Sor1gE>G`+uM*xC_BSthx$b+@x`v`nOvb^v*to{VXVR)zbDT=7z&t~Uy=Q$ zbm#Y1zq8SS0n2J{Mm;^=Tze|?_*Ur>p7#b^LBE#EdTH_c^V$A;^3YshuUTDu3Nyz! zu4>g4yewaf^1sWpI~0~4>}yM6O=YNl}S_AmULz}+!R{HM!&h5zkzoHH`Gk@>4(PP>d zb?$bf{Xx6j=aG6GogCg={~087>{nNOn0Hco=JU2Ylm02(&tA}WQuNh(o6|MclqRu_6B(==wp z<~)*kF)_<1w{fwJwsk}3m8VhxUNJpPKRw-6CMBr8SeL`{mD&4?L_qwjUm|QVLXNr% zRK-L21Hx5eTbnfXTz|-8{Mi7T@afJ2350^n~xOdDZc?CyFb*f39k*J)$T3 zg~=eS?^SHV)?(R`t?dhJv-+i2|1nzTe%i-&S#QTeW5$vh7QZxB1gzfD#~OMuZvC-_ zsVl#~T$jKbzBE*BMOW6uUrb+;=Usj6F#W>puXPtLhio{-yn09K8jTI_Zl!+ZT%hay zk>Sv@dJ~2JUqOct?Ox&T{i?axbb`nXCduuW=Jc$3Ap3In^Vz)D;!2NK@yE!&xVujG zKzYh!mT$^`xf^d>#**!tA>Ye>5*MUdZCV*zjoH8wP`?Osx!GBCPGp zOH)7bS7h2SToAt^#Sp6X-_1TW~+drVfi)(eGMc?YB z2h#T5v0`8F{FP+GMLV`etAk!;O?M~z)G>bzw-bDzZOY8BI8H<2KO4ikXYK1J`*$)< zSiFqI@xhhyRi{EuXp8+&ERcA@t?s9@py_Ad=>~y+^Pk<-{IF}~8j*&-Gv6^K)GmB1 z7;r!O>JEl|8GqN^mAaDicjG?^iN$u01W#0HF|N@1`8fFU3NN({6YP4J^){dS#k0oy zTG4drH9oH!=CZ8Xnzg*D;r+z>;uV}vTsKGDV;0hw^xx%!K2y>%CdQb3J`XBq9v4j5 zKJyH>Kt)Q?OqOR8HQ!BPx@Z1)?&1a0E=R9roq1MbCU2Hd@KqKLACuE5A}oC~-z{n8 znfA|O!Lg|77mQj*RyjLw;F@?=V8f*l)7Nq<%CA(c7Cw>0_lT`-;^#C0gQGTFQy35J zJgDy=vhfc`+8&i4jz777elwfR5Ki-ZAn>D}`v)iU=bkkR5*`aSlrTA1{#R0H=6>?9 zsj`B>!B~mm#`N`%Cx7zeYPd4BlwsG_uR09OSCbj|tiLw!Y?||N!K+n)>-rDez5gqA zEyGpczivtsd^}BsJubc9b$5Z-%jcmcJC;YS_GF)_S$p>OgY{2q&P-Qup0QS%vGwBx z{tpivC*~i|c1)Yvqc=13z(jyA2VK_ zz`0D(BRopYJGPRd0P=B$Co8^Tg&v)lXM8=KPEE zG8IaH`j$0@dRlH0{ZhAasll*=3rOH3&Q^I zA3U2NHUEv@Z_#;sChbsvD)i)TS)QeSL+LTogI{G{@TS>|AK1Bc-X-RgnRZJ~H>{rJ zS6a{Ex8$qSJ+^{(7PqfDeYpOyHnooJy~Y0)2Sb7F>sMCt7oGlPcTSu4xV_!KcPkkM zetZ78@**~%D&?ep*WA_*MGto$+U;=Zc9>$_tI zS6Zug#r|P<_ho0~?|Zoy-oLo_uJFOWhpXOOG2W|N*1v_p^jviGewNC;%vU!{H`Q;~ zKT^+ihOgdrGRrT~>Yg~(ul}~hOC32qYc^|u`d}F`jzUV(!fAE~JoODxZxr)bx%a=-;d90O9AKSR>5x%GO zNYvxy3={LU7Efwt@7DRQ!D7ng&8^4%sm0=};S%?yYrJ-{ubQZ1=&xX+_9f$mWJjdn zWSQ&2hpMOOh~E_W6vrv^Z6Djs+s5{%>O{XVR?pol!gN#X<{sX4Ws@@Fn+|+Dz?E+M zE2SIl3Z&XVNb z@}#qq;q{`p#eU5qSLL>{EzwW?!F46==3zkt*E83j`3I~m4UH9>k;yjG+`)fS*Gt6( zt5z(}VYErp-|leL`0&-Z37;n^K5qQW`q8v{g@h@?8IP+`w-}SxJE)QNg4z=L*;tBybrf}M@ZVg=bT1&zGOt%)R%FR3KEK6Gc zXt2#OY!vydbHeO1-^GNz3-50WZ!isgdnukt*_V5(08{OTqWKN)|2ibUf30wR?QauC z)1z4n!0x+n8lt6Fxi zG39wuI`g3jYrT5cNj8OcAJ!l5KTIe|m~w?-!O~k?4J*7DG){?r`LJGs`zQxng3Wz~ z7nd9VGMrh!&ag~j>BCt}?hVmro+? ze;0nopngS9u31?9{Y)Q&^bR5Z6PYWum>XC+C!04^7d1sY%$n&YH_MvOi~%zvC&xZbgOO%K2^yIX`dBVS6nyZ3;taONb_`koN7P zj8><=`!E^@hMhD~h?5K8IAc|Od<}D{>)zGt7}k5(voYw;?5kotQ4oBlhSli6nMryI z%sUUnO<>)luj(*u#_l^!Yf5iMzAQf|mHK~4UenJzKYS#9?5=WW)pGc9WyhNp+ZC2( z9GkpFcY?Kzx?MRhOL=$QSzE@Y{U!fXG7XH+IJY@_ZN3uyGFPG8=N)gpf56MC={1a5 zD?;6mH)tQZe7lZ!&eF7o?V=|V{}ivtp9~L{R@Y@*_PSVyY5M)rt5aF8adlo5 zb(p(3Cswv-|L6A~_}V`I?VGx94okg#dD8Wl=M^@mJY%oC-@Lo!^{!C4JE?C?8$A`` zD_=*MH!Q2_S;z9`a{bFs0v{|F-s?Z`M<_$~1>dF9XUv~E@^6d17B66}v@1Br|HF39 zJ0DG(9*7` zU1D6|8pqAU@^xnLy_rn=EYdsn%ii(d@%h+e-v|1W?;h1;UKhD5Z33%dl^cf zzm?`^UReLKGgA2AT%jp%_AxeY56wHkY$;kRdCy%ybXoBlPKU~LLBR=ot+q$RMP4|6 zd;QCY4}KjnzO|KaO;uQSwpc|Ot0?a)uB;u#-f!FAxZb?H>2E^R%&t1~C1>i26m^;I z)bH}Ut?U>-EqsmMgFUBBIosZ-J@dJyYaq*1>BG(xVsiO_{iMY&V--H_ERg*p<6^GA zY5fVS4@X|Fd?(;Bf3noESlOgRz3u+RRl@7{|G!f(r#WM9~QHLI(+ z_}I(TFvj__1Xo$ENdGF(*YLG>){z2*7SlcKMh>RQtP{MN=CGUg`ajWG@wg}S?}0z6 zi+dW5tl0d_HQ=?TTj2yZt;jtJUyrCRc92h7{EF$*w8%5-7qGp&mZkEd@ym7fhI?6a zY`C>%sfn;&y&p26Na1_x>|cBmPPJ8ynL3|+Dg*2{1=sQ!c`u6K`E!ituhxmWnM%hG zZVKsn!~A=j^@3|nt0%2{5XBIA`H$&Ui-N1gcQ@~ESTN;*mt(hM{lkBU>$tB}@7nu1 z^Z~2pvfmAQi^^B0bjXSCSGmL-=3W|_-duXC?(#dHPpuKl>{fK8nXvugWmf$o@8A`7 zY5K~S4_?19{}xpy^qK*v`{UyB6(R#21l$XYv(`f-C{I@(cZQe*V&1VYsT% zl;IEa9PWK07tX)#zdOIFub_1MKIZJUf3B(xZ-Zu@V*QnCdZUWr*TQopz3hKO_O7dA z{JMJI(yr!NUZH+XrDv@6=XS*#V5G6{l5IiY!`FEB%gPO>Q{UWar~n5)&9+^|2F4p z0~G$3UZ`H?@bBMxN5&_Y*%qiW#C>Bl(6fIv?NvZQrgI&W!t`wnWsm>d;4AywbWN3O zNwdfu^NkK^qC7VLt-oi6Z*2N>omKbU2bPA0RgnS3R`Ij;vHI`gu9#nP=fEz9I<}JA zp3cq*+UuEF?rC~ls952!>tC|ir}S6PYvjeO)#umUoa?E`Ua?4OweJDP>j~L130IV- zF>XwmwekSNgntKoC#*ccdx6{WI>Ytr540G)8m-)=?lw-m;?QvN=a+NKJ(Klj{MGW- zZFL=gTy=-E+&8-p5holINv%;`*n#HLRZ^ik=#-C|fo46;sx> zUoK}EU+VsSS~+3Uo`V`3J>k1E*q){D`qAI?MYwQ}g6iK?RoNTsx}Lum_^)5XbRc&c zLxO04zr~mTA1*z4zn5p1$dvP99>E{Xg&J5MHLP3LE5nu-AuoDmnaGP9D)Wmby{zL% zY3gGAYSiw@_S#6U&v}96`Oka?-3&JuS*@C(&@~;j( zxSiN#W$logzNe1!hO+t+r4vb($#%@evAZnH8!FE{lRRK%yV3Dif@l7MrjTTfgq5;J zQWvbc`4r~fTrJgj%CPd8*ovFaR4v)Fmd8vzo^bSIs}!$VwAASoh0xko#?8lHX7w@l zr}FhP_M04L&gzv~c8%fi6XP~U?G>j>CL|0eHP1tkdKRa zyJ{Jqt=psi^}(mF-j&m>4?JAhzDp*S=jpmo9}RPPEtaW!g3DxVw$7hZqx3=Jb?5&; zgBM0$LVXqA7v#D1$zPGv`mE)#W3$sSr@XssW<}aJ)*h5fcWusFtMrv+gVpN7YYG)s zZ&#|sw(fs6Z&uwU?sBu3;``YQ%a+>sr*62vC2MDU!~f!9=f7;nvg^JrYtX(sE47~c z+wosh{t0dIo__haVt~qpE74Lu4!Qg~jPI=Pd$L}z4)xtD{6$4{{qn}Q+mzlY6ud2! zEt6%@zV~XCb<_95cMRTpUJ(B(_&Rb!f8@kvIxKR(+MiryoPON)f*zmMX{!a+4Wfy$ z8|#=uZ{70sz91f=!kNG|o9$dqft|UdnovzUb7|4-=I5L0&X!8597vkU{wg$mo)z!2 zZ8}rf6Ut_v%Mv)D-S}Kp@?CxT|A&v)KREeAT{URZ!aLizc6)J6IjdK@{|V!UQ#W6J zuvXz zb~RRSEOp{%vi6>TX}06P5Br67a8%7(r&W4zP3f~ud|yr-nEr~zLtoaL<%M>ZfO(^F z&mv`m=)1|RF9gz(nuQPBREnmoSd(mfBI@VSE3B(lELvuJqUP(IEKZ)OU#GBz9{D21 zS|t)cnImgm$r6DTqNjpl9dxhFT~oRsY{ttwtf7A4VVnmT4u!-TaG&9x#ANd%*;K@b zxpvk;2PXcV+$o!zE-RQ{E!N_WNNxMf8u9pQaShACuQq>UmpWd({PLB^3dcE9R|~8V z@%Xxy;cstUpg42&q}S5coOeufrmyC|@qT0Ae$F(V&NZyr+s|!SeBiR`lK#64H#Blr za@={;SHiwDBzr4sin~JF#|c)?GIwt|HKEqFI`5kHf}~LH&q^*|`DV*ji8zU~q_gO= z-rJ&gSwf|CUy=BV+(W5y0`FFxnZ2NTm(^=72lanJwT^4I{CcIGuzRh`>+lWyQNK&% zZamaFBE|S?!>>bA8LM{eT9VdSHEm`I+t!fvSARROt`-ep_q+LVmDr4JUxIHp1hKqe z$Piinll}iu@Sx)frP)3K?N=O2W!Lmywfn6SQTb-Gal_APs}KBmv1oNxo$%J33v!SB zPr3g;YHcy=C;QtfVt@UY)?eKlVbJ!7)&9K3kG-lt+hSN#|GGEs2@9zIzyI~$vc{Yk zh4atk6~2CD65e2(Wy7#F#*u*~WFH^fWclyf*XF;ssqcQv;AeTqVTUMB!qs#3ZU4H@ zooH@~OZqoE@JapIVwULi0Zq$8+GnRY-cQ+;KX0>wBaP%Nyg8% z9_bIVH%x0VmHqO2fgR&5K`%Cgi2*E3kGX&CUQ@Pe?u54~&rzH&ZKg(2k==DMrDXZHk$hcgaobGb7_rzl%9%I7}| zP6|$F2wmX+bN=zgO>@H?1Y@Rim%W+TRQ0|7$%4Pr*PlIAlPS*37cfOZ!L0uL?bBTA zbSti3x!SawUq@ql_|XiObM9i-!VeTZd!2XS_%pVv4$D@j1hc4wbj>bSP(NXCpGp1c zj3};W&X+I#WLe?Nx{BF3^Gv~lu97KU3(6-hieoRHayv+_B=mB8&VqK9CrlsgT72aGcLjrv-R#I^*=Ze=tMdo^2dftk?qI;O&n;a*H%pP02bER{*M zvIxB787)o8*H0%Syebrfh zOp{x~`mU%yIKjb=up@v@i73-(VC ztx{i6m)5g|m2c(t8Eirq>{p$4$n)Ty$+BnlqfE9t0lE?VDJ$mhV0b0@CyT+u%KVA= zgbKM+RTJVascsM~n)_z$PV)mk-}H?F6_O=aK5KZTbouLYh1it2!i+`JRz8ysxXs3~j(hL0+%Yz;6sXAU` z-e#$5c7mnPOEp18e6HC7r?snI=`JvhzbJ6U{gtU3Ry8M*l5tIO*3S!!Xm?xUV^1c0JoIrv-B=Z~tVRaXPn> z^^Ae!`yGuJ({^?;7oT`j?$=bajBS;EWACc&SIi}s*RPN~kona`uW3tCv&Mv5Qxb$$ zDD5)ITyX3aZ-6?zk^&1KN$@0HIevyk>V#I2i9zrkPuQmBA@+D#CO7o^!3;HJVW$ok%U3;+ROz}DS0|uFE zr!($f{pW#Rp_boVmX#}{5*W{3wOq*^GDWT|*dX`}mnPpY-mmQO&DtmLhV(Lo_m!T? z{~P@`6|ZeO zVkCY#KZ)bW71(wAGn+^4^YVF&k2MzWYBu3#xjnUUSISBA1=ljJ*eq!JJt2$xRqgln z*U~m@wLSNX+v!$>aFbl4-<7?g!54f(cT~xjOj)&XIpd;hURo=Jb~WB&$yz6+UEgqP z<<>5S-K+XUq%+q0v4k-%Kba=_bH4q5OWps|AJ5nPTmEqh&m;%a(8^VsJEq_0k6GWa zduQD-TlpPX>-84^?{3HZHD9D6HJkd zAAGMDocOl_91N;Izw0fxX0)jOv^QproD1_( zPo{!pPqMR893AzKbEjOYe;32mF)=hD4E>DzLyt}6>JUv|%J}u z8@46rvfO0c#PlT3PI2OQ2iL|Zre}&T&LwPP%3PrRWWHC_Wl78V z&M4+m`xT#^n`b+SsIFkF+T>*_{@~oS+uC_G4ExfH*nY_|$5}4eb-1-$Uu5S6&PC=f zaoy+IW{cOpy*A8ei^vq=Y*jZ|1sI=X;Q0 z6%+r->nm8e*3||DJlD&ZBfpZT)+O|bTE&#NU*|JDP3=)>3@Q0_vM3-nWJ?V5E3cju zroB_T8x^MdgbQ?#m}bj^vwMr=_RW^o`_$NeKJCm?bE_9 zkJvm!GqXN9Y%x)lJChxBc@Ouc@YrSi26sf%nVwYs@-%;7EhNFX%%1xw+n!CnzKq3= zxzo55-W^a7YrZq>mko>MF{WaX6m#CEN+%>O_3b8D_?K+DMj^lV z2T}^&Sx3*^`oPAdcJ60|#b<+dGMt^V`of$x^@+Uy7MZ(M$? zZ(jLk&PQ3c`%?b{-j_dE^KmEhUI&BRC*Bt>U*-JEdO}j^iIm5}10^$=el2{tpq_d8 z+?}tlv^d0ja`dn~zk0wqUBDvzEQc`LyEI38Hs)trM2t5x&ad;3*kQ;2O6H69k42O1 zKfPsrzUt?tooxT+Gk^XY?|8B2k!jbxhUfOLU%tO1{I9M^=W~j?#pQ1$H(I?WnBTD1 zz0SIh{hP%1gbn-6{LiQ-$eX@hlPPfGir#b83#wmK?=pOe`n~TL-;Kp>EAB0*iaPo= zcmhlMv=BDq`Ei>>=agj4P%yaY*LRbzB+UA1#D^zsH4-swDVtxFw zpU>_IybyZr{uuGOsgir^0QSGkF1s)}Pf=dr#z%~Q%yanoXz%!;j5TYt)NENZP}s5!?K z)pRC!cBuM=#Fo>y74DyK*5v=xcS}v5sea|9PsS&lHisW=mYH_GgDo`lZj#-G+iMb4 zU$pI-@KXqp<0YyS$hqnOIg%&*Z;W^SY-_{!L8vhI!mtbNyCHo^Z{Zb=~o;T2JAEs10$Y zOxas(@80EIv3S=<>kkuGd0DJjc8&2W)0OsB8>QISafj)f%6?&*tCgtmU1ssQMoTG= zkll`_PD*I9?n~ac@?XDVw$Ho0Z;n3rwqyDZYsWL^1Dln9HCtbvy!LmainMHj!iUB8 z($yMQy4t^L)^wJ(WAVFmBah=-W!S3bgzagKj~Rb={N0^x5TX6mB9`Ne>+3~c%)3^` zKUMUIw(YGw@Z5%X4@=e5ugm5!tXgc9`l~5x!L83cE1s=VUGAtFo&Qqf!pBvMqxfAe zbS*j{sbNt0pZ$L;xHICR+#C`h7c_uv>mSLg3MRK(s>fTUx2RY4r z$J`w-)+(Crs>B**DjcoKf~4WMaYvN$DIdQyuY8G9=5*e^=Xk4 zZ-&56?l9#%vi{Y|qK5e@lN?$Fru*+Xqkqkm;qmAFtO>hQ zj07BIMOjuk9jy4bbYbKV`-*u@g)7Q_Ub+`xkaw1MC*zVA*}p6%WQ%W)Zho@r*($je z^pkcBv^j3D?iIzUE%Pzh0eVlAOvl z?>)>1{cYk~MVfB?lRBN9>hMtcXr0bJ#&2uyTc@56II(IZIEt?77)fO~r@m{N5?JrWapLxxDpr}_yqH?_AZ@SmOihij60btt>R`V)~qc|V-YTgjiMVI09S>5N+? zi&3B6v-AnhCR3yvqH`ZgBz)dEhxzAGm08{kc!IxJ3w+G}G4sUQ4;3%|29>KhD+v5B ze!#AN@8`9Z%znF!&+X(4QVuw#$^T3_epx=tjE_Y(ck*Sm)rPb-K38|E=4-0E`XLxN{Mk(-rcTsLkiTI4=5a##rhD7Uo9eReeX8Rz zvNec*$0XQjuEF{*YTw>}P7CS-+;#L@_03&yt`@pw)Jb) z%Rc#be)Y4z3{N_1vV{)BU)_IJo_X4(y<3|$q&vM?;J6?wIMS45Yv}Y~M}d7>I?72R zOH@?${rsVEV%vd~pF%%;{5CUPvDtX#{sB$S=X@HvzpvC3oDJcAqrnj~!D*S?4z5Vy z=1mUAD)LtBKPa{8U%=u6FC9FKC1OseF4`*2_hhKda8M-;G{A^(Io$qUd&zNQM&snCQ zeQ-~<*vu-0te_?Q24TBj#%<8Fa+Yt}Ju738I3O$q!2R@}3QuP_$*rGzI4!EA<)pKb~Cgz?Pa~ zwc~NhqAkq}3hzB{y2qgIZdGN+a4BW=9M;W~?>@_%V4mYG$(AOrKeIF;>*ec9Tot!h zuJUJ2eky*K^;6rtLb(Rb{PU;%ALMMY)?`h5tvsJ=FYCg5S6#0Ad|nIY+?K!7_K&ZM z@r>f;n>noCQ~yROH$3^zZSG5Buf+vnc%6n`AOiMc@n{*Kb3v^$yV-9 zxAt6857ym&b-{jje)uWT;?(%iJ8ty;4 z-o@azC|y|Kc}$|chwi#f!cWfc`6oX;uJy0@^{LzK{ZDf*m#zE9mUY76C2x&Frkb?H zi_8};47w+axS6KTTwmW5IO(i(!nsGYv=8h)5dCD*)BS4OS#llnUTd-$MYWx_(D;_M z(edc)16PZ~t776<_g}Z4u=#xB3n#q<8#puzLYIH zdHunbX{mjz?_bZeIM1EXb;?1V!$K&)BkF^#P7t6=^yQ$ zWXqK+#2dNnd*kNzjrqT6z3_k2f>oBOb6!1&DJZiLcd#?r{KWZ##QUOt#`jw9CEsgZ zm|GUMj@eVLROYt!dq0r}|9Se0g=#O&dF!-Gn(xn@JAZqhy7`Q2CZFvX=38+x z6nXS9Mn+6>OnZ9v{f9kqpPQ<8+pNEyTNI(z@O@(XzI3${)}M5iHcq>8UFAUf6wU?Z zks0kxeAjxVSbv%Ee$Bn`c2@j)Ci$XC><&G7u8yZ~CPztanSCtF|3cWRElm3^U;4t3 zdr&o(ealkI*$4R+Zt`XLd2QXq)CJey=I-T?xfEE#_ifU)WHEs?XG^svNO2w?}+~b-7Ss2?0eUln=qBI-rup;(f`A$WsV`8HQ$q# zHSWGEyK=pARp*vDU-%W15})u3N-KQ1ec;T6)(7vly#CWU;c{&I6CcLQ=X}1hG_H3F zn2;~$5#O&}>NxL9Ou8}4x66lm+oR);UH5*$y#3sr8}mO%zF7aRwwfk)z2mF$e!u0l{E69zm$@@7bg315$ZS35YZYrr_ucaW zC(KtKc{T&CnAbn` z%H5!B+bJ(Yc6z;REJ%1XZ;JiCmdnik=YF1Uob$=D`|s%knlCNie{8z*Va>#k4|E@X z`5eM<{`b9f+h~UHZP{WKT^Fwyi~O1R@4}qM37@0N_Rmq9@GoVSCx2sy{NnjtvzP=8 zC+=N**r9v3|00F=C)}&Vl1?A{8>_%0k->Jt_QZ-gFH`<7-JD&#EWMOzZ{qwd(g!3} zPtQq9STx7&nRLOVIU>s$ER>t_El(fV^SJWT8}`4`{(Wh3kWh}1Z+X}GMraoE)pK@+ zTeDa~B4XAB{XDO*#QVbgc>*VHO}=WjS>eMzSx1p4dtO~TyN>tfMoal$-<^(KUGw0K zp1?MnMQKl@IR0s^$#t>2u~=3b+#Xjd?@nSk7@GxT374lyy@pl zOg6Yh?@V?q^Q!#B?Ef<0F2}MRd(^TOKWzLzZ#Pd$bM?^*g@D7dLN9p#<^H>Iz<+}5 z37I*jVokqT_e(~}H?3~V;j!dcwslH^O~m|tDYKdTBs0t!ZFNOUjAm@Iit%d7nI^Bn zb7#_38-~Ak{!Uw#pf;o2*Yv@P*o8k2Oq&|Fi_fR8GLEm|&(it;#xrI&6?GlmZ>w4H z-)Y|YYsx34#b>h@^e&wbF@NsN>d?#Up zKfBeA(~kZ>W527ND4&$Jo-wja;VyTaR zx(bWj3E@WL1>sBP%CMTBdoyM8gFiW0pN%ZCC7-QfJT^PfO2Wo|)@#SD2Q`!CJg7a8 zD0ND^;VJ*T|MoSP{x<*L{zIMNXL;e|%Xe5l@7{d&58uJq!}i>LZ4U$&Iyc!f-@E29 zk6}XP71je!3nfq7++(KCV7T*FNX4ShcI!{xu7AJ(V~zae9iO${{4`k4`^*1+b&SVk zchmpjWzROgiJ8Y;F>i&wW2L09qzg}K!|IqPb{@6CH%(Y>s%5A0M(qE3Q$gXTj?r93 z)n3bl>5m>TIDbB-u>Q)a&w2L>|7jndZ=Y7caL#9`?lERO(6Y3NT2XVIGR7+(ukQ?ap4stN;ePN&(}gELsBNF!(=hwnv)&yRfT6|F;V+0KDhm!Hq$VmvSRAwsrHR4ZeHwNRCZ|Z zjorcT*EBy+Rg^!X=3KJ)g>GPLP-1X9B&Mq4#%;`vaVDq^0 zWF=cgwEgp3hR5fnmv{c3UH)qSp~rQfKDEp`&RF;#;Vd-t5Ez?D=-(A3) zE2!kqpOtOPBeS`F0^^-)+G-51E-w~gf4ffgi|zt0-(1!Nb=^|-jOw+^4=y-cx-ggT z&b87Xjc%Wo$}qiLA3G;&!Q&r??{Zl+Ha%(BoAv#5;smc6b^j*b3I1+P`;wk#)+Na5 zn##RlQhM`#0qd3}atvwSD(9Q|CUE@{f5B#bTaGD|Zw2G-i0K*T7qqv{)MdDMchQ^| z)&hk}S?O0wJu>Hps4X>gS=0SbCyBkqV&PpUxu$)|=^4fsHe0S`c2-=P?huUSMXG%TgEokwfodRc)e!2VzOV@?w-5}@0Q4WZ=XLq%l_vu@D%#+hxc2{@5(a&6LAwaD{G5wsy|a_znUv0 zv&zrSS)*=$$m2~<4^*H1y!1Wig+-st-Z!=wxy84im~p~s;!_19!>p7N_7iia#VLQz zJz+Ov>ZHHsDeOf`o7A0OJh{;K(0t*8PfXU_`xw_HKi?TBASHFtu*8OC-YL$q)y@ff zWM8gh-RyJiM*M-Yt)@H$9FHx@q9z=rMqi_9nPN?%4H~Pn)ZZyLeQK^ z-w&8woj8f>(?si2cI?j@jT@2`>`%o6i%R%$){4JK(>uXtU?;1V&)8hT#@xsu>*{(z z%je>u1=5`uY27e zbNKD%=gjB5BAFKMb+&!TteEZc?9G<=gLBt$-;Gxch+WJidf;ah&w1kqlCGA^6;H72 zn|9s7ZT7cGoN1*2Yy8+(9%ZuRa@?BYe8KPo&qS8xOco#24h4(7k$iS*>w`DN&#L*G z^JlQw+*QaZeJ~}Ox9HUg>3@$Ce($Vi?pAyr;MY@cUGc8^)+{rH@IBdQYagWf$=+m0 zYu$V>hG{vMyoI1b^4zILVmZ~TZss3IvHW?)*}!hI_o_zYnJS-cPgILclzQN4>glNY zKr+Xy@)<+6=aQ0p!LR1?oT#_{#|b(~+0F8Zn#cZm+tl{5%gC2yeAzf%F(dWF-30C_ z%UfsiaeU1Ue;_z*>jC8$uIHtnA~MX)jVFE9w{N}3z)~o`VTXl-)=L*APmPrh0!|DX zGqmS-|LI=zdw<$An|r@z%#rqbH|gU>^+WY;SL(C)pKdhR^jU1y+oI<^!CKq(C1+lL zet6IO^rHUV4rct7EH1)xpL2iAjQb$$qPAl@r#iEV$@f31`VyD-2uTI~@0R|!6a*z4eBXg$tzaWEZXfxc76x^_{b8w{v;aFG>+OlD}V0 zzMkU@$J*4m=HKG)-tLy?Dcdt8!S>GE+V_r5?i13NF6e1tfBeq2>Vj~J{qj_K59ZCG z^V*j13)_3YV)GQPFkH#MVRqIe*6&fOZ+ia=Rm9zXk+ooziJ`fpm8Z17&55X)OWGAS z$KSd&=egqJrCVmc{Mu;ui+R0s&C2ZZN6n6Ug@N0g9=-m!T4+A2UQt80LuH)m_j`XP zOqcyVU-aVSMwu8n#_yj(t~7oAcJ%M-=zivD#}(f{j{Nasb}2)F>h>$zjzy84btjWm zXI`#6bm{5~&I4=i1i6PuG1)a$uuM5MS5d9sJ4NovKh+Z|nd);D-k$Y2d4nNSF!OuR z{>BfPfxnxo7|u#MNe8rhn=vj~dROo10jrR0$&RmII=c6|N|=@3s9X@ebR&*6@0I&6b%WU1 zuUr;Pyi*$9@cppD<2y}4KTqw6+QuUDvSh)_dy6L&8Ga7BmBq1J;^))1z8@463sWD( zB&;rux_xlpt>5J`74kNW%-p&`&_p#5ro522B-HOAcd2^89 ziRINPTpN!3y(r9{=K5gLd)*xYfBr1wUj5*gdib$wy9RgZcgJk1#GS<5uXUYZU-~Vq zL+3YdhBVVZg9|T%oSrw=WIt9FtT|n?+{bmrmHnsYzM6ly?yhlpw3Q0q9((hTEG_ne z6UE}BlfECfoBH0eL-);3n>4N#QEtu_S(f;qf=%C$rxOI&B~jGxs{7#JEDJY5_^B%knk7aeOZGe7@g`2nlSt9lI! z(wom!7%jYW_>=N!`8MJ1J{9>o;h-?vTXTYBm9$)C`c`oMs|vjRM_VCj*YoQivLC$6 z(3;Jtu%6}K)F^>(wP%)G2o0#Nyz6Jmdu2uN>wvWx9C8A?EVjt8B;^JrF#m8)S-xLr z#re#G%n9B9rv2sE6qZtcDu(T43Y*cr#wBe1=WORN-+Sno>FB%ggm>$05q~ye{peD? zfUeMO$|sDTi_U5;J|UT}BhssBx-dl~=1qCoy_x4G?6N6!_0s3qCG#mJz@~+tcfN=n zFV}JLI{`1mRHp0>R)46=5%pq8mHB~YPk#A(-<`82e|aCw-&o#Ve9MR7_9d|mYaRbD z*`&!`rYfron ziJ!G!-XI|TmeK;|Ql|4w%VsMZiT$`)#udU~yXg5#zFRTJvr{Lee_@YeS{XC-wLu+pQIJ+a`eRIpq zz-1OQDoZXNTA-P>S(a7pRLoT21Fy@(vk%(%&N{7%=ouS|uw7DXDa*{+0RIRYTong%CvnCY)*45 z=bW+B?QGZsWlvMj2bwL;~xf6*VyuP(FteE+p+!n9hAXO3ft@YP023M8&@0j0haI{fyU9pO5*~~307kqN= zJ+e~I=e&OViQYZuIowYsMJhXN`!Jh_<$U?P5n^B^J;K|UZR$uBpE}lQu->_ww zshxrJnV?MvZHzRZu$OI|&Tt{fXhXxrI}Asro?>D4ax50_NL(w!{-Kpe@^^;KhaZcL5E879{=!6R-p9Qg9i zUp>>}?X&b5&ZI@MD$FfTU->I?#`L+f(~FoDO~o5Sf{wCJQjKhBu+&cf{47@Q&(4QS zE%RP^+B^|CyK>52j!RNY`Fwv%9=g{TCA!?9);jJ!`>oRzQVna*IYi5HWN>>mhh1p4 z5|CIic_mlXow$on7u+q~TP^WLa$PWI3FqanS_J`hi%c@IEf=nJIQ}X{mho0Z`pfJC zK6e!kADpsLIz{Y-#HrUY7fyfE*xzuuIo!nVy64?IwqKw3nK0H)xA-e$(sQ&*@Jo8_ z%{r#4*yOuT3pOv&f8QXteg2Kb3DvK*mvLpt&dt5g7`cx9PFTR7$&Y4p`#0Ub?iH%J zkzw86(zm;{V-?Qyt`OYDyK2qaYilp{2mCp{h`l?kxhB%E^wU>$B{$ z_=)*8lROqQ?$Z}NsG#g?apPH3pm~$^HMZU23Y-0peLEwwqxy^Em%XbCULTG=n%~oC zZJu|>JA$i`!|3j*4=&TyuV3?OJ#pg1vW4}Ha@QumlUcCJ&OiTxvz6C$#>FpHQWrdt zPZgV{!La?wvuj1x55Hxb-`{;tPD$U*x6h~XhV;)cl~$)SNB&Grifgc#lU=y@!1q1h z-S3M(s0_VU&DOm0g@~E>!kFGo*{4F9ltR1jYlpD@v-tUY^;5mW>jJC$-`6qwa2rQh zIox`E)BBQ|^QWCxdX~(aVDY9=<(Wg&^)qi@>RqW={H*cTS4AV8O-==p8&;qFb)aOz zvrjAe7=NDZn;L$g$=-mgt6V#3NraG%Ma}mUccly7H}5{LUebEP;Ys&IMvwdQlGDYQ z1Mj=;mk#Bcqi{bnU3iLI_{QpwwimYCG>)D3k}GwdPVKrUDi@Ze%sTm?-g~c=Y|2!b zMbZ0xIfp9N6RfVCNfvW z<&RvT&6m&xHp+Lx56m$?zJ&Qjo^?yrg83)TzjM;~;P-FUPj`n;ZKA?f2<9A}4C5{0mPL^w>SY%JwVg ziCfcTr-U+h?L7K^dKB}%4-?{>`Wx@&u-~XrIC@6&r|cQO*VkVqlpDLOZYcdXIG zaOw=kT^*`dn5;whZ;O0jGbJrZYD+`dhJ^`zw@Of&Ppz0ASn{;a776rRmuJfQw% z^HK-)7iL{e-!^bxWn8{n#fo#m(iewHCRi?8S}!>x|Ey#z$EG<|b6933guh5pxcoNf ze6zLJvo5CWor{(>ZhE%tF29?GdaPqi)hX)-M^2d(3zjfGej53}+jbdK%1O1mOta3$ z1oPa`xc!v(!?cfQi_)BKY~)a~cHX73f65inp3+q_6F%&Hv-5GaSWaO6RtKpHJLA{Q z(;98no~AOM@U3lc5->UOdv!kZ4>q>ice6Rq{am1LE9wwme(jZd!ag(OGyDs(E3fJ? z=`MU5!QA-S?8LbihgH_6c-gHl?0ff(^?iEW`RKG}a2J^Y@JoW-H zOYhuNjuy-A6dQ|ll|zyom%79*$vml))YETTzIF0b85Z^6XS25-?3}p$Y-vH;Y?0sE zGoGbsO?I63T)nEH^yf^I3IA7Ldtv`$eh8+i|;nMvb8fa z9xPLPdHaC2&sFCKWu?|x7C)T-J})}f6sCOhUb}-q!jI{XF4v!IJ$c$@=K7-d3pe-1 zY~AX9;OL#e&uf^LHC*PtQ*pwR?X&a?<_R)EoJ`Rd%2+qV@APH-QoFW;!EWB!%l7LZ zonxLLt;O&uD6ezjldPbfEKihX%s*TBJknrqimqfr`J`ja8;U<2il6`P{BQ45r#C+R zc`D`UTmOkW8B9)|?Ws5(z}8@*^SzN>#XE0BQtkO?vDIt${FY}8nzATj0blli{l_BH ztS1?r@mVOI@_&x0)@$({JB{le{cUboJpGHnjm9*IwGGp^sDF1<-m>8zkIxn56sEJ1 zr#&vL{l@*5yJUS{iGNewis(}H54-OzvA-~nsf1ZncM;ngyIHq_4;aZFV7}mJHm`cY z=C?AnQacVCo3Jjsarg|wqsYa*jEmVz4J`CFbF%6lRF~q6N!;*`y?lb&?M#QNxV99n z^%-gfH)0q1H}x%P?qiNU^L>|O#-i(`N;fKhHS#g1w+CNY(zmtRZ^Kl@7U(?v@C6(!kf$8sxP)OW(BKD`04aT;!Yo`{Qo^@df;2qRh8eod^1aB z9Lsm<>T`D)x9ziiW5+6)T^x7vkD$=K9k(?fGA()IHGlFJ={wc`oEmi}Y_%`Wy;im2 z?$UShrM3avqFS##4Qp`o2sCT@w{-On$uH@$3)3C;IWN(8;k0)B?1Sd#s;^CEE8X*T z-L>~D`}UErxt!Kdtoa}IDy?t3-ZW#wjrEJU9ba78Q2XLx$laaol)1b>YM6Qzx{l}KH-AW!uUh19J3yI^HqsAtz+MP@VvuJR#ES1 zui1FL#s4me;5{KPzFFs+(TwU(z3bRs=1E_ae$vJJLW84+yJVv3guf2fDe=u~&KspI z<8N>e?2l62&-4BCed}ovqH?_+F;nlzd70?%T;U_F5wz#V*+q{X_g@KaEZFe#>%MiY zcUSzCmCulNdv*Rn+_m>i7aam?kKbts`g+<&|1w`u(&qj&SEom>JAH+3Hzn5?&lUL) z%eC*-ZjRR9;!+V6DlOZ)C1eWO7p;w%w*BDd>pVrj7(6HWmKLPnT3W^aXQHL=ot+c@ zDm?dk=Han-g2>cgic9uS*kf5KZ*yms$j1$AOYA3x>FIKD{fSjr=%IDQ@AUn32ig}* zG;)k~dSG?`Mf`(3>bXC8&*(k3eb#twWA&HV0wEu5Yk>?k)kwximo9pr^fQ^U zj3J9xhp_Hkyt=nZ0yZ{Z zYF;>VaoXO8j9VX1VRd zf1l<4`nI#_(l-wVWQ4t$YrwxJ`1jiLY$1&PcPG4ec5-|qky+N@zH+6m z!=cjccC2?Ms0ec@?6u{JnB*%W;A48K*<@N+5bu(v6*in{YkoSYH5Q-TnNr96QcrdX ze@1!U%ZLyBSMN14+%U?sljUc*)0x8H#{Fckcio-<^U8aVxBl^FEZKGTtILPVQ%v>D zhZ~E2oMmD7X*d0FlHJs|4wJSkMk<7Do4{Xl@{RdN1?wjZH97pEg>`p3$ejwTl=6wc z*}}SaN$3uqM&-N%^H|od=;FaxKZjp(LqS)@7ZIo?kuHX6FlELu1R*8Rp@o2jMgHeGet7X$9rYWa*6~7k#i1-qp{^v$R@TYfad6`!_ z?#%T&yXSS~lDAurN!qoM4!A zQib(}hdZ0YvuizX8_xi4PP^z7nrtg(amVFJ)CpF z{;ghH)4i?RbA%u0G{4}yb*<`c#s!6MnQAvgzBA8eyXAH~i+jT1S*qa&)?NH##aH3z zeoAmdYw7xZOuO#(DKkW!@wxVHLF|_)V(i;qOn<{(cj?wI?F|mgZst3fXS45?eG_%< zmSw{9+b(bU%#QnQ4t987a5|6S^{dNK{7f?&KdceFP?)&;b#9d7&HY+)biXs+P`~5; zE<0iRY5ppvAoHba%S{@E1CL*m$>-}VmN(h|UwevdSXpf(^Hlz@n-M?rHNw}|oG$Wr z{&ILl%Zqk_NpI&$PU1ahEBIWF#mQ7#fGg7QX7c8whv$C%&VIk9y?F9@wfWNA>HBOe zKc4Z}VQcm9bc<)e$C;;!SMTANy1)8SdzRvZHD9iLHDQTd7XHNIhWyuEvD}|@`JY5z zSoCYD7^@A3+=TZt6}21kgxvf+%vUO3wrh5lmyxMb5E8myKcOyPJ>UoJl>Zq--Qw4xumYiwS8XYr=B z4(~REO+N6IJ#O36M*F4Bnf$*d#$CLXP=DF{jn{>jRj20i?K=LY^d8fDlg}6Sur=QO z*I%4<_-oSi%j+35A78rXw~ni|PUpso3mO^|&gV#ZDtw5+pX_6%1k=^P23i23pADsK#?D}-abw!u=F{Uk%(Buud9wF3tFDkN*?eB?7y8!Vwm#q%_u;qR1 zJjN_o6upn}-=^6fOm<7s5(F+ZsKoL;+jCj>1v`hejLgg%c0x1)tu+ z8vBTIqNc+B7vXc*&d1(88nr?A>#`|~zeTu@@;A;o(3Qi!Np)@Xuj&cG7x$Z5vo>9- zb#2<_958jlk_{{dwN3lJzh!wp``+YxCl}0rW%5^OhkEu--b-$am#E&zUA8Eew=w8n zQDe{pf6j<{z7K9?YwTG|y6v?VJXlh3@8cBa`)eLJD*86WCULSa%M_Jm`^L&s_u>B} zOMPwLl7w@H-)%pr&*If#YMWivCx2we(WiVfrmNmK!(JR0cV*#&z4>)T`+x7IfhD;g_%8GXBvm|0$huKuW4<&V<+`hj*uQHJNtB#b4K*@Oj^FXFtac(@lIw)f7`*|k2><$?-__q~c|-Q?1K`;SAlm7xSv>eh}hw#{>Po#QY_wb{P+ zZiBU&%d|$j%uksz9-1#TRS%@k(X_L-2t8}HoYRB*rPg5wCyo~x1@k7F%w-Om@vEh2 z|32yUUk}Fd@JFqj}tU+r~YQ?WSedo$;xfv;Eik^%kE}ihbw3X;iljXE`wE z^y&S3=S~0plZ$_)L-Mo}?-#tcn7#M5qurF8%ia(~@|;(MYGQY`+?Wq2WKerp~((=89KhFtaNQu7n3yB)fl(e?R5L5-^}lP_xYcHQyvTnL_Mt}1HK)6 z(dyEJ>uskO+$dMg7LRANnOVLl$Q9$OFC? z?g_2`>kxG^B9r|g@8UR)_3XU-rm|PR7{p{0e*5?HcEQ@aPxb&w45Csb=TB8d`pG^1fTs;ypj^*ON<5JAS@+ zb|!lAL75MEANg8lt-n&Y?Vg#&x6+SxuCp7{SB4b-N!V}XeD}G-p0ar|ESKZ+H%Ts; zzGkZO0k>=3x&_;2Wi02qa8Guzz2gCvZ@;UZF0jWnEq9#y^Xe^|hfaSk{OFk-=b=$v z?c3y(HQ9Z7@M*`IgFmXuxf~zvKDtk*NMccV%YUhTtb3GeJ@4I{5UBsRqRhbN$ESzy zj#TUjRo(Y>?a6}o?JZ*E#s{vucKy;$4hCeeO*;ny{bKWyf_# zVUtj+<}(-1=W<_I8s)>@R2{zi$8!c+IgN*_+KO z`e3}7xoGmRr*i<)l^KGw?y;h0gdP3{=phD;YHpnPYQev^vUvJoFL_w zsLA?eVpA2%j-~vj@(HRj*37wYG`}|19{h5VJE2bOdJW4a|4Ai^8+wh41R{Jn>=~8k zypQA#ihn)Z?|@?4ixh=(ceB}=d9Q9y)n^IY36#ytSO1|(7zD;YjzH!)s0!pgkSibRZn-6e$oHSaK~g{ z?n?1F*1L|eG}q3~3u2ukzqg(xzx~3?i3{h=eehT1^W`&o59&>CPrl51Y5J0>XENB& z73_b#kHt~bF`0?`V8rEkIVQo7MS2-~RvzTpkY%1>`l0_@|Mz;HchmndzT^9#t28tG z!0mhAm%V4QNZsSQ$G`c_6q#2ICKuG^`~J6W@ft0>RWKv=CdTzuFly( zOmC_iH7*PKD8AK_RN!CIyoGtCmi|fCg6!-~J&e0AtxL7s@%ipg5$5`pbNaUkzc77! z)Kg&opC(4GfBAOP{~!D>_U~lFpVm1U6FPGyb{-I2SIqik-4RbM z6@Q<+xg}zo_MOj~aaFX*jz3DgL~M~=-#qERJUilFn0(jfsL8r~*YH4w?#jiDx8IrM zHE!Fy@)nm*d8}kd+m`&G`Cd1f{t48#?ALG+?VKrLEO(~1_(-sPP~Xz2{_ojqoUMfv z78Rdac*12u;vcQQiWv>*nHQ`-d~ivsNYeHAp|bGZ+ie1s3P$x;7&M}G-}ta7`oQ;9 z<|j6DF_qulv`zfQ`)ld9nOyJ2S)OLl|8Bv^;n>2QdcrsJ{A9aJ(njp8>vp#)Jj|(? z-|~1uu#vUtZss3{_Nv#&Gd$mU@adk}M%vpy`ZFduJa!a5d5oLou_A}cl#o0|&rhq0 zSv{uo`6U;;n|V;BSy*W&tF57plJtp1Hn|67%=9hA4{#RQnHN6Tw>r9$DKAr~;pL3i z#rf+qtl~DrXUF1#J!-o zWK$peT0hUO2EWJF)0)pD&hTMdE~Hp-aHqdo`oVV#cYT(7p}}d++9(z@|5k(ebcqY` zB@=8pzU{Dk>c>3EQ%SgDig}#r3YW?v?+=$6HceS#^&%ndC5OVKl9~gqI-L%hdzLhP z=G?th-$Qur`f^A43zNP(sClu~I^+d3=Q}o4PBuSSHtoDB*ON(m-q%W{Ow2Ky>~R0m z^*e_jgk4;JM|OeWuW8d5Z))16a9f;xy5}cU4Bi_|-`3Ugc|34^*R(8q0!#DU)u%r;UAyv7gdw)qeRkuw3#Mi=UslAWF%-DH z%e0vg-?wx5fxgSPesR9A2)lVR;r5QtxjHwBQ+C{MWP8cL%XoLq+EVr#d1bTY7`JcO zU-GT{!=<^?{kHlweLAWwrg2~D&cS!j99$n{=gly8WNryyeS^>9gB z=3<4!_h)xRTR(hQ%>VXptwVg;>wJMKJ0;^KY(Lbj-~UWftd8~zE8{+)H8WCixk1IJ z-(nTJo&Ftb%@bY6)p|Kv>kec; zv*lzfj$1WzyTkcco_}TT@P9E8J*al$U!FwD_A_<%zw@L$bKdSGg%uGW(iQcamM`To?pIJ( zGB_#Eo!NM0PS5)Tkq>_CU9Rw`?#s*>JRJ9h?kc}xsnD-(x0>6qNaxbJ1h(qBtCk8+ zOGWoLKfBITD_n6a=3(sx&9Vi$OrJV`PI;wp{>1ci?;U33wsCKnkpETsf%;qJ_(qXQ z7Q*~#OKOt7HRQTV&gPA}VVkMeTz%;8#IFm!R-U@YF05W$Dt*FruHNQ@b4;)1`c7D9 z6tb(yZ?o_V{xUXZOFlEtvdc^w>GMCV%H-~EwGUlZ%fuUwRP;CedwTXOzh>gAOWYNgTTU?= ztgB1h$F%H4$h*dyx`kJme%_6{#aMEsft~~*`Nnq-_L(sM*W7)*m(h>Sj^_%K*lzbN`a7iGtWIO!kG51d}r;B zBRi@Uu0L_LWPMhdzAuyO(!n!R_#T){DD-1aVEHP^81qmqoS}m4z@Jy12JH`?o&1q$ zzQ46+{U!Hh`R#`mw(5!PJLn+%u zzfX#P;@YIii+N|rTd0d35ZhQE#r<3C{qHjW!j5dYa8o_OcT+rmSUu$YApUXv*Y9l! zO~TFkjt?DM?`xFE`|R;F<9B@j!Y};O$ zGnX&BmtVS`%jNj7zmk?5Po@|jKf}7$MWb@w%~|TxU;8mRIZS8BXR!O1+;*Nli(zKW zgvN$XD~lx!L?(q8T(@vyn_)CXm3@iw1eTOdr(78HT7NSCn7Hnnpuhv(GNw2lxdl#g zOaJGZ3Gu(Q<@7BG$_bN0t*zJGj-}L*h{I#lt1qU9?nE&XU!>=D-K5Duve%X3Kap6sYii6iK zJ}uZ^e5tKnSMkDC-XHdCo>N{;VmbKGaXw4rhd;08e4oJT-R1QszblA)TzKzTm(03z$>)<+QY+T$ zX7ETzpO@GW|I&9iLkai#*M1M&@1^i&Nc_CYU3FaUf_}rcE0bjsCdNH7cyLw!3*(Do zqghuESXsRYP6&^>ewTUKa?ho*C)}%IcQ?o`TyA3jg17G$_k!D{`|8-XHEi1+c)v&skY!wK?xg zPulKQ%kXpe@j3mH{EgrLTx>L(U};)i78dxS{Hy)ze%9)U`9-stZu;$-(!RjL|M!A$ zg%_^(bK&CepWtMwOm}X4(3EwQer7iBdkHIQqU)2leBUj9B2rKH^ zrSSQOhaG$G;`z_mBXf?ODpEMWY{&LeVKoO+(~?;y88?UIpVohH#aw%-(38(c`?_S< z3KM_o*?g4QQTkV$d9}h~@37Bnlm%{xMo4wAY}&f*3rklupApNRUfa91=bbNXT=4JS z`Gdh{rPK17tB;hF>o?!bF@MQ0p?cE&In1rwLYOMukIjCVU}kkylTqc=vq8c;t6L#?tx#!PWyoze^ItST5b^vz)8A;Zkn$e1`kn_g3%ge$Z(mY-4*MGX2>M z%Y8%nwW0O~05j{+j)LEB}ka*Doe3oDZJ0HzBXk>o-IGrkU#$PA+Rd*0jcS|4H8s zmyfP@Zn#>u__O*6KBX^Q7b2U^lo}|XG5ai;5g+!lTVd*rB%=mlv-y2Y=~J(nNINdG z?%u*O!&@z?ule4D$3~({y0#fRJM^>s+oarZ>YZz_bWTTb@badQ)}pCuGg!}EW?>X; zl-K0>794evJ0tb)IW7a%IopEx-l+XNt;Xd2HCUEo%k+K;zDKvtS+N~3yH=dg&!V++ z_W`#bTva+Jl!CqXI`DpdnQQQJN?93`dWLd?&&As{>_*empBM$iuDgAwA##I5TWnfA z+q@-g=NW4=^THZ#itRbQh2{SBo9|Rbs8g>aCT#2wVOYE1=@u52KDI7ayM`uRuDZ|{3UlU{zk78v zZ^DA@-N)xMXdYDzbG~(dW$vWyPFtlztwS>T-<=M+AI!>B{w8pbLt*sgSmrdY&?~Lc z_DAOLGG*)ATd?KF#7+V8H5YC+nLo(8-(q{yyOFQ4^Ow>GvFRtTo-Hy7U(0FU7W2f>g6+pwQ7V{OoRnR<-*Q0bosDprRBC!Aj@7U-a)d5OP6X%d5g^A^2x zueGmR=}R!a`=HifR<=Aq?uR3HhT4;o6CU5Tw{_e5aaZruDR~c&@bF-IkR;wDtw6_}k00D&OrLV@BU@wf z(Pfs(2g*wRHhz3ydot>{!;yU(_!jmyJo_fMTj9s`1|I1nNujlu5JGofh zkLY&X=9G-SJpb$(u1`&NXE!h4E!lLA-PXJRz~uubyYKusSi3~rlUdTmejZzJz%3!h zP2TR8Ww%U}U8(GF|JuhKhTAvFuF5JL{@G-^AbaNW*9~Rg4Bk3;7o7RVAe;BSh_{4G zN!2lW@`<#BX>!-^E@)nv^PgeTQhwh9*S2`)a9_y0bj**%c2#PQ`-LL21*aQ?Z*|;n z4C0!7VA*YnYQ8Ny!meZqyhx0`@vNZy$y;-wI(C2U^d4h|oN2MEuR2zL5-)D_t65Xr zkg@Kx*n~nUJ=TL?9>i7(?@0Tcou_c2-|9fo%hjh3h)uozRXt(9-h{IYSR=Ko9mRLr zmWbUbn3Y|Xm)R1_Z89xz3ftd4F)Qg08M$})Zp}AMVbC^d-S4oe(lM{$ta@)GuSp{B zWQT7S?@z2hP;zWDTgse0ig5h0QeAKo=eEr`mop4z&zbeC`oco1o|^}D@0!EC;Ov>~GHs6?B@bOMY+iH4J>kzY z@5?M__+7l2AB5^Uu$_=BeBwIcyRY_U`5%#=PBLGJ_&&#secQ`0U50m3g z-EJtml;L#n^QPtV(wS!D6$S7$)q5}45zTk!?6&#w4Z4$ZnG|kso&1O8+<8C6V3{v_ zWG?$TgzM@}d~p6z-7}{N3X?rzFU-rct(2Z|X!b3}ISuJ6mn+T)OEckoFy$Lh(LR>n zJ8R>u8q3mG^ReAME>o^|KB_Fs`P-PL{^trY8k_kik{@)v; z`EqP;pN(Z_T>SNJ_k_=a^=TY0wtTUA#$(`oPI< z#-8Up>bNb6g5NjIJ;!Csy6JZGX-n=$g-JVjpDoo4t2I zvFBsU2S!g#>l78d0Z{o2m?{q6Cy*&{4zvwn&&Ww8n+0GAY)xGi>?mk+-?Bs&{U$39<;R%{O zQ--Swe2mg37f7bSJIQ*e$%DH)x7Cbti=WpRXxbEAL>P?Cd%C#)557ezX z#h9?#&b4$wZ*KN<2J<`pccT*aC9d~j&s05rrdYxLF55p6<_)Pss900 zf*j}8)?V*lqVV9>I~`Utqt5pV#o@;`ow0v-Z_aM{JCR(h>SB6f1#A{m=Pc@DYTvoc z{=fe3+8EP#PQ|+6kln2fC<75rKUwr;0V`>0J(%*5=j)bM zcE#`Q_w<=I&C}Y%bVkzVnHo#nx9hw-4A)ohf92sJIqjO}S6PGbvy9K#E3#teR+YYM z)GOf%6jiw$GF@as+?olB5BBIV&*f8C?Q49Kp>>z&n-~oyu6tS{@fAWb@zuExwuY>T zTYc=f>Gh@>)%BhHetZ^!MLJxoOjdpJdMcz4zEdT=@wd!V=LH=nzr10&sUxZ4RMb1= z$rOJ@W%b3*zdTRGE8V|rc;ETMf(`mkbqv<$w_S5{=#cv{eZ%XEdjvoKRUs;J4GfLw6shtT)zFZ(exQ{d{Q6Iy;xSJUfLx%cRs#ziPPIQ02Sd z%h^G^T>0n2mnImo0G;34kh)LYwf$m-`fe-%lr{~@oHJqP5H@_^@W=3d2dPviEI-4%{=pc zlkxf6M~k^jdMlk-AMHC4uDFdm#(&qgZ)uhsabjCcPq4o{xiZDY`OVZDW`S}Sq_#yQ zsN2Z=GI(9LuK$2aE_annwjftjfBze~3sZkJ{WiUEv1H~lx6ciFDHJTVu_zOdW; zYS?%nXxauDjdbon&KpYppQT^0S4HSEx-Q*k$x+p~>6h?{>>&4cru?ixC&$TOSdIC% z?7R0^=f&=wn|4~c?c2s^@OtOUuLt!maai-6xS+O&;nzB|E1w*+wG26*>^NtX-+XKq zXAi^LCtpOk&qZy%ev>^aZda!Agqj%h`As(u2^2br-I{bsZi!_6DW(aIYL3eqWfE8D zu=y2Iqj3-3#Z8|yc^J#k==QhP3YxsG1p3&yUquUsa9pq;CKgoQM z{j)Kp;G3nt<^6`b<%*YBB~nj);?VHV=Va@f5&BD7z^5aR;cfo@ZPy!vFU`|2I61{& z@c|phIY0SQ^d&FJtvI2d#&tr$__hzj-ZkqL6XqGkMlu^bO!k8N8O&dqm#W^n!Xo+JXX10m@R_k^*n_W%8KgIee(IFuiJ7KVC1|tu z%2d9Dkbl#rD(IeN*va_GSobNjf$RyBvks4Z+cfz~mMy7cOPhbv$e-!qBxlJDkKfGJ zVZ3{uWxJrqHXGf=2iiK4>)HMuD}55Xp*?3x4fC=U!mn-Fdkpsn%FOBN_^o@wtT-sT zamrM_AmKT0Yu=Zz^UeJHhS|Tm_|9eaC5tQ*+?stZ*w6XxV7>B);f0-69_39ppG%%r zRdD4GV2jY4d`5D?R`XlN!cX!wQ`sB8vpn{7-|%_sws%bB)Bg6{KOm`7`z3rrq0J(} z9cSNim&xw9zw_&zum#OuY;*aau}kKaHT0;-?{9pwv^k|AXR6Lj#;D7hi>Ei<-?FiW z})COL2__Sw>`Buky z(=9GJ$azHub84tBe#)1@dra}cyG`>y@a@pdi{H!bS9v_c++h7|&Rbk1+y5=#n?ge!S}59o7T*W3^uZF>wc_vZ0>qKbFaAl#kG1z`1)+cXESE{y?S#c zxS;ab)A;}Y7}ibOQS|4ORELzjsQ6Xcrs_6bZf*XVtVV9B-utQt2G#RheMBeuLbNgZY2>wsoJ|e7-sDrT8uuqq%3qjd@~L{`%hL9p2#c z?gUSVjm*d5C!rS{Q@kA=JTF))Tu$XETo78SZR!v|S^kV+#}CC#sb{1vT-w&>$JV<& z?7HKJ?faf8<{nrp{UY@i)9=h4O{V z`p-+9{&brM<@vFFZ~S{ImZ!}AeJ1;fa??DgeHI>8lXhqo%2s@PRjYPjZ*sYwnhZl` z!}&^%?Rs}|jQJ>7GjFBM#J-^2G@74IJFvw88l8#Zs5IhTwEf5TAL33aMf>;{3n~13yrk4K z-mxs|oAdv?9s57leQPdMIQrDP%JJ0o?|v%$?2(!KHzZEDs8SspCc1!e;U9*wmS1n8 z3fgyNzj^JT8oT1BgZE4AUo06+=CKTmy-Rs^-2T$^mSdCK?bG@W)ol|vKDDgXG;P>+ z%RRqYZt|oxEYmXT!kXqiN%dUY!ev{?GvTv-pT)*btO`hJM>t0_iTC#H8Wub&*$yIwDs$T~PHQfBxA<{Nu z-_Lu!lH9IlqMW*$B-gm7M>4C0&No@1uz_&t&eI<91bNvL{1)?dt{g}?CY_{axbu>mnaEb8#Qw|F@Cs)eOajH5vlXX+j z;}qE$Znq9>Vf#1bbP=DAsOO~jj@B<+Z;KsC_sdW!I5}mRbw>QHn{^8L8Ebf0j1O0y zJj2%cXn_kOx6SqlhF{a(UEhA-%%gcfnNoJGR4i^R`7Km8;n*g(Tg^8!=NW`Mu+8fI zq!-Y|-@ziAKHW$*C%^i-8QbPdlA7!de&5Qgn%-P(PgA;KyR1c%VcG1JihCQ(=bS#n z?8^W8dP3LMo;N%BzD=k+ zzK``&o7mKfgr$Gnv?qLer@4}M(y_9_gECV-E)$nX_L@>Vc&bCp&(_rn_XFpJF4+HR=etIS8ii|dtaZt=_A(evmAb^QaYnp_DgI={O~yN$ zN^9ya#Hk&XVKd56J-n#J{Z1rW0W4V0u<43j`jcOiI4E0&^lC0-mFKY~zH@R}=k%&aQMkGT}e}1ag zix^2i3zm7?^Sz@tB>(+s!ns6U_0HS`snhXNoZI%>rf@gkU3B{G>H{uooys>P`-Jc2 zNNM*|kXmt8Rf<`?qW$lI<5f*!2P6*7@pf2~`FaY+!;sZGMURbLZ29U6n^tA{7_=Dcd6hEu03kLj?*^BwKZzy zznZOmAam~0^UX3-YnI7f2%k6Yx#PQ&6`Gul6H_Mem1+LAWZTu3Udh>U?a(&?gUR#1 zswXV2D3ROoa@)dKrfrjK)7Tr%U07+wd?zLPrZ3ysOYXB+^IGN)8ouu;YVlHu)6N6WR_!fe=CkBiI1{;>RDV$yBf>Nu%$n!AJds~{FFleI6x zq8JWl9$eb&eQ@%@tzEWPc`rS>d}{eSCgT~on|Qo*wW^oA*{$HY>HZ%kKmE-!Sq~IM z-+afkeRi#vvg2B_>x(DkSKoQV^m^f;rC!aeb=q%#7S3_XK9TkO`kcaqcQ+l4WCZG- zGcLGtUV3{>vg7RM(Q!)sl0mI+8Qz>fZLstStJb|e6H>hAojzK$jbXz)O9rEWZA>2= zW+gjZH*9G>P$K<+|G+^ZrYfEf&Ho(v(i^8suHjWkE=cqFb-&<$>+jd^?>5a!^uM;U zl%Hub^KGWx3hCSGQ=dhB(EXtM@h`(oQ)8|4rwx%7(tNV|~v+d#D zo8HVc;i9U6NdX6wKij;5!v{UfmG(+5@Gi}H!1APK!cD7md6sp%A2VkkTz5U7Qrc-X z+gW}&Hcsxmc|G|&X-h0K`y7A#yYTU$u;T9ga}RUGmn^VJFFJ5%+Ib(Pl(VPwmQ6me z`}$|uP=&>NW#@ggV)0o}rodxky!W{6{HC|ZMZfd>$lrN=^Jc~BoNqhc6h6>>JLUTp zHudB8LfC_5Et%%u#rEFg{`7qtCrp0o$2EtkzUhC~xz`UWuLmD7X8LeB?y}zZT6f=o5TQ))(xq7%B+fniaaBa9gh&V@;IPoR=EUm%{VtZglXjoWk2TpTjDc0W-Z#D>zvT_?EtG=K#O|M>dnUb;4pb#2o8OOqBzZ(Uo*^G&Dqi~fc3%#7KMX4|Z{a%bqyUGwxnUSRU}2HO>T z-?DDF+i`Va!sa(T^B8QZf}@((ZFMct$XFv6CcfyEJAa=gd+UA8n7tpwHk{wh{>E~G z{tKPY4spp__OMww?zLjnIL>m4aqAM^ua4id>SGyhwM6gYnvrjB?3oZ(8fW_5q0E}s zlH*TTyW|1xOK0yeRriFa=$>$~Zrtd=Ubl&F#`PsL`xy2qOuMO&JVPn@V9hH|^Z8B2 z7bKtYu$o(b@Q7oOPTzf*xgzLOpoB)IQmXij-20&IXkYh>^`eD z;nSyp<<0vN7Jm^fsJ_Acrscr(Nh;w7_ar=8!T2X}&m8tsFHL<}i~V0k>YwobWqj13 z>{j|yffGTOQZ#39Kbx|Xp(y6piNFh{zh=g<=H{4x7W=T(a?$q&j%y#;ZwNI`Diglp zTGsMg`-6=864n5RZ-GK3@~x%zCuEBqCpk>p6kW%(?395WbL7JNCj%y|l$oUOz;-f3 zl4Vm+{lwM;p}G3LY9Cs}mfL)2*>gxZq4|x%Yxb8f0@az{Og<+tnaikCekOOCMDd+G zg`YnUoniVP_}lH=^#$5rCxx-|Y1lB#n;;X+{X*f`Y|gyE-^+Kh6rJ8Sg;Qbv3;80! zBTpIMH&h<>*|7OwP0F-e7ZvmikN)E`o#IdU#mu4 zfeekLw1zbo{M49d=zZ2;F55I&Ir4$dw!Jgh)w&iH$vkntb;4(YXWEvRjDIxCReAon z-aJ`%fyF9-<&BW-lIf1i3&UnNf8M?B!omgKOwn(PPE;P>e~axvNqPIr&;qGyy<1BY zUf)_LwqfSB>7@txo@rZ4e7I#@;n;oZca+4JebFA?43TdXgauChv){Dt&wt}Ti<`dY zAKIJcoSUdIPrv^Ci|+|<4qiO?ejDp~o%7D;UkmIp%nAK)e!=CRF7{0Ro1V9+i`wW{ z#C0m+DlJ3E4%V?--k(w(Q~6S=Oq@!ZnM7t8w!g%V#;wyMy!icUkj) z$=rQ1tgT62tcc5(_1LBP*AMz_*!-piQ>j z)0Q#p3OmpE!EEQDt@Hke+?+Xgy5q^8*T20IVXUdTRmaS7(_w+0z!6Td#=lJbd^=JD zMYbq7K4E{mENg$`f{q3DO}89BygwN3^onV!s?R;vNfzFr7f#9~iJqv;bk8m*dpUm& zi+^feGIM~tOtDZzu3q)GCYwo%(_|}N&G;6=zI@fncIJ{P;VbxZu6}*PHgBqbD)$DD zQwBUI7Obh`%v0Uo@|$JGt7VOuET1;NnKZv~UC&bK39CYPu->_TGKTf9W37?IAMU%0 zfAQY1i)!27V0LN3tpl>Qhh;CQez~l#uzbsI!{>q@w(oJ^d!bf7b+>%M-}ArXncps~ zpUC!a+q;y!#*&FYtwe3Idp-OY=(+u8c@@aPY&5wpNZ4dr<&nnUoo~HFH^^r)$1;U1 zoA8$9TxB}{--COyj(ITr2;5Y2ae?d&Z?+kGw-r4QouGZ`@E?wu)|QB*}A!`)W)bK6q1b@`Ia~HiD z;KpS`wr7O$Br`wfu=%~zJ_evq6tbAkEN#q*fguIt~T^1@{9lG6wBF5WD&KC$}K z(mh=7ioY%SEt3%(yY_t3?3ee-E+6=}ivMlh2GQK`wd`gt|AXvbn5qQ|=ZJ1^?_;gL zu}zuvi^qeoGWCk5yYs5!HCV1{g}e+a<+!msZ^Lfp4LWWOCRvl%-aHVrZaB)(eQ@2f zC9%8<<}<%=+GE9SvB>!*2hXHkpDaE040iRh-hQz9%H^vz47aD5=afz;yVYEB zK~(2z=K(LFNX9QurSCC(k||g+OaXat~$vd!d!Vo`V+^S<5kz{c+%GCm=z!FGg|&uV}aGwm3+$# zi_e~3V1HZ0lKH?Zc`?dGt_pZ)4wwLc=`CjRN)pDoZmlaWWSKF`Ix+}ZS^4O2Rvl>6n-g%6FSF?X9`;D)+?yO_aR6Dv< z^M%Oe@N(w)vp1$OSlG$xzIB+qp-S$~zIQ6Kn*N@M*>u>U?}X@1t}>hM8SHbOZ0%&8 zn`Ufz`e51AtDE>fyz=B?-2G?mTQ`HvomXuY)XzGZG4hArO*)_)cf;vG_@A^o)<}h= zDdqvkBK5B}ta+BdmjB+zqI)8Y$xHRM9m+P`nZy2lQSO=I2XpGyPdzyE{TDN?w3U-<@R!FMR%XC_5BS%Lv_2pH*8fBeDLBN#cU*TPMtG zcknJ>GMB$Ga9wG4WAxE@_vohIC(h>GX8L=^=CbS)|CjlFjbT?mSFy}ddA{clv!bWs zv7g*caTdRA=QquHurr2b)-3jUuM?ymf45;Mn$cb*cO|iHJ;RN;?@D)bf6;jSMPx>F z+B{44vR!*~*&PnOJ96vao(EH=yp>|!H7i4hWtmrxOGEDh-rR#R&o@~z$V{!A!|*e@ zK3&`5dg)>tCjZ!z+IJhK7rFX5Cv=EiRC*Gf1+MhAu@j1Z;Mry zJyK?0)-M!u;%-mn=Wdv0vU4lP2SM(i98I@awsK@Kr!9=k5M%1E_+rnt?}U?>|A(gUdl-Iu+`r+T zP&@0?uY-TDd@I#H^jq_Nu*-6$uQxr*_*qK7+kDk)>6jy!dZ_upb^Hg@o$_tV>>2*u$?JH_Z*x^Ug=J5aSVLnJXM?uN?=$D0i!sc5a#CR8lbd|c zXG(HB__w&9VarA_CI#ul|LWImZ6be1?|xm$aQotZSpnU|-3_w7HoOOxsdfEX@U+5@ z=>xB(kK}^oeiMxzh)n1|o6EuS+WnvXLAi%}>v&xzbZvPsQ$zi!xxrjbRcFV(39aP` zZL|ORsVD5;`Qi@S`Qp!0yc6oCJ`Z8#d0)Z(W9KnHhm_f07>>A9b}`GHR9o3_e9Gn8 zhNL^O4gB+Mc`Y2O6hy^6Sz9s+Htp}4ZIcymuv6)Y*cE%mKaTGtL?VueE^$w zWNck7!St!yIMay*lM=PKV|!xw>zE!o?FupHF+jH2bRLv%8$1y4NYnZCM((NbtdRry#jE zm2X$=XT0I`iFL;LrCaYY>^$IphizrPwU_h-y(+)m4SN?+h-auIDb;f4j?y{Z6V`h;<|1)!Z-aXOr(=;=Lo7R~v8tqUyosKf1RaE?#%Fb$!=d z`y_1J#|5k=tHl_Eo~Usiu$eT2eOB|9S8B#Ov@%zW^yrF zK4r6X>O$0#1tKfdKj=PpsNb?bTla!N;7?JR zFWj|!RgV8IGB23BRgZH*NjTffzzb}bCO`Rq*LR!pjIa=+%mX^tB+8Wpw9dcbp78NU z>WlWUmG>0xKR(f)VDo8nuxyT^_5C`oM;qeYnu0@?NjDx)G(WhlbxCUD+s^9_;h7W8 zax5`fev;vr)4W&Q7nt&Rbr~K_{P&14s%Xx%2c2sB)unQx-`(7%px-%Vdcw0=i_-K? zFiB5fO?dyptD60ceA@1x%y;5Goq5Z8>u~N$+XgW;^|OsUB}&2qP7~&^p67nP?|JqG z(@Fg?0vhreu8bG%o{OznP`h=%9<%PF8g0u1As6D8T1C8$UtsFE=uE&#-aFg1OQdhy z&D-(Ybb{lzmFy`3){*?b_WoX*#(3H3{>k+V>Mfs^SQ#AMd|!mk^4%8G=mRU?CNzF^V5#R4lP%KQ@fqHlLCc|r77Z(GJ$p4J&|35$&9c`ESd1ny+#xoUccsXq9A zd7i$9m4L_Vb$bgpV|YPOLY6V;w#js>18IL7Pfz$_@o;4{ z04@*?KpL2RxX)3>TNdIVh`k>wOS<<{$7Ojh8 zXpd@M8YP{PU!<-h`h<-p`st zCV2&MdMLfDUn20I^IZGA!ybz|R3dvF8CG9E!SW=9ai?Sb#@V&G3@^0Y8J;YX>nZ%h!KetdQO?dFLJ0MJLaDJKW?sAUTWqhQ&ID8CEHecnzHU zUkEFNae1d3tbD?Llf5zc-CKvO4Hsu!e9Q39v#cYRYn#yYQk@U;qACu~nRuMx-my=* z4a@xeT$tA8C^O7m-OKPs$B*HLa21!szGd7EY1hpdZp~?DSn^N)Tid-SQ(XRUif6o$ z+}Ou3=dy&t#N!$Utr3&id@}i}mNBfm_t;jzwPOkUx#BLt17)^ORtA-w-&5Gv9xeTD zXCSq{k2Nztufm!!eCZ|;#*@kq8lLa_%$x6U?2_MV#`!^$v=+oZ?MY`&m^nY^;fmBZ zK@63TbQ$+NZ?$1qm8p_{FlDmlGG-6`n|e1L!!DodWt$xzd|PG#f8;8D$KOREznLP> zaBN~Ymgjo#PP!!5vw1xV%sX^*CdIWnY^k=Gz{XVkbz`;Aj#JWl%oC#&Zf@|-epSOU zr|fyQU4ipk%U=!u&Q(6B4_mPAfe_EFne9uM{@i)2_Mx@xsec0h!Yc6>uI01e+-TY@ zVB(})Vs)d&Y|f@`wqB23*XgvGizQE_nxICJEyV#ycRRz(t_?SJy91lWY;D) z_Lc1rW9=$-n8)z>rI-ALpj~11jGO1Un=t>pVzbO@gKzc(p(hj8DoHm+RkcPPD6F3G zctLB*Rxze%-uY`8zFEG1>%HN<>5(<8e}jH~Qhnj^yCq0r^AfqIj=vcB55!$4HsigK z{A;Z(Yqw73H4Cas8uM7s6`EEq^jY+eqvV;@+=FQsa?F@Nc+@`-%&_0N@UKVN z!jFyjt|?V<`)J}1$yK9eKIPBDGRzs6Oj!Q%5^ z#&6Rd@vq!!8I0VHz3yM2`m|{oTjjFXPbEDhWL0@o=3kpwqfmF*=Z}K**0wh+pLPAu zZa5>{{QGhML#gs!o}_nW~rOeWLZHwiy!Yx^uv(jx2OdBM$O^U?!bF6}E~ zd?Q|yrQbBAQ0vAV2jR40;VmuJ-O~@0Nw3m%xIOFoEzuWG=S+CtPk(GNJLny|d_nw#;TCGJFMTvN$*?I+R-wv`_ICD+)du)R-s zFa6xCg0D5Fa8vXC?)^*D93GS^ciH64)Oc7!%-K7Lv{TMj!hx_Cxb3r-xV3(`0|wRWm%17*%^G7q7M16 zEwKDKljWIG`WYL8R*i?cEPkO;)>j?6F8g&cZ@%GNcj2>6i5>fYrJ`Te4Y?k2=K3#; zD_7n<7%Fx{N`^yaBgDSH>Dfxc|G?S&Ylq4#E|*%&-<3# ze^39X|MPYD7hcHmfI?Q zjcbRa$#n<$%kJNqH^wM46p725ANcoXUD0mF({D3=i)@kWzsb%r`_C=@18)NLCFZ+G z$0S?$?`lgETb4~Mn^=sRpLe8Bf6g&u>7?6#_^jsH?BZIn{QBFF33i`0>oP|< z*Yox;M7w-0;Pwe$H=E_c#75m;>>V>qC)wPa%g^xhLB`uj@(Ko1%^AwrECVJSyySDL zasTfB4b{wd?(Q?`lx?O z)LhYeu*I_4gT*?$Us2#gr1*ilrO^yadUaSP#BLIA@ati@aQ}-5gX5A3R~UYsQ`7(8 zoGZq_XDD0I(eJ%rdWxpI1KR_`;s$+`T%;5q?f4s6qXXh&tyP?q^Ak@t-DzJ1nv>NpIj$JNKAL{M@AfjR&fBYj2Qy<8$ox zOou6J)Koo6Yt=O!{Wkh5H~PE_{?A*`_tJE?^qomdl!Q#Ir)Tm69jsfjTJeL{f-)Tz zxtrWFEPeuBRtjAlO^ot(EFTu@FgtOSN;7^`xzXFuULeb)GGW0v^t`>nqsSchh zQ&M(5dA5N0$nzZy?u+IqCHQB1$1+9DXm)A7a&c3cWPp?RYr6^hTiSJ4cL$aSa^Fz# zKgn*kKjq?Ahj`A(b`E~Ow?1K#JjJZbW^{AiMAid3yE`ir>Qk5g75&m=bAfxo>z~2b zo4=kE`7KtkRd9d9yJy8~`E?%IaxQQ_xp<{xc(&(V<|5zCH$667e#;OoS+ef#;?0ir zo0oPn&GypI6x;CjYuh@O?@|x9*=CeidtPdmdVP^=K|mk7LvD0`#fIryXTM{pJ&+;G znZj?Avhvq__h!*9C@>ogtT=E(l-R8^>(9q(4>uM8-?4 zzl~nIrpqkQF73=?_{r>U#(2zcvpn;UZ9nW-EfqKV`4-quvV6lh`AOwd6~_x4n@_#u zC~Nc0OlL?u*=X)EYgj6R*uIT zY$k=QVfi(Kxr#AI`1hT?9Cs#2M=_hUP377!iM56;YS#KUtP?m*Chm2R-4-mDV_cgx zyHWbdx8mjlJVkx89Tm=(G}$oE^Yok7xNocV7hR9injqO13!`5$s+mf%Bs5AM@l79TGvVsrvu_#7EUMoYC$vTTiZR5VVT@#t zy7lY|qr!XJovLga>8mEleQ8*?-1bAWq@uXOhDhZi#Uayp)|7@sq~ox}R~;#?o;g468(wkbDUU$*tH%$A)Ioy@0BWdG*MNMD`o zaQSq`BgeGaji1yrVxPAjYsxD-_Q^Eh_D-$yj=OH|zrlD;Gj6t2!mUfQuP<2rw9k{- z#w@;A@XM6S#NEw3#rvj|HN5_}w%IYs$Fq`Q)2S`f_dcj|Tk%)&%bK@~jUCuCGuLvL z85u^2`lM-Q*s&YU@UP^$)K&CJb3&N$M7fIfq8*I;CJEQD1-ULeWxn9&k*n_=iZ;hK zED%`UQh2cXZq8y&i;r#+96giv?P0A=T>r9q!`G5&YuSHA{d$?5zF90bmfjG3BlmR}>-MIzIjS z#7Ek5iv5AI>p^7`0evD6`L7F`Oo%7PIy)|YfWZ!lU|CQoX10#Jv}EF zbgo|7C2(T--#Y;cYwsL8u=`=Y)*fq?!s;aNBj4J3&Kkv@{bD)8HEnt%Q&sa_sRrx) z_s$h$^DT*<`7TOJ-({WO&qx=Q48E-*EayFpbu)Bks6Sg1%%yQw>Qs}aezC7?0`t|M z+$&DHt!lDO*uN#ZK{!N#BgeTYSm;g0ci#slKbHLnQh&#C)?Ioo-^S&_=Z;Npu`VpP z+xJ(1sXLcciu5LaZV6lvMFThHwPViVQ!<#b0*Gw(6{C5orwY(&(BL~ zus=!GvOEyGbQ-6?ik!>0VjSikxc)CVA^!HpU5s~fS_+w#EWD|9+Tr=`&9UN4-I?#|Xsn0jWKt-|M*&()g#t;_#&)zR;S%uLRj(+;zmJyZE+ z9ZvYajrEE4hU;Io^gOcJ9mtYEFqw*l!Z{jbZN=Q_Y6+Yn*K|8X~{2@qONF$EfzX z>*Ru;Th~2f|2`%2^*ko8EIqHLW3MVhFKB;KRc)-8d)je>xy8Z21zWaVc)Z|Z)$KIK zqRiS_mOEFc-C^H-@!jQ_3*ujz=`x%7%)hCBfNx9fSDOiLzfT)q$e6pb)nRji`#Hwn zrV-r@W(QKNyTGlG`(<4TOHgKX%!NNwE+-wx&OM*KVQ$L)w=7b}5_p-p z0_HF&%7I?nKUW($)WUuN9`vz?u6Ht8zN$$t zVa+-A%k!@^Tvt+mJ=I~y zUKfds)OT5h3A_{Y59bZqL`vai(fM<9QbCNZ<4>0VScifeOvF@?Ac7txq&y?-i2CbZg0FDU$?n1 zAv>ryzH#!BYf=jG7u~!Xe3r2!AIRAlIhWUgXTC6VQB>XZ%L>OM6puUP-JV(`{lh8g zCsT?Nd#ch4iKv$n7i#WYu32Chaz)~S7V{3SsFTYsc{#}Kbd@U*^WLyo;?4FynfqBa z7rHKC@reEC9lK!OxrIg*S()B1S>CC9{3KWLdd|csh5t7+H%Z^I+&1?d^SqhHX7(p^ zsxHed(XhMCzhqdHZqV+>G*PWSpxTsb+n`Re!v1=j zB~-dZOIYVBKYe19VLtbyd_vLXTUVGRmH1z>|54!H!ZyLZ$XN8tvVTiL9oW4r|H^q> zP*Z7mH~rHt`6qkgCQepZxpZYDXU|60PWERr1(q@1{hI%)srt&_E4Dn(y5x-75Ar&{ zDOs@KuKvXYt{K^B>>e#46WSI0f1KROROKiB+P*>Cw1QhCL5tPlc3{wL#=TF2tD2up zDw@UpWsdHe=M6j4IEoMYU3WGUuHY_q-o#LyGDVLyGgt3))CK=LO}CitJl!^Zx7?Z= zEY`v+R;Vvg^e8J1^kPoFt9x$y0?QW%B{!^mTYOvYOW)kJh6&fbZ0|MizAJm8Ai-4m zC!2xG-~Wk`KmS|(+Zy)s{71$Bk7b^T8~jb$Yq`p`??=^dx3W3;Wyzn(78AZ2%{-^D zRHc6kqt2{1@}KrAcW^MYW$T>aestx~vh=R` zam9x|hkhP8Q&4~6?~>}~#LxG93#CsT+`9dGTHHw<^Hq0WRQ?UOkT2WzpP2uUmbiMo?o?hA8)#lf4prdQ(o`h&+ndzRQ!4Kac#TqL38!O zibskSf`63$xeD0q{;}p>H}8j}yPtQiKXu@wxq6+=Kh`}f{ye(y+4Mun(aRm`-VZKK z^**a#$NpXWZgkOk)|*Q1Ddqta7Cm7;d;WP!!;!=Lx;#@AU+QPyZ4$U_rfGPxv6lPW z-<|oUF*_?C_rLCb_;i}N=KOk2f2sWzpEI;T7*JbVNb42gh z*6qK)_>h3P?ZL@A#FFHWzVGR6nK9>M{qcXioTn$(_1SW5l%AAzRIc>}59iY4ey2Bg zk6gI1Sm3wW%A_}e9k#Qk*q7-ak^6epyFlUF3j0saJYKgLH*WvyxbLli_$%{|f&$ft z<)wCWCiVZit~&qyeyM7aB(>kRhMy1oRCdj-*@i}^zyi$|mtIHvC4>JkW zn-tdjm~pI?kb0%!xNKGA`epZ-7A_Rr?ljLSZByB~Ir2eYI)2z4jqwooke~a{V9Cz; z?`mf`3H-Qh_hl(_yyee9$w}WKP_?^|@KFGOv*%Ow>v+{N*EU)x1 z*l_J#x_U-W%@V@}&QE9Xu^uWld6*FWDtw#j3l-gE%8vTG^-H-k^uAtZIFYLTD&|7> zwx#=+WM7AEoO^KDmijD{8}GKw*~=IeR(>=4f!`ID-JoS1tL+v!vSaC~6>879E=Z{t7C z9|{+AFH8v*IPvIN0_O|0x+T0Gdt#=sY*5aTlIHxU{x1FBafjlct;AO$uE)nz~7m}TIp$5$*@V>{Tov*$5LJoX|+?!A57!3DdcYcm&yNX z{({$QDoW#&5m`ZD~~qZ)r$UN`s4U6^Sf&VPAssS%YV(8^)~OF+K}>c zhX=D8%mY|U_Rqfmmi3lLdZx~dpT7?1Fz-B{ypz4Cv9gGT`QdZWy|v-9E)gl!dG z$vkQHsUZ1^m4%lWQ+8}V_k>}kcI%n>2^TMKlFbp7_Aj4MAe(dOz?p>>oCicR{iPQ0 z-mkdUZ1qRa+tFQ7imy!AuuSeo?WH^Kn9W|t{bMQbJDbt%yhnfC%>QCX*7n|gJ)eV9 zO>eC=w~)Nr9iE)_CC!~%Cv!PDE&bu#Ia9GP>CmkMNd?zt$hkDe7-p;J?pQuk_wd26 zlu2w3;TddyMem%By1kbt&pT+h?!mjuw%*Ns@J-?3TgJxDGM5CUAGcSZzt4E*tW-gH zLyc?wKE`7w&PT}{@$ffms`T@=^oxL z82xIR`!s4TZh&Bq_8-7GI+Vv4*oGl=WXce6XeY!yxYdMc+s{QCbsuIitCf4#QC zf6LC3#u7Y_bT2sFyDW5vm9fzL(BB`sQ{VYBo>zOH`CKgJ_Jpb{q^| z$1LE&#jxlE=Yo&l6lS!p*}VPXr)}P;`Yg@Qr$3mqaJAyygp{^PbYm~Q4jpD^ct!}8wpR&%i@`(&o(en@#RnaAI*y=Cslx59nS zeb;wB`t*nOxzf2M)_-}INKdOvs^S(8nx|j?&Q`$1lgshOi4DmQ`xV?DzA-J5Ve7r} ze3$SW7wcPn?Drl1KUi|~fg0=kA#CKlu0bc)a~Sw(Gm&v@5nUr9@9z9Jwv> z!@l3Ml`bkb#&7r}q~XG{*5O{@y_Xz?{Eyg<)N_}6-I-UlTVhG>w5*s}@dv(Lm7T-< zU$egOEyt4QMu`hNRm?-4`mgie`Q7f1SeSbM9QAHRb>)+n6)Wt1Y=5zhKflJnZpv%< zzVAN&B={x>wR2nux4E|cLFUUgDMnxY{Wl&x;N_UNRN#NO&)FTv7W}-^J*DwLOUsvU zibuq@s$FY+P@!HOz{Ap~a+h1Cb;?ht{;JguuZ~Tu4vKB{R$aekcZuX5-^ogMd%eo{tNxa zu;X&>)+J5?+if=-yw)lrUunKPf;F-3{L~F)AJnd%_FBK$G3Diy=tW^ox^p+gZg$>q z!vEmH1FYM(iZyq%T$n7FuUHuJXU>t!0yc+#T9%03kpGieV*BH=`ZNzMe!+u@%MU+R zbbb5b^+R99dq=MK?Pt@x#u6yLg1Ee(_4tSu$;fe$f5aBXXVTLg|iY z=1k|mzno!i`to4@Cr!N**EfEdR^>8c1--=(Vsc|QW_4c%| zZKfHKKiy6@&dss4lH9Uy-bJ6CtL|d;#-Lj*k_oGC z=;ei8h(EE|_u#i{l26!w&nn)vo zv1*ofmDGwg^8@6*%qTm+Y<1Lci9tsBU)S*F7zOS4hU%kpeX0{QIXO;newNHW;BzS{ zsqx#V*|mIWTP{yke9*_+vZMIG=e;Q(YxvIz1z(9|ikiq5CHO_~;V+JeHM|i#vsRoo z<;+msx|B2EbY}WuM|=J!mv$eRZXw0|M7gKGQ7ouOETR3DSebx_uAGP41%|~XOgkq3 ztv?Z3@NDMqAif{xeojequwP~<^JVR{YaA2)$V`(wplPl<)gkFie-o*F(q zxa~slwT5F}_H9kES>=HoCp?mO%SQOPzGZ3Dd)IkD$Uu8#{y&C&3q=+lkoeuTGhvg_ zu@u%^#p7F8CuF2e;#g+9*O&c*!ENu=&1TDXeq)V2Z2yGi_g3{&Y7BSZ-ao^pyX2yk z>=~b3n`Kw%tKMLKw3XkJ)hJ5NtNOz1qOknNGv>!r?L1l!l&oYBSuS_fQGfQbD#;9$ zm6H1o+ewF*%G$a$i4rH-IO_Rm8i47U%pv0iJcS$g%2*MdF2W-wUIZu_Nu!Qp$= zyG9@L%%ki+(_})}ubtvP$Hea|JAZG$%3%wh5TRS_8V|4X%G4Qgki0ES|-fayF;#FhVn|?1}Q#8H#zwG}X_Z#1?Te!_S!};u5 z-bS4#O57*XKP|0cv+`K>+T?<3nW&}XCeP@ohG|o$OyctCh?yQyaILEAv=T>+_>Kjh z4_+qt-8gli(Ci|cMKZT{-iFmVc7M6P=x#0*-oc%-sE=*-DZ6W@67+B1`lUW0>e9)K z4W(}-cS{!RJQH-lIZf-p|C4H;_W#lUl+W~OzVNgh-{a!nlotHDm&;>Pu65gVLdos@ z1}7W0%O=%TZD%u@R#2XHiuK@f8`)-m#!OBn;RlbcYdH@vSh7pxu8eln{_JGze4<=2 z-08K$xrvqYp6_R!cvsolZC(5C&hO@TSMyc5W@ma9$oMNSw{|i0`&zu}$^@h6Q<@XS zE=}{5p1qDE<@MaPrQbGwNc+EgSN>+FJI~+Ocj$75Z1tYW?x%P@BAxZg58LM(WZE4s zSzUi>XS1um{_tLj9rkbU&G?%Vc53%_L*FsZxhz0^EcaPKUnl%=)`uFi!v%r zyN%d{%G7u~DwsXb{+YMr-UK_f-0fTbJ5(H=q8@DFVA8wiaD&biqvZ$pZLefbJ@`FS z;naRkrn=d;{_;9?FWVvUNyfuBHd^S<)H@895B_!jtdOwolp{l45$6H7ml6z;f9ng5 zPqsb(Q{h3k-2VcbNHdX}pA^mh6x`vj_#n(ut^V-j;Ojj9bh3P%XG(&K9{jlkS~Ae6AtAWAoKw<~b(wRL?O@c=Kv{%mh3CWouuacK)*SMa{*h ziZ2q5^dDqB5N))@@q*@#`|m#X^Yga6;kzaBr1`|RiF1QRn9wV z_SGemr$2a~>3(CEfSp=Wd{_R1%sZ>w&eu1GoW309@su@v{r36A3_(8q8PajGi;VZ) z|M9N8V!q|K0v}b5nX?vUc~}~-`o=zPNDJF|_~0J3$&qYT&8|fvDc0s^7%wa<+ZN08 zZReE>-Umv=GI=t1Zm-~eXYjjsx8xSK#40J1t@Exj+_<&L)0$zjMY>K#@!fS>9e&?h zzeVsx+TBF%ro2m{dBzu{{w!O3Fz#aLE{-n(u9o~F+m1iwy`i;jO=J=ONyO!yS*Sb68#GO3Kv;s(11AqK2Vog-+%I+s4LkoXw7} zUqr3tz0?%H#PCGWO}%c%@M2%{29fDrK{6$l!6lYAK3iRgV_S3P5WmpU&njFi^4c=3 zFU-=}^!PyEWBz^Yo~hftbQ4zW*_wLb%9j0J>=SPKOpkNWKkHCmAm?HKobg1auKx0a zamSO_H{Y9*R3$tkIf3tm&fUeE9i%ctGg+P_iSBevzqR;><0fy`j<R9)}Mf9bZ1kxMkFtLpH}JN&2Ek3(!9`Cr)BuKn#0f64K3v$D*$7*H{wmhU^6TcS6K?RcXzVWmUI^36va zezmTgeCa>yPUEMirvBCq@LN>D|IYI7p}kyw(Vj-(jq2aTw{tYG%&ci}zkJhfLGn`N z#|Q6}@>Q@kubHm5hUq`w|Nehb3BP7-I>tOjL0?z#MP9(?1;0MzJ+Q9a&F^FRyl*DM z`E&L!zm!PMIal(kKlrxdzWP7gInOF47wmfUvYzR_zh`spT*ZfZkHkCe53ZabEbE`w z&@pqR-ZCC0NwwoXl^SJhD!P(_ADnz}oLTNV>prDxHH<&Cp4^?P&vvFZedTlEju<&p z_Zx=UOKvJ` za!olGvcGxfk+{!-N7i)zwkb1d;pP2r@uu3~`J0;S?0Y6`deiKXdcS$e(hvWh&d$Hr z+w|x0=iXE1Y*$!rv2iua=DOdsRmgtnm%8EZoxF$%UC(qUCUkj5IUP8*b0Whn$7aS8 zlis)=`2VMudnFxW)nl(m$~H9zh*?8|rT%#U(TS=Ny@apr@M{QU3i z{_*~K$z1oJzhU+KLVl-XEo-?ex%}VTulf8%aYg^lSwc?h+NbgEg zJv0>jskk!fP}%(|k3-khS6Z>W=b0rpM=WJ)nVR{L8qvEZX&Ne(llOQ!E)X~I({EUr zEH=yPf!dB8g%3(T%qi`2_^@Ze&xKbX#9jKkvAnCH=i{+cDj!V0q<+e6DF3zW+izoq zsZaDPxvT=hckE}rxwQ8u`=#`g8|-QwE51CrIKBUM!`(M=8;?GG);Rm$Z1)3_57!j# zYd3Gcx31DyFsA?iqSuWJ7EIXeDB9@#>J`g{vwv2+ZCtcv>Mw`LO&-+=9d{N98cb5* zVpxABD2^eubXyt2>x=SVC2v%#+*-cCJ!NtolT_0646O?;Hy86arhoE~Ydp4Uy4!-C zTRUpGc3s=Iakpc5X4H218xpn4s~vXV`dlSh!mj$obcO%v%Yr#`W-m!S==MVSFYB9H z-E$fR+1cJf%_bK-U$I)QbCuw}b>rT}V+(3Imh->hsE%FDTp!rHZ^2}XCGH3IF7ACR zxxiat{Tv3)^hqVm=fjGf{hLjU);}#?&|bDwG{f32)B1sl9baSc-4<`o0{;NL0I4_f zpSI8CwVB+U!#-u^E`cpmzg)Fcn7GDFwc(A)wSWmnOQysr)PHpO)p%#w>^sam4;AfX zx^@5GvcHat@2X1i7G1s_k=OL+jisi2^V)}YTdp4bm$h~-@0ZCQe}yBIug~EBwUqCo ze&d{yiTRGncT%skIBY*4Rm5EVb)pE9@r;b0VrTA01+g*3SR>i&v6`|+ z-M=aG8?W*x2){7cxuChx_0Bfo#+}PrYq=U1y_?v;{BEzqF^A9uDeJH06Vl42u4DRq z=VXFX$qq?(?Oh$Ny;N6|mY5%@qI{t3q|@uBB@@~wF`3-byO^<{&ul4QhTiAx z&zNdo+g$&?p!Y+jbHbdw=jsOBlU*$|l=f$2U$Bc&XP?pfY4c0=6?M(*DdJyK#XX9* zUXq!y=JZUSs3_@-%!EllJ3JT6tynPkfM3Ds8=L|+q$}iZaDQXG`o?C%#alt&nYmS5 zem7V;%Wr1Qa15W-v}e+KU%3hG-`FeJ(l$iS%c=12`o(nh<(bqC>q{6?9eaC1tTPUy)uY9fs3;5r}Ry91i@bgHM$%KD3 zOgvYaudqrU_J5`@q0Ran>x6B;x(+3{pKy4~?6Y|OZ|fEQ>KBDg_NuIIyLwso3ai*j z&N^nx!%UXkQ66SD-W)KgZn<=zdT*L^lkw3x#j_guu1fedNS+8b=6Ivvv{l|?LUGKV zgj?39mzZ8Sc=xplyUXXvjBAd*5WY~M_rlAanCn<;m~i~^y?qR|9n-TFcK#?&c=(^`pRdEe;3*Z482A6tn(Sy- z9%u1!LSk&2mgFX;fo~-2HCDefWv6zDT>9$$->#O=<85cvoGP1^8ksA0DMmYF@AT!x8*KH_ z{;a;Ir`qzDt1yTEef+n9r6l;X`;gOC2+Xvrp$ytP~GNX1)!&mf*qj2{%3MC#{-Mj6So9sAC56nKVnGw#k-&1`in`dav z(n($o`!0mOYCavvDilHK$8ss8(b0=5%2={hq$HeO0o zQ;GGDnP$Rv;?Xk&;~7Eok~aybOb9cpZ@RW%@-l|nlk}D^O5;+HEd7 z&sOtNg^b+Yk-`mAeV+4Qh`iM-xxhMQ&K1VB>G@e2FWjnq+#B~@+P+ONL+JJo)-4;u zF2^j8zNvWAvHQmQx0(g@Tfas!MFszVqI5&0Rw4F)*k$HD%wiW^`J4XUv%8X)5SGo& z*PxbZZ_BXD!8}9r0_&fJR}Yw7tlTEEVTMK;snqv$vS8_}`-Euob!8Ain*3tN*<_dhS}?=|7*EKl>MOt->J?oEzXr%Wzb`CR>V zpy=*lNrihhNyZaS>rC-;sH@t*bmENaONMi{JD;X7R23g6mN;>q<-7WXq;D&1+8j2d zZ+^>AHvOcl)&r$=DU89_!b;m1SbgO?*>+DfpT%HwbIT;>hHx=SeTl-qj~a3k7UnR# zOEfK+2kI{upLV!>OaBGW8PODDi|b#Dt0iuzOpjnxd1+?MWRdpI zMAE^2@?SGskJ!C_as}sK%l?*9koA|)YX`TZ z{I2vjo)=21&d=r9GVRm3ybW(t`9vGmZ&9&5XqVEqm-UyX`WD6)j&YkzTZ@Ao-isk>7@ga64ciA4} z8+}aM_lj&$RSY=TyTz^``EI?5@{6pw`Rf~Olits=c_0@$wcBCGH-p~?_AXeSqwyks z)i&!Bx{{jD59TRu&13dFSzGkU-dy_s)BRKHg(v)yJA~ClxsM6}3-EsNa;HlWM@fgt7EM?kdS2 zj84m1-*KPi__y)j?SJPE6x5~gEQyeg(?81SFr$M}_MCGrPv(B#>3VmgJuX@r<+iX3 zsjK$+|B$=%f6Br#HhGUd|8!3`{Ct(FZ7^T>4y)ehuFVtv7JZYmeAjrTBGi zeg=MtUA=l$<%7T6#QF{fSiAD}q zn}Vb|Ha7pc=+H9vrPKu`p6N9Yx(m!1ZcMila0q(+kH7rSp?38L(f>|wGiGf_`}x2k z=wKbMgWX#O2hYiD4&gWQ99GoicQvfLvU8jKjX!UmF`Wp?%}Z;rDdd{5R^fE&N|o(D zOrNM)O!i?ApR;Hgb7I1>(}r@)-#>ojFo}5|B)VqB^nwYIe-@i67T8X>wx3Nx?PiL? z65H8oX32FE=B90oWWLoD79We+JE) z$j4-T$Lp8eAMrOzXPG_;_A;$+IJ9J|#_I$ej+fce|LsDagYECAZH!vh zdl=l---uIKoEx;jV#o1JU%e0Qw@yktSaWWnaKPP=g~kHP*E^a`c3v)B%6g*mq^hms zwO6rgneOd9pt!*DaV6i6Cmk~x|Kv`q%4@FC-R;57dHhk=@p(-C?&-U?Yku(BZF$~d z^TtL`2hUT*f+ik8udFV-`L?Z(P1eotEWgniw+zLC@(gZS2B|aW_ptwsS$^aBf$zrz z9exJ~nlo(Ok!8#fx@FQeMy^jTW{kO|J+i#NmOe{kyfN|1_Js+vC$~Rau(xEvHkR9A z>x-pMEb%lF__8qfQ0#@WKPS`^`ZKHKntV3AeiL|MidCq2lk8gM615!UMn;`Lagu+GhvP$ivbX-oKf?i#MfrhZLubg4a8Sr%QPw8xI>z z-m9?sOjIQEyW;H)XE|ABA8d?M*u8Z1a!rfEC#BmO)YmQ-OFba`@{h{zhL373>mAR% zsrmT}g-lncur@RZq^OCI!Gv_p?EQg5@1!|U76Z}ELNsV(FO;o0w$ZYE&|B>)W}4ae|Vc>q@?F z!qw5iO+RmKF*0^I73eyR+4I|0l_t3@cR3mLW?Z-8%y@ccG4F@|++&U#PA^?5%PexO z-;!-t)VT|6IS$8eY9`D(;Tp`qlsM}W)3aIYIAZxW-T8TO4@>#-+RSXm)kg}KH``px zy2N&6V)YWXe;5C3;w11@mUY{on8G5l<^-Pa`x~yydw_s(7>;#p`ooqiR_p4~%@cX9pzM(6R ztBi_lYzJB1}O7Bz~PO`81RUdgJL-xJnZsefg=vD)fVyfD*rX683x8=7+sR2^Su z=Swra6PPO^!CK`ZtRnqF^KGJA1X65}q z_2J+5&!AbjNgo`!6BU+vFf5;YQl0tW<*S>I7z=&b+IKm%L0+GYUyoKqAz zpz~Z>R(XY!zpmwVNIz|B_>#P` zgn6#2?AP=MHp?yf{%y&Nn#*`k_gpx`E}!q2oD=RZb-W}rL28rNtY)5x@18NvxcAID zlJAI+v(1h0Z!_=m`W1e@Re0cRQuqx?2dUhp+YiVjN-#YWPFG4kxYNxpSVHBHwDc_* z4d>Yxyd1J-ZRY3bIN!qLc}_Xs;)3&YBi=7ohEJH^1vYMM@OaNJ)><$4_o3WB)&*;Q z>J(0xeSXej_BvuO=dSB-lv#I_ze#7Xxq3+cT+~FFGve}p9HVZTGUUmYb2U6m;bdU9 zVP+`s*(8&1Bgxn@Yt&G@9E+*QYF(=Ueo? ztNRqHeNxjoBam&!`-EC58S(yUc)1G+JcvT zwj1|OU5D)K$ho{#O|`#RBQ6^MGJla(`lWiqLzxua3`gFn7ZWbu2(#jOqjdL(tHRo6 z*WDLnn(V&IP`1kN>eK`8qXIuS_Fl~Urt-pix6l2?zO+?+%(e%8ZTWScE7vjQIuNr!ecFS`)1>>U_JsIKeQAgJn|!dw6&07=@NPoQE`bv#DuNn&uPAL{W!o^rOkw&7ZDqv?B0rkc9{81Ydn6h} zJ1laD@|?uOl2tg}YXQH{nknqpX1zSa{9Swds=sn~=6_vtmw|EpKJGmXc8c~h56;y- zdw0Hr5L5b7*#I}G0G5;qcc$7U%s!ENTjYsmzADd&#mv427p^Uv`2Jw*^RQ`t59;2| zSKxMtm3$q?c=?*obae&yB@0YgH+|mxj3GC1y#d#f#aAXLT)1^>`q~ATkF1Y&h+*qw z{}Oz2#r!6#o*;G~Q|YW#jX#6@X6iS3eD_vrOe&h5l+gZpqAc5;skt_+<+%yl-I|td z6)a(To_yJNp5ueU1^EwTUhqr~O>a=XXW`z!Gs{Lf{H|M;`-RL~$MqO>Pvksfx-8)k zcVX$Qpm@faUlbx8**uviv!9XpIrE`|S#;<+hW9r$pF6r|#_wkM(k1f1=mmesHhYH! zQ)`~r@oigP?N!HadS=`F#%0BAL2_r}iVyyjS>li|;R)l*<@Zl!T*#iM^-JQ<)HnBU zu`FrIJfu}%r@nm+dzojpUW45g_gQQ?d&I81b68YZV)24WR`0c^C&-#@$ZSl<_Z52cJ{DX>PSa06h+phaAf%9CYU$u?h?vsV7UL8 zCt_yK50BDSv+57zrEXZuDV3sbEcC_i!OVQ-3zt(&KQ}C&H*4N@g-Q$6mj_$CEiVgi znSO7HW(L#REjk6gckVEoJ1g}5Hb3FBy;CBg;(_7915ujC^e$|hG`zV|1uY1;bW|02_;|GR%~U;WnpxDdBWV8B1N+QeTK z;SA+Z7gRF*_)x!M(uwx#PyT*7!JjVgx9?e5>t2Qg>s-%ib)47KYQ&f?3dl1&I^ zewnIvQrE85zQ%b|v&kmWhTT&h>CLDtx_gE>f5qh^uNi*^d!1lfVBxCCc;~5LdE@mx zk4!z(Gj-=SxSh1!^5d-f56AXGhjzyGE00MlXlEKE)L--GWdEP=-R7mk^oyNa68K*$ zuVwsJZu{;YThh|bIEGIX=l)61zZv|E>4mE8I=K%zU+;Y5V|njvsqT7U=Ci)S_U4!Q zn{WSlz#<^V_=mbsYq@NuZdt}wFh6P&Pll6JUgJuwi8ic3Lg#}YC@NQS z*=*Zd$!BxzT@_13&gPwG8;{Sr?8hMY`Ise>&9k}cQWa;igQY5bdJGRR$8|D9R_ebz z;PY_pHs;K8-a-w>st(^{$-5QYSbM;?Y9-^n&y&~_K4&y|KbZYu+5@2GYRBM=;_vAfeBWGLe88yhGv6JlZIk_49ClbA|b zPlAX&!}p3qd=Es#Be}LbHDAZnTgo)O;rKUmy+%v@hcPVqmd39A4DMSdePgPhd?3=A ziD~N{^D2=oi=JIbeh^Yto{LDl)rtq(O_OVH) z9?fI9x#H#z!HfmGUwCe8xpg;<(d~wo71JB>Z&SB1Cv9!dV~kXrnkt?ln!DhwqueE* zvW98OUgnKDO9WjHoPC_GAO=A{#q3itC`a|i%z3ds<%vpz^r{>J@?Rjmh?T^E?mWYhiB{JhVEfNkCe6D~bdSKhPxW7^@{ zavzo*Ic>N8m9x+B7xs&mJNi7_zIpL{=Q9tt-#^CFaKr7r(J`C08SfYUKE9-_{J+cm z-gk@_{d{`AD?j}EX~u>ZjUVcs|9)$zsj%$U@A*k$2Y!EeA^E=A`OKH^N0a)Ue7@h< zx=~b7?^W#29R?m6f_DuR89s6^{(YR)xn|)D)xw7YyEUvn{1ZJQ-*r>iTHulL<9SE( znr6)SZvE(!L(bY6!W(rJ6F>c)Q+QUP&Nb%m4k>}j+Plh}ygU9XKl4h;_V{I}c4WS! zN-yt>DGA;iJ*GF+uCUw5pV#yER$j}2#QL>MHZO3$lbNS`!Od)U8GDnj{IZ6UbO~pN z;>$O6+0CSNzv$kOzmde=5O>}G&;7=^3hlMm8TECU<~pu^&GMb^gpc2;_YQ@B=RHcW zGYvkwT*3FGy07Cc_0=;^s#U7acDVG}>r%r><=sAB6UrwASvAQX2w^^r`*;GiM6w5c{=qq{8+TNhPcJ{H~<|kNJ zzVT!#zLsq(s9MC7-_)<~0b5PI;lpa?k1BT|X%U`x&q2#XaCDK607SNBQ#c zN``Z`ldlGI%rT$$+Ar|Kme2dXhA^MayOo`{p)w-e$j;_d?O6UhTZtj@d7G7Cg6CQTY7!`VuzXO#ADX7qBu#nn+LZGFR_+_WrbD zvwP43#-tNYJRYHv%Ap4)pDp<=!m6n>f5xo?scIQY9_5mXybjw=vRN{yoqJ-$6*B2q z2-9r8qjJpF?VqK1T_*l)cE~--?|+=NkKy61-gybeFD?FZJ&|{MB4Y8OaPx|KmK5VTWVmBnLs>%T^DzT`^n2UcOhC??7m|5rgBc$zL2c z{65gf^ysVmer^Y2Uq8-fmCx<@>v%mjDR1(-G{2O|!^>!sNQTeT1Bw>}&+w$MOZxH8 zn#w5f!)3n$%N?$5+Wqp3xxyfOBnBY^0zUjb6l-&2%7UqenFG$8O{a! z`X0&z%(b7vGAlg()-Q$0sq^FqI(D=5zn5NN-n-N|w@yKsd5c;`-7`U!GEGSn!HVVe9_k5e@2;Qf`2F@8 zAGX_f`mP5iOrP8vcyNjo!Z#{Fv^vCeoUl(en!-i7CvWbTx{ z6X<0r-z_1+et4aH`YD>!FTQy#k2(P(((C2FZ*^mi)t0Mj}#oV5C-tm8m z($hvMHFeno*QR)Gv$=3~+9K%}@loxU4`^^I>IXDczf)1z8)vw2!Sf~i&oF<_=Da3< zz{Q|*o5O*wG`8}|=fA#hdULku>$zEcb5_oi^mALV_{eL{3ufE8X0mN%KM571A{a6Q;jpSEorTpYB1kJgWz92p$@+@CjPwZ1W zhVs_Ad8-=J8`ST877vI$enIBTtSi^E518%t^lzN2z`m9L!_;Y4#T@wWeU#l0>@UD~ zt7Y~r;S@X5RLcvKjlZx=_V=4vlaN<@?GD>qC)T-4LA@7)gqCpkZDHtqZk)+CtM5|^ z%WSV_r=1GeH+pVv-hJj56XS-v{dKx5^$U_M{wc-4i1ejmx&sIs-8gt0d>&sLHt$o2YZF9YW%vdgUtF5FY% z*pts?o!@*!(!T#d&1uJIhMYn-Hvfjo*he*Q4|sn2`^1MyQ@1mQp)%P0WdDPIPrF{S z9o&A$T8Clp!pgU=7u-(SV#io}q`>CGq?{$w*mfV+`ySTRch%C0Z%~Wm4?{_nr73y5|J22t>e|NE`|GPi(GyeSkP?_h2!q1%?ji(wvH*SCU@`!r9og|MX zSDOEs63IXDKiPiUGyU~af1NVnd_h4&rV3L)$aCfk{_#vN0&j~p1m7zY38)rlt;spY z{^Z)ECv0`*%|B&3CVDPmHojhydX33frC*HY(Z`*-4thbS-!Q0OJDIY`Ve%7kOV%jg zB15JtuRLyxd=dT`_ParBOZFB657(QTvmMl4IsN6$+5O<`gW3siWS-ehsM2{De&O|} zU02w)tIA3+rmZQyrOEV-H~3O4`}Oa4_tq^JONpJn*JoSh1MS!QUu3h}EU}PvXtP+l zMD54%*1nI`0&^BP@0pp9zB_bpL`BR~&&SJKAD-HN{aZ{f%f49)_{GYB^?fi~; z>dgh(2Q?3g#x?LvoODUtW5PuviH5J6m>K@+XIC*aF>~l^axIX3t9GIP#Y+3;-iv`P zjcuQ;7#7a*(mog1t#N^K&6ev9b&qDPozq% zrrhlQs@Vs=dO-$=qL=oC~@C)ct2E*na!{Ud{xm(?*k-KAF$Y%AOE) z$)k>~*!k4!3J=MtSM;6=Z;;*-XeNHa@zn&LOB2spJTNIwnk$>2{5JSC^X}QtrnD|O z#yMR$x^erhxRURUo#k!6yPLx{2d`j@YCTrPwPj1lCDjAwvm&_~|IS$z=D6u~bP#)v zALp$(3pk_Piktkle@>P1P`Aw6&5&L2)nLW_ZO0jJ$CbaFyYZ&r`A4Q7msO4j~i{3WKog~4OS zlL@yJo^KJI-cWr-O(>@Ilq;8Ae>ymQ!+_z4okk>&b3WnzV%IHnKN?*7%rVES!CqlX1Rj3;q0aUrwV5+ zbbSxzEnBmXZ^;Rn`+gUW-dFjf?+Fsgo$TpTE1LxmFy+3j;jD4v7iPDLk-Nc_5N>et z9OKSq_8<6e*pxNy#$5$o-{NAKGh#0j zUNze0hTVMM@G~#3jq#<)WI2ZXt=l8J8)}lO3M9U?Zu@x1VgAjuNpgEq)_2;mme%ZE zs($0=t{I*WbS{~vNN$K|uUy|)vsV2~(1+BiZzu6QO6HsDFs;zXtm(&=wjBxbLLc0k z-(AbU(olXiuC%||*iFrHKJ(`{?BCoI)}Q;ZmR<6=V>DmN@|%k04(+dFc^M*ixQQv8 z5mV(c*u=zaVcglFEq>4@ZC@qNm+RN|u(nJ%#Gf!p^U;4c6IIhi2XZz|Pv!eCSyMOpKyCM$ zZI(BhmaXq&y|1S(DZXWLAAcItvFgQl)*gud6{Ot2nNlRj9G{VF%%L*dQ$M=ltyO<> z0((YowEUgbw>lCNK4xWV7{q^mnX-ZV4Pzz4uIY8#7;YT@W?U`3r?_m3IP1SP&$!X{j~zfVm3znlG2y?6${?}Z!z=O=$J>|YQT z!yBy1w@!ZFuV+^yJbo$fbO=)R_;0Yx*QdOou56a&VTb1zr%iK+P7-*a)zA3mZHOz= z!&?@;1$U0HU)!vBjajU?#BJwxG5}nUm zNr^z6e+UB#p4T63=V+WTq$8}65EOMff>5}RXN^X<<~<$}9ArRq$@ zTi$-2`{8X{`h9z?FDu06%>46x$^P~Ok58!ol(%o)P?xyXdHaE8eL0g#5yr^+vzxSw zy(^4ff4p?u{lUpo&+TpaTjzc*lex8xebQ%@GXFV@mZzVem}1Oqar|u7UycL+Uan)9 z^+Lp&YtGkmA?d<~Cth>#6$aox`1vKjuF==X_`B552-<#dkq9&v)#}e7ktdH_jum{r?r-aUQ7^j#qwX zwP%IE{qI*DI&3A(74jsH6L5Ljn*>= zuUePOdcOFGSj|PDI|l2wT61o)J5^g{_g4xOaj|=*2Uk+Etp>!n`?Mtm!9|Y zHQb6<#LEoc6npHLv&mqm5zFq~h4=U-v`#wxruD+1#FL6k6(6sknRI!5%Z-*DZ#On* zzNt@$YFb}=qG`f@#gA@Dx0kq2m?DuQe}ng&^*;~ch95eeS08_5H50O7@3(OX;_IwuSO7Z~5->EIjYr_4_{4`Z?RSaTUrwT6H|U`H%7^`4iv8 zcL-;sy>_0)K9ehD7HgjB?u+xS;JU&!XYlDV+>%9mRW#|#3FH^xn}*vqo#O5q-syGPx3F`tQUIbrfZZ}t!F zJqOE{>9Ut~#cl3&nE%qQmaA+*?)u{grd^Dv<$vS(+jQ;0-RpN(u*ht^y+rSZS)4vE zY+}@!M3lVf)*V-y8-p3Nmjv*XB}DZ_+uL|?UJ z81uei@)Mowk_Y-#USx25+je~Kn+5(`_S7+LlN9`B`6B0bL_Pcb!)Ev87qF(Rzr(sR zRc^MMg89i~As4)bbhR7Rx9J8Zl)YAb$^EG{Uzo~Xn7rj!bE&#w!N*ApPAZi9 zh+Y=hlJg%AyfK}5 z#?GMfjOS$bBkEdq2G3I$Sh8Q5YIaFdCG6Hooel3#)J$f$*Ds`e@xk0h44Mh%!Yi4| zCT`ob^?=={O}p(Dbc@UCaDQt!_xT(1(j@|wjki86yT`y)arud4#+2DE>Jyr6rk+(; zd^XfrN@e{Y4Q_`T5mEaYxtmU{VRcHiF+WfdQ{vaOPj9g*`v*4OGerrTT~4|dNS|w}Hpln_|M&I5{Ac8Zx9B_MSDD_CRrt+0UD`pl^TNV}ds`=;bu@Urv}LpW9Oh>$ z^X*x-FFE6Vlcn(PquG1B*gwsRILrObSAXw22K__Nwh2C{b&OZ&vCZ{sO}{MGId)RH*iuvtB^piP`^E%-{I`=|7g|Ke502%o7FU z7h-SM);knrd;NNHT_M%&)C?8*J5iO&-=6k6OsZQ_?>zCJYn+%Q!=y;AS(6^{Jr#ZK zXy>h6dA(&`3v=hDc{&OwCq*Y6)O>mS#S_Nu`;P7UdeH6iZMWviq+K%?Bh4%5^K7xoIht*AwSoAoeXZXrk>r#d*^@j-!cE6-@NGN z10{#I9$y$UGpO6U)%iotgQNwfF&dShduKNz_?&i;@;m!J!g#|aUZrN7r zFV1|P^Sasr=h=^JS<|Q1yjatCTzWoF^&Zwd`yG$(>2bSeiL8puF!`Xcbjn`tPi$}B z>MuC?%=aY!JLS)&j0%l`nJ*bVJ{ul9{r}*vnOinBz2DqdwP5qz%PDL^E6!C+D9YTq zcH8oQJGZd>NhoKucJODiZ~Dx$#Ju8gW0HgF2d$=?%fh0WEGI`--fx=5Kc|G>!R6+M z>q^rc6n=$1p0Y=v`pwVijfWfOYOc*mJ@Mb-dV8f>^IvU&hyR$qm)+B?x8{%(JCbwi z15eS^vmeX*%oS?yz2KC*<&-qD<)){s!rdPop%>RX7rk>h+~Ous{Uo*j$HED@8Dd2` zjOItrzKLc3u3o<0n)^-Z_c_&!MP+CA*k`MHbo$Jj^+zUX8%sKyp2MM+H-bJ+b*$U` zd#QZN>^X&H%eW@|sSW!1giqkayPP7aru@n4*WPFTzIMu@#?K18Obe}8oh}7mx-J^@ zigh_#S3}+NYn$gY@$G-1t#DC%!&`-wzCksoL^x(DE?&yDL|^uwR+c)8y_enW8VM)8 zlkYEyOgg8&&NGL5*1^~pd->Divt;KzU@lKuz+7>g>2*+&V)b&L!$x;mzpYx#wZ7{3 z_PVQLd!}kSGc7-8J6&eFkqB4MS@HSr**o_B*!21Idc`ek@&iu9dAt+RSvbFu@41Tk z!FNe3e;r)qcg1Rf`PW%#>}Ew%co)he(kEuy59Khnp_#ThrI0Cxd+Ow)tQNWS@zdKzVUC{s$aY>;(D(KE!g`e z@Ga{bg&x-fslE@CCbZr^$#%(8Zx(-}Oq6#!Q)1Ap%?kdh+ilo0^?aAIW~AO-Io0w1 z1g&4}9^ohTVjEY+Bx{`z|1w$3foT=Hqd#Nr!Qaw%5*4N%^kcQ=af)mD+0)R&{_d3K zTfS|LZnr8f)UpMW#al-0du`8MHT#SCh zIPLS=D{OC-CWkhdMoq{%AZ}uk-dOUiV-3^IwKIK670&IulBG~@5xyrV;oGEpnrv5= zZT!c1sd?=g=D#y5a+zGbRX;Y%DenEpS+?fN#>tM_URJg8DOQq8%re&e(~57fG2fZ0 zzQO(9wYdrRFZ1X6FWA`a!+7$%f?9*zx4F+{KJ1)+RgB^Hp?}ZbGOkRt@@AcIYuThL z43P&tcvdW4de+8cr(0QClgV|%INO4_i_y~?dM?j@!eek+vz_rHtNAQ_$73fT5MXzyT2dT)1M;h(avWjHUe43>Kz92wTRuPL`yBb!7KY3A+cTaCeyhaonANSlyD@Oe)prU@ z&t%qzelUCOA1C@@--7+J%-^QSZB$n*Xuj9@_d(04$m5K;D=+@ieG#=?EBruDy7~Ra z-GSS796oscZv3h3f@hxlC0epuO=&FRSu#oXVGrxFOg358v~4L{jvu&VW_^|~VcRYh zCSM8v;t68Ae6yPWC_G)NxWnB-GyPz&U$OL-#=1od7wGR?^xZN4<@q?v4V>>95-JzW zD8G7|>({nD%W56|E5w{<*ndjrT%&Bp=@;b*$u{z@8`j==>OW!Hmx;>~xCC>zF2 z4xX^_%H&Q5 z3g@1&m~htT+Ny-G47QWZ-!$&O_BF7+krv0W^$lAuX|Koyz4y%DI&|IK^G$d|uSq{2 zo74@LS~i}e7mTlp{J5W6_0L}Lzrcigam5y4rZtz>N3#B!wQqTGBd1Ry&xY>WH>^b$ z+hP*7S6qKvSMcta(AnJ!Tssw{Z+w60YJOmS>|M_To?bKk54?6`UdvzB8hrmAdz7$W z@P}B{?)RJ%4k`LIW-bz)$(J@aWVvdF{N`IT*+UFIpS>**X0^n}!%(Ag!H;{7o<6)d zyI;?46>qw{HA^xcU9)+*ncZ#W4|6(}}eC2KLG;#9->)$a>E z#3pnV#W2KX&%Y_vu#xv|8bj?F)dPifE{^QC%HM`=xNCEKH|NXpl#JQ`1ee^NUFf{4 z&Ozk#4yHS1xhd8^Vt(HHZg-;m$un7nnc@E(UH-XqD6=@&tJ^nNUo~S)n0<~-!8~yZ zd$e#4|Aan;^9Ofxek%H22j2_Gyf%MNXyu-g0Hs>(Wso%!<%xS9IO z4I>zsZZ|nkYH#c<56c$*(97}nz#kreF`KzE*ZG}RyB}$L%5>lDze2svjI@83&v{pf zPJK|lQ*vHf)xEbreK_(Zt{LvKcH!ZSl>Edaw0wv0r0otV3ePG^yfB&i`u3xHvp}&{nwyV~%2}UE+e8kV zd%rV2Jq|cgW*=Sht?9w*m$$Rk8f^3H%XT{_tgSX*YJEcLxppk8UwOU$OSZ!OM~@zr zXZiF~?S-$wddtmc;yS)vO7A|w(rKtE`sjYE`+d9K`Ww7Lb^dzOE#&7ord540ykH>w zZYlG<<#VsG`<;-LWSDh4Zt9g&Cno2XKQR9px4NdGhqlf`cr8ZOw#bIm*6iSH|+Pi%;w8j z`8|WP#=X*x-EH%Pg$}XBcP&{%zU_avo+s*jT#2s5{a2;Ayx+oeGiNjVn&|5~@}HKm z<>5)?a(3Kg6u*fnxkNp=LGY8dvg5P~anB~rdbdR0Lpnk5#B>FHhs#|Rx8ggtIWE{@ zwpsK*%2lN*o;lr1rXPMV=c!!3!?M$AJDG}{g;NYK^uG;Zys6v1lVzXSu0}zJ$Y`hb zrs%_A%T!M&8&79)sErP+ZY+0we3P|jakC7|?6a@4Uo%hMVq$$zYw?8=S;tCr!Um z5HLUF`o9M$lb9C1JW%%0D^Q?DGPS{ytw!UqPv!*Ow22|?_b#PP;!_EUpFTUm_@&Km z5tTC%!NNJ(#=X-Q?4RLzPie!$Z#}&1#j^y8Ic8)yYjWLtZ?@WnN!BDJo}syU^BtFh zRWHnJnfIBWi4I{2o5>j@pThC|ud&0f86{PWPc-vyyk2m9OV?dyzx>UIGr6sz@2T?d zu>Wkm^T26O@mq~=SKGcxy3l*C?0xiwb8*$?_Ae68KD^3qoA>-yzGJsXsxI4=&H1O; zK4{%m4PxEnZ52|_9(}V;E%AVy)di*{Tt8Jf(so5AEo%^y!EJ1^{>GcRa|+j287S}AcyJq|!i_t(=iCuD znY?d(qJ!Rg{`7-wSsi8U=b{R;7{yIo)-~Q-f2@Y5WYX<5%$u+EOcQlb(~k=(-Mu!0 z*zKS${BGl#OOxdrbK-=*s|Xygoyqt`apLwy{+s8m9rm!9 zm`^ybot)lOds=Qwv%-ceJsgS_2kUoNNiIKQ#At`Mdi&czz$3`&C+R%l1u*-~ z{ipq{qI8ADvP0^k_nRktubnw(zQgnv+IRUKG=4fve(U^O#z^-39KFwK9I@hdDsN;f zu6%oW>-gP+H|3&EL^An>M_+o$Sn^cUzERlV`WzvJjnBL-8ShLmy~8kR;}IrDrX3Cq zAtx)gg?R3NxBVvLo;CNB803Uq8gx?BO9uMP44HC`L`Id^s;`Ak`5mu>|5onuO3<6-`;5s( z{U+424%CNbIko-pnR)qnlE?z!>?`|MS?+gNKsq7{H5#XJ{at6T`q5X`frJb%DmqHnjdv$%)E4kr``S8yXE~nE^<9zT*DgKx4*CZ z@k7?*pXSTLAekFq%vygvxbu1LG^>Oropgdg4_ddNO`4?_;*9SY?-%_}Xp=6U@7=Onl8!5J^zA!WG2TV5|?_XvSIuLhl+C8od zO}pGyH^nZTx#wGgdiD7?rWcl(fzAfJnrX$dBW3w!c?%UuUzrK#^Z!n6P+7jXgnee( zw%=9Ff3+(P$1=&Nh?_gEzYuYrvEE>Mslv~ac~6+PO`M;b@at6JW$_MW=0XSMDaQ`z z1vo5g4rag6_Ie7N@R5sB3wk>)WII$QS#bZF=O@e@)#Z}Qbjf-3jMEPCF^d{>g-#hv zP+7H^>ka4cxpVoyiEC!9Z^&nQnPT&y>C+~*JHO%*zaG$Ce{%(=)|`vs4@@ql?%_5I z%QohScyBuSro!^hC5#!qMp;ZA!tXinGx5(zp2d9HBIOz5(@tfX4>uI79rI^>yC*nd z{>qhS59B?x{>xd?HQhO_@$&HqwFa@t3;%GKD6H7)cwa&K;{y8*|Mn)CZSkj!C%Aqw zF>iSDY4=Y_pY+e$W!U!~HnkT1GUr1>hw87ApFSiW53KbxJft@x4&OXo7Z$p_jzGNaj_G=7^De?TwvP0)lKA;p^q zV#?ZPoldAv;InN=KDhFemB*UTtFN*A^|jt9?(pf?>DfIG>eGsD%~S|Z?F*OkNxjwP z!&Eu>+a~!lnwRhAvAPA_n#(XRetMSmhfiJcvzlU)_#;_!pYrjpVU>MR`hl-GEP3B` z@i*L-3;sIhpE8}-RDNpj(YOoU-&ijnu)A`&hWE>Lqi3oT{;N(_T$sM;LKwsIIg#(@ zFvrKI&w9`OVQP`oZ#jneEO8}=;;xHl6xQbIb#On9%z~QHH}bw~|3Bt>24%g2e5MFImq;1kVUd@b*l8)>wV=^wT=Fd+KG4wR}C( zuf1XYJY(&?(+g~utTUI%N&mgkoIxXP`|0)tol+~!53boh`3XlwaY^HK$&`KX%Ig?k zRja2M2b?*=rSQ?^6!V4aYvSXTxAGjk-|#S2>d$*KyZ`gs>p4#B=RE17(jdEj>Q2^s zlC}xIoX@H4oMTg|;JtcHVPvg>>lwpz{tUIXi)9byzIs+>%w)Xxw3+se&ZM)B2TI?~ zIG6Cs%6BJ^A7}oWe+)gB`Ak^fK7RMzj?s42(mlTp6jl3wZ#*}t`dRdYmHRF{I>7aN zV;@uQL6=}`hdEsskRe7 z)q1ram@Z&uP*YRkdt#6B=VNbQdAB^{ux6K3Ha78^&vUR# z({-|yL(0;4~v_pEQ)T{FKo7dOn?zsNu-}3T4=Lb2DIuGc4P`5Z+6YluDXwq_lJuW#P z>>f=N8uq{>nm~gvFQt-mLZHfyIh!iC_nBPj> zC1Ikxx0GcaewY7P&J?M1 zbBWc9^-(=}52k$REl@fry;ON6Z_)I+Ah}J~&jeatPdia;vHeLV&&y}-;pgv2%Vc-G z^!l*>Q|NJqK$|4%AJ>*w+dk7S*nLYUgxUIX-GTfiU5}j=it>J3UeTVQZ+Po8KPTt< zWk+OLzUO{l7A~yh?!K+>4tvR^UnT7q3GE5S53*A@&*?wdG9`8&$AwRSmexAD?35*Sp-XUwRF>$8Kh5My)~&mAHR13ZPid}i zCZVS08PanX>Nnmu5&zsMY~(4%u)L;u?q!9)zSb-GYf_#E30WxBpUk+>s%T;E@Q|ST?QJ;Rzm zcuJ9)?T=Z%AJ^`0Dh}i-kZFo%PkLjsVeQRyb*5=|qS#HCdo39Mq!br$J=vMIkkzAU z&+>9cXU$F)xev=Txi0yY8Ov|!v~>{JanNBeOVNoVw_^_|-+Cdq;r^?fY0WwbBJGWw zQNEv>wk=xH!&2QMUBv2%|`;{}`;IxUd30xnyiLmdQS$U4*Ld40l zt{1M0?qs)lkUf*jXWHyZd~@zE*{H{49L;x1b;JEzlUo{R=4gFZe^H!wBJzUfH#yUW z0L@d{8Ls8n1@Vc^`c{W8~@ zI#;HO&U==>1#fuf^~mzwatJ=-JV7{Z^-X3Oll`y7I(}LH%Dm!Xu=eflNz6;O{cP!c z!2h-OQ9jd_MSYbVPbSQ|Bz)+w(y1Taj$Wd=r)3M2net!cJP4lV>6gI0daGuJ=;hfv z1@0Z)u2>~?;(FP#U=fW><3Nrxl9wH&8cNK#pX3`nk4Rs|pqm?K#`wO#`&Glew2r-u zAx7d$m`)r|o3^BDQ+5&Slh~a~y^V`_7HW9t$7jA|O}QZSgvIlD%mRLk^*@|`H+|l^ zC+$(=bhU{$7i@p)_uKeI!OX;K&7W`ROzl?4zin<}e}XSDG1Fmtfl362%9*q;>llvCQIX`9G1OhlcyIelO@>SE z(kIq02rt?6j{V<;mzrE^k=iel4W=#q!uEcP>@wvB5#20%rq4V1jLse?+q^y3@PQB` z--IakC-WDH{(t$Sz2VPpgO#nnf6Sa;D)_^xw)wBT^ELIGx1XM1ezjOwQ9$EC>x=gf zq}6y09vqqRaSzk8j8JQVC!5x0Hb0p9eb)~W?`S)wMROaMX*ERVhB4jXOkuw?x!dJ~+pWX9#C&9*Kc7&p_$TbhcE#L-Zm;8K z@dw>2Se>2tVe4mpK1q&!J^x%4zdA<0ijigC@`zVtQI+f0?q4z%CvCl&_%3Sg5=d$I zd2A)u!pOt7UG}r?liTOKFYahndyx22v>oC+B<9ICh)YFEnNek1VX-t1YLVjm{Xmpu?? zx5Rpe`?uV%rned1MRId4o#S3^_G9s%>wjex3^W90-I3XI?jD<1!}@hQozg$V^Ur;L znyX^p?R~}#?m{Og?6NvgnYBs#?MX(D4{}TPt-H%`X8tEZhZD2U2{9}y$B$2YG$u{W&WCRLw^WBPUa`WN<@Co*Po+?u@Y7E_LV?y(pSo6kX_j_w-g zlM^JGBl$dUPx|Lq!LaZ7zKj2?9(d;6Er61LZx9JgYg z8?&Ww7cb?zp&pYWa)W)=gSV`;p7FiR(cZ=x!WY(AMYlI?yLL+B#r<22?FsWQdc0zs zKdD_HL1|(Y_l+}BH`#7Yc==82h3Kl=vlkf3Tv(n^erx|*w+p#t&DVvu@bBKr_#!Ym zjJxr=CwrAFz26v2m|m0hskuk> zMF^8FUy$I7<_{uQPJK614)CiEkU5iYGrd-!e}`}Bg!@nRcQs0CFfWzND4draU!WFr z@e6aQ=e2JKq7_UV7VSx5sPHVCD$a28U9Fz@fwJqfz8Ty|dHL)q+uPOgfr371HAReH zy5?OqKX~M|)l0@*PS-L7OdMr?HO?_MUShD~`Pu|?gC6}<)f0}7UT~~2JDwAI!SIup zb%V(xv*V7^SuOwACbjdYT3@)Z#i(IHZb4A#pknMHl1QlH^eFv9o zvritdn|xoB<;?}(O?+GS{f%>PbUE#0Ea0Oqd`MsduTk$)1$kjr?Z(AX-uVrhul{7R zHNROj*FwPNv1`@UhSXnXf(dS)+kZ;#5nE>FXrQjCmd{`-amH;zYubESo=q2PR{A!+ z&s}7~G`aEAEa^RWtS0{y*fZbg*u1tSwkM_UGCI`TE^R+>#Cqr4<_Ff#UkmJ#KH(~< z)%<0*v*qd7b2~KvzaAJ>?R+Wu4H>Q=0abPdlysh)99PLlU(^c)fQMyUdi`s zUd|=G15Wn~J~pOonSQ1`VcSU_WATdEWk!r8vuqEvKX}S>uKaaFeq0N$EXSv5Ki)n+ zP%15F!MMv`eI~aS_p5=|YBrAG`;n!4awFrrvxP4b{KFGWDisj&I|E3zA zD7Vkqi5GU=@zj*p1HnY`QGdr{&N;)v;7+%eM!(X zXv})1IfHRm&vr-qtNB~_EA9*A6*us{sCnH~e#u&I{edsWvYgEI-p@rX4`yaunmxJU zwdjR&3*x=)e{;{+moq)nK{TKFyaV@XEnOa==du$m7Bu=U;tZIpI)!=OY@sNrFWlCC z{!G^^{GT`bKJ=ZmqvYQ7B~`Y)0eu*Co8H(mPw!G9r@>Tgc1r=|w~y>C0=|Ly~9 zIdakLMPeuBOnRp;aVIljJ7cXvQK`$q4$FkqmpUgdSpQN)sKF_PrJ&B_{e#B(>6s^# zAGk|>WV&&@GOzi%Yk4rs9>;B>tOximd|Sf4PWW)Y1KXxd76GlqHyjrUIaJ9c9L#*ejll3OL z*54%aJ_{Y1*J|_Ss8h!L37z{&6(lW>x-8(nznN{v@!&@e-LKitu;fpc&9`>ku-^J| zUi$-K^U2?>W^8}jVa7J!&?)W0?Ucj(@8%zwAo?b9UID+wL@S?Qy|rg&U%RF{EkeLq z=%WK~Q4n`UPS_7CM#E*@`HBw=0xvV~d^`Ug`-9NZ{_0poi<`@4osL{8FX-U-xuHqH zJ44Ugfm>*8>A~+SzqlRTuDW%T?aV?>ru7{Qq$a4;w}>#eSU=NOh}%{un=;vx*JifH zV}_DeGsX#e%j6s0H~y4#u>LINaLdw4&1mWi3tmg+1uthZB^8Yi ztQnRlPvKY~y!r*p!ByXuCy0J{b?fTA-wJm*4mZ_{Hw$pE?OATae7rR6H&^5PeeY-G zYp^V0zIgKdLAkBhe=^i)s!i;9@Y`s~8hybX54B&d<~g7p_gDP)>-efUDVrEhc=@tS zNG>yP(7$8DVz7FNEkpmMGQ-T!7$fVwCwQ#<#9v!*)J(B4`ylv2`L?S(Y_M4mTiEi+HBU;OF&(xP!Q)^l4 zR`DA(_Y}{vlsPV9Z=8O09pmn)+jg;)Ec4sG z)uI2^+Iy-OZrzK>%C=H@!XmKjjW)x*d`~J}IAY^GUki ziMpNI8xL4t+3@!u=VR~59CM}$oaOq*@t^zWSu()yK18F8B z(Gr8OdghxxGjdqpM?39pJ#hZW%ta6KSe`Ze95I~g*j6+|{9M z63-%YgZoo9M>pJgCNa11O;N;tM$2WJpYV7n*G*mZV9(Kv`{oYCnosAjbG^L2i_3&x z^d+C4^-OQJ2j!2GSq;L~ycjoVnuQcMsd;^-`%L9Tn!e|^ChMEte0w;J0_rz2 zH*P)mK8WRwgz@c4#;9ZKCIu^~p5gl|^29wl{y$58>46ICgR}h{qoq$Av($4qwd~1h z&DIA~-&b~viz|5Fdvo2mj#ua7&#!hfWUhRF-aaKpb1n08MOEiVvm4VAj9R9ZzH@wX zc!l=FR7DkuNjd*m&Z>tm)eD%g@e70Os>rQ&H)hwI65C+EY6Ih$ut+E41>!G59Jo9x zA2HiY8osOEUwXIx5NZMX1mCpUd822><;y!XH*MhX5a5*UAboVQlk|X6SWS^ypmIP&eYD# zSuur!`-yZxl!qiIqpX!{j@4=DQt9-o7|JxT{RAhJD%v#4O#`=3{?9;W3 zZx4CC3$v_j`)4O^~d#A_PV2ORrud!jB<^Wg&76GxdZY}Zr~`_XE&jsJ$&o6jOC z2Ksx77wqQTsP3@n<8Pe{^CFHn$QZZ(lw7jma-1FW%$2RVkr!sG?G0%Xx7qt!i6>8F zpWV;0G6UnxcPXrIGuLMpUufI5(2S)_=)7OSwOQKdia2CSkH7k^aP-6NSqk5m^et=O zo&0|4{?B4Lo%g3^-_JEL{gZu|rQ%SS+6VrBlRxycKe_MjktHzg-=bWG`Db6OWvssJ zwxipT{dCfOt^=yFstxXYR2aTZNn5~j!#3#5A2vV6&E824HG8yM9jk7H-{Useo!V~4 zG<(hR6%4y#voA^R>5g)^)VR(3vkHgJb1vP6l23e`4pz^Z7~Hy-^aU9+Pw@0-`rs41 zG(__9x#j=QZ?dvckUQ{4XF0}pZyn7&?VDt4P!;i||jDpEuExF28YB+A- zE!fFjqR2e)K$XzP1HYR@f(~^1*?u&iKcQ1%f?JC)bDFi>@`E+YXXc7t*n7|X-Ry*K z0?E%Q}54_FtkV3$g- zKBarX@0MdV0lgva~<2xv-5W`J!#Ur^zQ)sugSI? zzb;L?dThboFH3ycT-jebT+m9nqLonmDs{GS$^5koemgwR-f@n7?~3|NPlXBJT>Kv> zY;JfgwX(EA9h9}04E%Y5uy^R0F@+LSoZGO|4iM>X=TXxM^V(Q_xd}6-J3!~*a z(;ehr1%x#(`E0l0Xrpk{HErgoro2@N=Z$^y89sklB6MMD`X-?z6JnT~> zo4lsl(I`=ZVc(?$qsFtoTuU8wSic&+;JvS+Gh^winFkLf{oc8SQTK`Na=i)tE@I6y z6Uz(*zO>w#e)K>>+NrP!HagpWI*12`>}ReFPCMP+uykQ8m(7&>kxWkv`|=ppo$#$= zuv~ZjiOdPhxk~8=UuIvjp5M@EUZ}?QMz7k5y?LgG_(|3x2ek__Z?3C_%x=onTm8kf zVE69tdny7Jx4#i8@SBy%J9`5OL zH}jROt$dTmF#F`}l=;pcTQ59U3{*%Idh~~{tm)f_SqP$97;!>*Yu?JZYu?4?1v;EZ+47QEzo$MEtEAH(g)`b~2i zXV&Ps9(e8L7x=-_##FY!>dXW23yD_})OH-4QlnXW(Cq8J&sGgTkF3;XJ-GUjC^uI{ z;=p22JA1bHEdMsG`dg_7c3T8- ztnlTDbSRtbxQ{t_`sHuR7qT4mFBqFOOY+SMK4#2)CgXYVJqFKdOh!C8#<9!JHW=SC zoyoeZH{VqGhV`#$wtVxps@ihtYV0dWa&2V-8Ug?I7wtEgYWJg(^W}Nugh`ZuQ>N!V` z439i@_7+>7>ihLFDHG2WuVOr9_221(AFKZIJ#2Xy8`2*z%_zBbzmH9Jx$0JZi|Vh% zU5)19Wr+vwrHDv6oHv-svq06u^d_ID*1->#nkH|0d7Z)K)~l6=uV2jk%<=cX)oF*Q zpZo5Guzh%Ha(`)AcJO8P_sN2n1bcqQH^sHBO}!hL87kbTF->s2HgVR}M?0UfIXzus!fdp$BV^Tv zZtH~l2YM$DaIbadsA%2cBjT|oHanhuRn{6^o_(9tlwUUHS59Wxu&zly!}b3b>j_>L zdj2&sDt(oj*f4{e;aj+EpVJ41$tQT0Z9ijj`@qQ~rSXmllh#ex-zc)@%I2EPY1dv) z+VYK)fpg_TPa_|G#vgy*uurh+VVRJW+43=dUdxaFA9R;oEARLH{a51^gGX@}qd^n< z|EpDY$}`0kBvV@|9;})CuY~3E)PHUQ8Hb(Y8cWP=m-5euQ=9%%BEzfp9^)=g^SS(Y zo>{Nwx=}pqOuWDz(Jk*JIV@lPJ|oG#$^YAIwg*?9vDG)9oH^^-j6Wu98{S^(cy#=d zNW)B%U-RwK8CJbt;86Ip`_Y2vnX9844(~Z;m!N%};m(4!JP)p3v~gjq{NcHpF}+eq zhf89OEnh;ncl3exPnX!gV6k0%+%Y}t!aXKgotYmS*Dg9-%KYN?EwA{dX@}PbJ7m9- zJIlXkx$R7@D(~z}zuqrcY~+@25}Q@OA8LEtnUmSYu+w+xZvzt zx7}hZ+-q+!ba!5~bjXg)KhC^y8>_YB{8u8f%-=RX+i>1NKG<2l*>*wbTgDCiZ=%03 z7Tn-_XpoU=l%4v(Zm}o-Cii2fH7`v16yeRdZq9{g2g0{CRr6O|ug@0etRXONy0{BRugvdot%ICR{UoAqA~9- zkJ0Q5xu%q9`A7IOm%c7@QS7xKE+3iADk4wV*PXCF(9W^CVg12K z?kj93*|%vGh@8>9@O;7gOLh;>CI66|!BI5tipVY1gONS$@+qdNrkr1tUmmh}ki0VQ zAIn+arWB@ivx|Eei|5@s!yLQdaS8JUmv;p+H;(w{Fjb%SyZk3X?WD|2hB?OTg?Y*4>u&{CqHXk@^?o3ongM z|K!-x;dkMrz&|g3>(dVR#It`lWG;GI``dBbObfvcU*9t1a{N2B>!3aB68`3=$_9&{ z?vvyd%K9`b;DgaipL~`X`H|WCSbuhHx+H9ofU_|$H!ahP-Mso4#SOSc6dloswj;dH@kN%4KQN|nWDOFpDsxcr;% zP0_a7PZi?5(~YI(94|5EnqoT7k2%lV&X(=h_BuHozDrXyba`K8R$dl;Q?_X}Yjp?r zxAX_xl85Cc^p~uwVVRODo_ZkU@w$~fvts9Y=$zn*vl82)9e+Xai*w(teGB#{ax%{K zPK)IXIqbnXq0Gii+rdo8JN?4bj+W+i*Pa^5--*wOyUQ|V+Np2G6Vh#_xGtDCZKCx9 zoo5TyFtC4?n%^`%TKyvv)AqgE_bNW@)r~&daQ#5`tJcP_Ls_W?bI;~&<_T)sX;#7e zhWply`Hipc6-u*99ZopQc+X<(dtTO81%0a;nityqe_$^OUYRZ#Bwg$DD)Yl46Q@rz zKCfY^nRBW2z-g0>PilASGas-&X@Bt~%d(x7&7lismvrm0NXV?^^<(~j`a zdf5H%ENVRT;A;7Oem<6Uue@f<$@D+VVVb*aqZI$P%q-T>=#Dcf%+*q^j;n(i9hRRe zV9<59W~i}YSE!BhxVC=_(|=u`S&aWwwytXH4Y6;?b&F(J9+q`s^3Nb*L0xO@_WvAS zSMCQ#&wdpckj~8D=y}!Z&GLmj?9YYz{&UP&tgySW=SK%)+#-$-L7SJd?LWz%DO>J0 zyF9CGUFk1n2IjB)9=)t=56;wAtofIHV~e1j&)1Ml)n|+uqN|pkU3{ee>v#R&=PHt& zO4dK#O*oLO5dJE?!R<0e)78%$RR6x$s)(V;$zwpW41Rbe;cJAgl(zqV7i~ZOiI5|)FM;qp7{UCb0=!O z@4ue%$dT{0rX|O@nDo7yS?_FrBevZnKqH;9C9jc zwUR^6zD~xUO?&Pzzuq{_ID!9_Y*ItnEzj4?tCGK}eVF&`ikCxx`DN~sb#E7hAGmg5 zK_Bz(Q`auA!t3;P*M zmITM{V<@^+YsI@{>P;Ql#;zv{+zW06A8XK+(a~mhyms5DhPTD~?itqwvY#vXF7?Ko zVca+E{8p|LEPtoobC8tEO22Sz(h05yosnvW3e#uku-(XB?rn4l38`m<#$zSL{n8#u@VOHU4mW*P9%R%~zS@zBrEZ9+ZVGYyRW>;LR)tHS;e)j{d?bau01{nnd2%y ze0!0`%Q&e-$-VKk#W(i_La$YAEBJG(?bw@)y00y;zs*>~>{9G-&UuFLx55m)1zRN_ zs5+cl@Z|Wx_=EqeU!0oo>Vfs6=;t}C{M%Q~W8az2=Uw>xc2n zzY{bclzGm2E8Ezi{&Lab>5ZE&-oGry{$6_f8n;GEzhr%(EsX9rm}E-KtQjYi1l@ka zzWG4Rl(>ZdMbmETANVdUJ%ex4k(>{$O?+2V|8S+P%rQ_tP_y*w*@OunjXaMvXXZ-H zytbhA%R)Q$&7Qwzu_f%kxbd5`L9UPCWuYIvU)Qu9(ERNZ&+L^IJD0!eINzni4#LLn z)lGHBQ|_|yTzAW1vel7)J%7RLoojwKu3p9|dr(?JW}#~X{~V`xB^L_bWr;ml7%s$Y z6x@D6{E2(ftN9D+Exg}wT-a>3yqsf?e6D9K!?U)ZF_RtrH@XFJx0RXRbTrEd@M^ZX z=JiS9$-*U*+ZCiUG+sM|y$JJU;ycNIt}0;T{h8bei*sf3nGJ&M92*O2-Rv&(nmm#C zV0ioogNm)z{N{U?&G!T*Xa`-{!;oiP=TNQVl^85O&AumZ*_(0gFHPfZ%^co$AWG-9sZi}>H>g4DvyV;B; zzBXgcsePLq-!OO8(_F(1p>L&X*(2N|ZkY#u`+r#N2mAlXAMV%xzgIHadU4&#+=9$i z8h6k7a}^%o{$?n*<9uvf!_2g0h2nG0&pq$!e_?XfB$fk@Uez5?J*v&rBIs#T5YTn9 z{K1>A|Anl?oW8aFmok<6v-j}5UM;G=i{v79Rw zcS_qZ+xx-%v)*zImt(9RHkM2iXSihDd;Y%9<(4H&BoF;}^1H;Ck$aZ?f!l2XhTA5g z(T_|wuWYLo;6KT+5lzol(xP%t&0|+#_bAd0O*>uWa9IKiq1#&h;j6!*PfH$Cn>FING{8DolUtrKd1QMalCl zliGsSJDDE%ZePooaj=TP=M5pPBq|f8~7DsVrh1 zr=J;`<*xG&cAjsuI)3u3yD9UZu)O-rcC|4(=CSvMGqWO(GcY!7vzjoGBaFGepov-F z%kK3}_Z?16KG65TkmJs`c>A?UJcOS<(%r6ct%RhtRIcvqK(9dcN`u zp7mZc_s;i?4t2X5a}svVw4Y;e;kW|djaRd#h9~^a4E^I^b;#rH%*2)Z*xZ(`T96aS zBXHJ;;cxF6jttE#A@-7MW-%;#Q@RbfOWN-(w#d+2m#KfD{4KXG%eGCiuY4J!C%Vs) z{i0v%A-`e!%;nD=PTyuZ*U%QNYM#+0w^XJ;`Y^LXl(ish%ys*ne1Fcc)UoYNd4JnG z;q=W}+hlIA&N{5e@cTktj(n5Z;{9gKdl!9wE4m>)BI|s^v@IE)7npB_@-s{?HJ#r0 z<&Q_zgw@Y>#IS0rSua&An9P61?gB&lPR4tN`FvXs{J!Y>F!{oZhU{vl?B?fN4NlnD zI;t7$%qe}xFt=H`lK+Ne#c37BohGaE*drg-xW+HA6`sjpqy8X=QB250meKyXslA8p z$xPV`D~ntnH@d3Xn>*B9c>0Mg!+Lk@vnH|2Yd49#NnLv~GGSAlqxb|5r>DsV{SE#> z{7qjtURYl6Z`kBeIl(UTy5kxdmb>0nciGdHvYhoYn7J~usBz}fnMWInr_SeS`adJ* z^6Uq{PwPz9ci4C2c;Ez?Jr86A)=qrqmoUX_Td9MX#NzqP)*RR478pO}x0aX4>$-Kl zp{OXry*Vjz{_@o5-#C)Mp?O&N zki)(7_4nA*R~29IVHI|CyB})c7@5J>{N1@==WkV(y;F9XsaIUTGcQUy$FBO|KJHEN z(KBy3XfI#G!}2zp?PTEv#a&lxnC~gtD$RDxew|^<`s$VEZ0{g*-DBy}5FFL2xtcn|6UZdlj`U3NA8*`?&HM z6ptR3|9!t{!|8|C&Sj17SLSzV@-AztS#5f7=gaxYbqvo7UVJ|nG~r#^md#8%R$4E$ zo}m?V=Lu8g!<^~v4)L97QYTcZUfU>Wue}qQa4lQRmU~%8&eSf2`<{Dvm?P5-T!*q zDuJt#`HSp@b~BqFygS~r?B{A@SS`4gi)W(6Z?TG{D<0Q!R7Gl4&TkMg(%$3z;QizO zC-1Y@i2r^6Td&FZ@|?o7CgUq_3fvktU;Xm^F5|5i&()Yrq`BXCT?nw*E%;?Z-q&dj zeA~ElqA!$4tNZf2Fr-7uJ<* z33jZ?4AWQ7)F^+1KyX-&|kuV$F%d2mkHfJpSPN;JkF1^%DaN`QOTO zr#-myacTLshWr)9$KEjg=S)7yrkHq6POou)_vSg_4|JxRad>Ai&2Ai7{eC%m8q=i%R;|*cyfR2`eo1g_1AkrUp)rh`Cgy)s2z-C zs5<5SRnX;+d;iMp33|R|e`;5~I{K`vm^nlBwrVcRN|$HxX=|74)!sUN!v9Y87yF}M zhWInzc+XJq#rw|!`v;d(ABeK97h%y~7Rwu~pU?8Bm*H>J^B30>jBk6HNneOQ|Jm(U zZ69l#-~sk~`BiKO_ND}vyH|%X{byeoah8?A|I(%e(`)gJZ?t_?!-_5c+0UFlpW{H5 zUo@x3rM15-1^gKp|FTU?OlX_^GtGRZRR3>>Ot0+l364e{_6$KD8y`1j$6U^MV3*y& zT72-0$!SNane5B7Z}>%>^=11zQ@Zh*!poEp1KSCQ+|F`Z1--u{vS&>kn>6FEwRX>P z6)bgs=!?|f;tAq>qrC1TlgjQIWycR(Sq|$QKU7DzTU>a0YvPP6waZs_PCoorhQU`s z%47L^<_ygwh9=n$kD2c-RldRdD=7RGSAv4&75-hPzioc)_`Q1R-lhe`8lEBEIfSl4}n;nsn$>ud|Iz71Z>w<~V>>#7a) zt6t4tu(^1*ozxT~2;R>xgjoACMtV>J67=cvQhEK$D4gL$_!pIsp9(Enkf9OGQ~ z!ny1|)ADEXeDaYxVLV~-GD-D@uwzR#e<=OYJm>JiwJl`&>;tO7q3lgH3uMovD`@I0 zW4jO`o>89Q{-H62eYWiduk^-ge5>{_U3T1F$e*%ckDt|pg<)G}JKkH^6~}PQL&LOT zp+y?|3AM}TzA*m!X?!|t!t8(To4B7$l2j0H)bz96)tvK%f7;fBuAiTaxpJ5}Rr&TT z`|>@kY0n~w(t{`ccGQcU5WVuB_l7}FCI1c&x1CHy;`9ITZduHsdmv}R_cLr0DodtR zDm?zOqlfvf3)@|0pNX!U1U}4O8O69{>Z4qy$`_%W-`J~99IJlm80S^DlA-FX%$4;H z(_cwUaiHx=JJz#?*z*Chl}lz zFEv@)p#4s<(D60v=PcC=BJ1bxW6FK5H_|2n5l=WZ``a>`3q4{c%(dawFLy7nU9sBMA$@b$^M+>=B4_b!*}{`< zcw^%^#@qZ&m!G6ZGco0!m9G+Jnl5p|h$HXFDdB=>R{a_I2VP6Y=xlfRKaDv^@JCdr zG~@EzE!$X@-SB^FcH!+D9yeyqCtKzoJa_b!)`wX^*RCpbGAKL{mTB)YPNl^_8r_e>A}^DiDuR}Uhg`wo9WDT)pyrCZ{A|&xL)Ig#pYYGOv`3(`JBb@+qB=8|DM%sfjLZ<{M}y}FW@)5`)9$=CvD}H zUuT?;X8+a3v+1;>dzQ2%hfvz9Q;rw-C0D3BvcHzt$yLTO^(0%>h2IywS@Ji{eJaM9 zzg;%rvEl~p7scltPklb1elY&vwQa(C`w#40d;d1S!i&wtci%qPyUx8*de8Q^v$shV zG~QKVc(&jB@@M{k!XNxOuKm~gw^j0RaJ^Zx$0N%NQ<$w-KLj?sS#bW1F)!=w8*!WG zH?E(yewrqqmG5#vc8*2$%gaPw|}e3>}Z{{2OkWEEoSU&E*uYLFAlG z%eX&G@^~&`alPjFnX&|i&Mz!>7g+c3#~HsDeqX&o_>TP7{0-@QHSg6LY)x%bYuXRx$+r|UVz(1P0n*} z`Yl`TWhZ-uE18}g@D~wqX5(V;-!f$zyV0%nO^yeahvwue2p(lVH_6E+Kxu1|WnkuC zJGDtV4s5l~)0xiyba7|==%Zpg;n3qPg-1SXQ-{ziq=+sQ2k=hKDlVMe<@udi^@lH`di&*D zlI)Mfv)4C>U23|gdx7m%fO})@S+*wnlxpmmh9nd%JSoIp)~p)aD;Rw374NG>otzLNLN zV!7WA&t_E_iKp1lRPEww`p3Y~z~JfP7-BqQ?tl4j|BTOct%|mbeyOl$>`bC@MKQ#<-MzpRn(WfzIWa( z{r%^6pKafl?t5)>r`|1m?W$Ru)7HkB9+&aVs}6RoD^r}tkhx}C56gE=*Aym$J5xVc zH7_a1?_k^?bp95z{*6jE=JkP*_gFO5ccz$UgiRA>zvlla$L_<--V}v`j^=|Z3nue^ z{>;ezajpG^1G!)8*uVGQnOvkGE}>)JTvELIiPeVb+@B;=tTj`_UK~GjDJ?-aRm)Id z%GR|-!dEWO{3hA4v*RD%J>#I;wS3>YpH8}cfb)0n+5?K$ryWw*p4wy0k+){aglLD< z+B4n<{Ea%_JIb9(+sRsW$ab-H!%i_*UygUI=FQg@oHm)9%lvaf;yS?@6Xw1?!09K* zWVI{g4QtVEpKl^3E^j)^$CUjxnej&M+@(4@o@_d-&6c-n`}x+!{!(M{gL@P7gV=Hu za!;_$^8V#1@kVXt4US(ct)4L%=SHMZ+P`9B0F$O&Cwl2|1ClDn*X zixKymW!kyP2c(5($!rx$>HI$RHtRi&Ri}z3+%4)fOX$v3InS)`ojjduPuisWUzw_> zvWYOxmE4gac;rjZ$v1b|SH4u8p8O#G%e<-U7Cd}#eSzf-_bHREJ9M#oJIz#-P%EyG zz9T;Ewl8bVe}!_#I3D|gj|%InS3hz8P_%>Z5%Wt6ZRZB_rvix=jFxP_?`Zxx?SpJa zoM!&ihHGZs=MwfdtuqsN<6U{mjy3whrrS&#WNfl64@7wsXEPf4EjwZHU{1~X%mvHd zOchSXCo_D^p-&-{o$YGu-O~S-jpg=N?)|ni40`MjTw*KiX-tszzv;B$t7GX-W^l{%m9sSVF7@1~P?^*G zja6oAj?jjV_cypXUkhJj?^f6$d}kx0!q+8o=b27UnlpvvqRVnsrCOoXL)x829$)f~ z_c1XFsxkEMa29@`sGciX(0<>8Rip6@yG-IuR-MTkzA(ODd7w~B&!8(ZrL&~{!J=~?S2xUez3&sjchGV5vRVa?2bx7r9}ENU zw4cypyT!@D_NZ92X~}~edv>`%ft>X2UY7kW+qlabrf&0`CGC+L`K3}Q_K=YJ6GP97 zYMLjw9CCt882>-@a<|aD@k7|V#_IelJ%)Ul_Y5aO${sggQMT-FQ0Dl4;8L?F<7A3D|Y$uamt ze2>WGRDmTNZ9g{(T;cNj6?^dAO{QE<$1K~G-HLg|HCFp|kNg#kKQ*7}U~Tp>kq`1O z#&Qx~z&+#n6c*E(aY4009 zZ+WJ8qww2AIf=B_l?+$5`mbc2cFZb5>H&wkC6mYosW1T@uk0w32;thBvl?tStoz2W zW_#+Edk+5EIkt}GuTSriG+FGL>6Xy-c8g|${zqN?#uR5&X%9c4#hcwWxVkmmetfE` z`oP_?^4O$lt2;s%P9F7|#`rjO#T+KCT|Pz3Yizz(@LD7+RkgpNU6Uf(kW|*j;E_AG z#J=F>q}L)7m}}B^9(WafBAzAe^m*}>Y-{-VG95R(HjACr$osK9khN*O^7UK^7UvR% z*oN69WnUfap7YmSSZ}d9^x&$5OYAe$ntzDoY`XHu^g{72M|Ot`Ueno&9ac@AbM1lD zN$qtk8ESJ+nQi#~clSG~31`_(n@*TB&FPUt->xl-9OA!s%w4cOwU@U^VfE4MJdRCl zyK}Tyc9(S6vRG;GuyF0%=))TABW}w*>GrCdtQ*#S**4$N>V)gD<~iHspM+0fyykkW zIs4JqX|4+Ay=~7jYzoi+#1Nx;I&pqeN|AOBlgq5rx_9}b?%jKC;Guo`xYh^*nm*f(6xTeF+} zpxN4VU59$X^H~k^xN@%@bqL${)cu0ZnPoOC)1O%X70c1izisxx%gel6Iz=h+PV9w? z2m7Z9XS`o`N|2*o^4x<~g(+6A;=brVxcimQt^L4R4qm?Uh6@{Cta>Q+U~7-m)4g0% z_`YtcnD8mkdWms`>|yr9kq39nZ(0%hAht(J{vP|nDG#L{URV4QqT#-Rr)AR4w_@B@ zr}CxbxrHj;zR}x}e)x8(TFb7MXPYF~tT`<>hn=sbZlSn=@vBu^*uqc#J~fqbr_%Wo zq91J5Rq)+Osh`)w(DhnKpX<*a*`wh?sNXWIR)p(!*Kr*#4?r@(*v?_wx9w*$JD!NX}-xb#~vSyaRs6HVZFF zUb1Qr`;(6M>Di5|I5Yn^Cs=NEKg6FPoSK?qIpNSP*K^Ev%avmpH{P>h(Nx)-D>dPq z=+!+5wUH|=57WqU{$S))q-hz4_hnvduzXBj48Oe*4X3yg(`0b zyUk~!_?ylc=@oIkk=A^x&rlut{3rXt|H?ZA{xJRv-QQc*n9z6jVw%96?Y-A{nnY!` z?|E?ihRwXz2gx~POEAOfRjLhdY`haYV!C4Z?zQg_DpdU=`5^p@c9h=&$+I^+ zlV)vaaxm<=88RU&$k&+R=SlgStWVU+ofw{|q&rzZVCz?Ib(FrLB+F!V`P(jIh9j;= znG}OuLfPet)K{?-%&J|;^Zbl=1$+C_CA|U!8o*3C&D}Hb+o|D#MRBu z1T5F({}4X#|KhA0`U0K{K zCDoe;?&p91!W5zT*V2fmBEz-mnS*rotyPRGC8s>u@@0;yTdaxqoJDGpX%Fhxb^M7g z*vH87_KSDJgXa&}8lGJI$S^Oxo~eahb={8kZ~<>G4t*t`B97}1<-!kc-E3vW+_5b) zjKv_YSc*gaZ@!z*>xLUf*<1o9myR+_Y;%0%@=LXd>4f|x)0dH|f9_sB5;}Wx4%>#G zjF^oC@$Uv-1t#%;g! zBFx@>7I~2`mK*J``Bhal{~OUs!c4xt+N)Xir2D2?-_Uz|gk^(G*{OMKa?$3G8oR!U ziaPGUzW19l%j&nx*6at_zs=@j%gz70#r8({wNv|=5?XmLhzi(TxOR~Fg7mF}c@4eh z=Z`h_ZVAs(IT3dIY?y-a;gdW?2fjXKsMxu;P0#$qbE7$u4~iz7Pi4?ND=N;mTJP{q zu1k}4-pk#Pzou80dA7#-o6!zZ^Atrr*ttE`6BZpcX7SKoeS_i5lyBE~XDp7tvE9Mz zZ`ki9pXp^O%ytuN@+1t7+LfGlc(UdG6(grZjV&n$^%tB}{%#`oqhVVWV~*X+8GL1P z1-2fTmhg@DLhtN7{L_Nej6_oA&psqN;qJ8X&CJd_79=}V75lAgkUmBH1 znP)XOe4VpY=R;Su#^VFCZWwh)r3{O~3zS-2Uq+BmX&?RN! zcheoJd0jE=X&7%;8^Ijhq8Cf5f^w8F;+DS@~dxx%a7tb6lbu zpDf5++F{Lk?byBtd>{CBzvbO<|IG{$_V?M_jY>EpR+`3^b6opr{YD@nbau{P$LCS9 z*<8DBPQ9$RAoH$lHsh_^yjwI6=&oNI&c4Qd1-E8H>6a(A6YB0|zf=3L^7QLow)Jar zIQkjB&scnpIsC-aL<^S59@3nRM^7j8ww`#RkbgmF$CB-u4Bw`n+0Lw>{M2EE{Re6J zb|c319$RFXBwvf2NGOnvne&?AljZ9t#Rch4J9e`a8N4{FabeNK`+q(<{7qoq=uomm zU)zD{@6G+JF`Lqb+#270v+sP*{4F~`J&6CziMat1Z>qUezA)+v-xOQI8rS9X-1h?C z#9QC=48pb>ial9m@2%H#^%Hj_?}YBEsB=vA5%!vlcQ*c=CX}-8DbFvat&xIDm=Bd) z&Qg%yJkh=(^xN*Ij4N}j-?AqNdh4p*Fq*TL{iZJe9p?{zCmTMnY{&|m9`qne(2VuE zOWWy`4doKv_68S)<1{RozsdR->)vPc{u-s zu=tw)dWA;>*?lHy)-b(W5&dXgqxsdj#g+}TYYw?6ILbv_oK&9WN4*tVe&l{Ivjbao=fgKIJ?EUb9bY+HCw9>n zb(P%fDu&+5Q)TO~*03v>T(QWZOYNA{O}1qM4EDv18V|(y zCw=OV^|z{@R>oBD^{GF@`pMV%4@gT&v0k}yOo#p4jXssD>b>5ls`ETwa&BRK@UW2S z$HcjF8a(cL-1Uf^u<27*3e)>(>VeFSx2+n58Gcz*FgvW;J@2~(! zT>Tr&YOjPgd|$iOk3DwT&JxoI-`{@OP61bg*h3q`ww(%Ve7i;H8}kj`>Zp8%^*2T4 zvDqn_&TsfKo%fQw%KqI-KOOaU^A$-x2`>Dkxg)UXP%Ymj{`9{N-*#QS!!GGw`jYuz znWvAj;UxDttdVK)yP6$b*XG@9$o^)=?>KMTd{y}uIpWvWA4t94v`#)Ed-w9?4s4!V z_}D#FtMA%y$bDAZ>hSy{v$8|{l$>uO9@U=yIyXY*T~ig<^SFNr_l1+EAEz=+^7hYU z*yCg`%i~hq-A%2EjN zcFE?@X+{p|*N;|nrOf?tP;)}cwMe6;=;vuwwGG?va#XUtkY@?vdc(JRCx@e0=2~fm z6)$VV8budvuI1h3w>Z^&LVVXv%Y+3MvAo~bOnH)?&}^<*!nB0{`yOY7$lPw1#_wBW z`E4FFg=|=RKy;;tAV;UR>@J=nzwpbWKZD>!`D zDV9rbdAa@x?jnmF+L{Vh;XYSi*gTkZ!?{xQ!&lb42Gs~Ief1sZm+o$HoIT-~FZ;4c z{(7bz=FTfs8bc=?@M^T> z+ZMqzVbaGXtXvm7_OZ`8k$o@Qq58MU3!XoDn*%1q6wPuy;Anj|VFI(>_N5HcK8|wC z_EEVv89(jYqp-i>O?v+nw(HFLF+w_)Q&W{LIBuKFoTL2SySTvp$)?vVmt3NZnchsh zohtXj()wiHhQyf7(hou>+CR-MSf1MPmdS2|keT=rj_)bFH^dhGWPK7GeLP}A)}+?V z26xrhj77KDCQHspv|Y-vLOp#?j6&>PwweuAR`YcZs8)8%I)v5U-`4nSnnbm5%B*tV zRgJ5YPv5=#pzLJBiggF%POCIKl;3WD#F*}(S$%LFp&VHFK`x3N$*J!^m>5H!4 zuyI!ud$VZpd9CK7C;WZbv*osIN;{CqH(|$)pDJum{xi?hQ1~Bf_gs4>YvFXcqsOMU z9A3=7-KK_f(%G&kiM@hLG_T%ZvRP7+bd>$&tQq$`imIkFOuq9@^Z{e(5(R?>=NP7Y zORerYM0wgDwL5hb_^Hl#N(TeWv8cYihFZ{Df-sxdL<$6XIcT3$u2lvwn zClyRwSM7MBz#Q8Z5UI}|pn(0}@l%R3D;m)>c85icS zVP&e@`+$q#y6#HTAhlT4lNVpV*Zlup<=7b?A0GjoNa0*Tz0gaAQzxY)$xdG4P#-lx z;`7Ib<2r2H+t*jJEIj7&L!cvlcPz_<(t8RF+tsJ6D6pMc^1qX_SzbN1TtJjD=I)WE zmUlMxj5pYQST-2*Fm9N?Mzz5<_dU~&vd&kn-zP;gDl9#DCi%c~9%Y6*D*7A0)tHC# z9`I9%N?X!X!oDGYjn#*UHZPM66WC+e&5RDdVld9oc5Tqu4^2FZy{_K0zH+dO;}R#sNcV;ZfoP0CDR`*;LX`##_)g5|IT`rA6JhR ziglFZs5eyEk!7!t$mnGlr(cf#*U z&hMI|{qwfVE$OI^ZfjU`y*$;{BWjta%?|57N{0`;*4z44i{*I6gIb0yeDkKxNpRJz z|De4?H*eP-Cb6Rx-ApeR>3c>mSUz`J-vP-B$_GN)RhYGNoFllzrp0_?&ycFUxyvC# zY3-Bj3FdbrjhtRES56F6*ni8XTyD*Tbx+e5G@kRYU<->$N|Bv#%39xU$F5C&n;L6Z zmEL8VQ24E}T583*?o$4M!`g3DubIx!i#?e8P0v$DWzXa5TQdbxHk3F@eTe&XE-G}w z^Ot$bjqi+3ZfiKR#pD}fhF7)!e8#?P%W2Kl`dja0C#>|*VUsw2ThH`_Z`TKPkI>rC zX${@q^>%)9@HD%?_@>~)QZr`B^Gh$hKTx;*(o|;K!%Vqc-&lQB)Ne@5J*_c8Z<4?s z2EE>ri}wy_8@0JR=>9yim+MDfoQCX#`1=>nJveqFZa2G0`D;y%UmjKyWR}bpd1Rij z%y(1Cf#_SVvl?SJ6@KHqQE^kVzM-e2e5uw6!(Rom8d5PIH#e`D5Msr1Xr{B~Q|=8H zueF?Aa9H_wB;Texe3xZ*xNrtFZVwkb5jLUmUF3GTC#Bh^yB8GiJ?q07smX1@yUyXl ztcwd4@AYB0cRemMpZCKSzp2&=-I_ALcvG|=-}T(ET&4LP<9e@(*)eP{^>S5(U0hT0 zuZVuQ?7l2Z`opodu4=Yv$|XIm3iIY)bUkoYUHN_E(yfPu8g{>$wW~_u>k{6N2Lj$3 z9d5Aw7yX7ceERWssu%Qrt^FmCDGr){J$_HEUNVAPHz}TRtIl3sR_%=Axxx)1%hY!K zzW3RRM`d+(Qf9*;8Fk};i*vVMO*sD{=}Gm3>#^nQl{U;i7$pCuZ+6OG2fIx{Wo$Rp zi&J?Ddi`VK9;|8I%bM_b=7aUDFEjtXU^lw>bjnHxN$Ind6Fy&S5L5UXQy$Lw#C5yF zwFaF>UDp{blDO}bABdjR%CVy`=UX51@rdmGO=cVTQdqBRg>8EeabEmCeDiG0=wqaL-dh;eTu?*1%QH-KXo2Rny-IVw(_#cG)|j)33TV)+`cV%D5vu zwRbz)lk)2g+aAoi!4uA?<2dII3HOXoI3Y@a{>z@=B=VbVH_)@P_)aNZUk&bWAs zwyNTeEbFEt?-O0NHO{@35zTeva_{8^8sxqyEP-#3acn$`~^I=v+ENoK)CQ zz{j{}$;F?#fpZl;g&#TjKYLPE>ttQef7j>D`X=93rRI6+{`|;GI+n#jPlLbnXZ?zQ zzT9(9Q-~Tr!$;4qlkqpV?s2hhn(GnNxUE1fbBoaW0IQYz4eR6ot}pbN{366v<>k3Q zDzp7cl9Hb6Z+;=%{7v`+C&O{&b&QHe0SYVG>q8SJ*1y;}{fXU-&Yu4T`|E4}g?Ieh zZ~L3u^HbHOt1<;m!gCn%iYCu?(AijT|NO|e_v%MIZJ%%nsKn+6Uoxy04g0hD>XIYe z_3F)bvsB`}Cas**w{?GVh0t$Bn@4IN*aMF+y_|IAQFZ4@rX5pFR>fV~ZYFAZlB-I2 zg>+8alljNba6fH26TMme{6*<4ffpQhH{ZLGBgXnqY4(Db4(hLLpE+LeyB%5npm^;; z#e}I}r*B=L`z7}+bJ%sah};dU9QCp}9R235XLa1am8Xnjjh8{tX~zp!%~f&I5#H{ASoAdPwFTb7-U-%eNDHo1^YWwx*X+rQxL<1lS$zqaSk#sBU+A;!_iI^QL&p+53; z>v87rw{xl`Gv4od#kFAn)y+Q@L|5pG9=x~p;x%~-{i7B9YudN=u3V;6q#+>SqI z7_}EU@8HQvSh%G9L*=Zh+m3U}y@VY4b{>0Iy`f^};RuDjd0)0Sif()OOX|a#)eqmY z>u)VTaFwYmTG)u;a|6JJsbl|sVYNUON1d`%0hh`8xU2pL zUS&DX^&mSr`;(Mas{VH`(yZw=$f*${H6QQ`1iqkPK`JNG=F$|QZxu8-N!?D#!a{V4Viwm00eS8zKd zuUIO1z;{yZ!@CFKWWBu}82{$veBmcXe3|<;b}Jvae6V;IOG);j z?+3X!q#{c;@UKW*wESTI_h1(FgWEEarP+Lxr!hGQMl0wy|4qov6@1}rG*QMQ-TGy8Mayf(@TbC0+JQ+pU*( zzTooghv|X$!uCg=IXf*oF(pB$#k%GCZVnfHZuxI@Y__?-V>v%atL^dC{9ymZrd}~4 zU3&Ri2fHbpMuJafJrifloD~o-kJUUPZ%3Da2uBy2ytC8KmP4FR8+PZ3JUR z*Brk~hZ4S?$nSNSm)LuSt@>i*2ga6re6iELA6)XXU&a*fnR&SJR^Bde-jKrMi@F+X z&kC()D>ykVP<}_!_fnGy;Ze8x51gJBca5QV=4!bKUDuUYALxDLdzRK znC{3I9h7C&e7))9W(8r_JMszZXQ=ONjM;XzoALXkJ+HHx_GlWX*uIE(oqfO2behGs z#wioqcL_OJI8O8^mPGDY`eIRZ@5Go zOoH648)~1}Ewyve@Be9Wt9b?Q!r+Cc9E$jM8B|=oyY<8Dg2a0}d8T_EdS}IcUe1cW z>V)a0SqalKl5QT9V%__eFJS^ybj*~RPVO% z|E}1T^HS`8X2<+@`rrNe+#mJd!rqE$|EhogsL#>yeE0Xf)o1?b?ZUB3{;?&w~%5H`rUsK4G?9SN+u{Be8qib%*!25?AwFoAxM$X?0TOTh;|jOVi~q z%;MQ3^oDzOW_3f#92L2YMUt6JOCInWHO^huP|LQwZaMMqfe8G>IvozK>{^V37- zhLh2s2X~V@fmi$LB&5$&<92v=vOkCY(#zWiRW`6nE#-H}+7a%2o;h$*zmY;<*j#(Z?W|Kx zBzL^ecwW{Rv%G&U!{3uzC(ac(xinLUWz&@7w}d2=o@?-aS{F0nox)LP|Mq>%w~z7Y za!%6iSjzgMPFH#fqnY=jQ?q8i-6WT?e(IFy2X){7m8@)d_D!<+;O`Ssr^*vn-nB?; zn71YNhrpH1;%kH_+}alHUXXt}!jnI0-iCWDS})D(lqZxN{5aIP%xT4ru{Uw{s zq+55GH9hv&@YkHXyiOuQTjpkCt;pqX559TYt>k@HeOOgIVvbvk=Y((nWXk#P1m>^X z!(-V&2hA!XmvZlzo7XAMU$Xi18#YbZm?@tQNYyV7=UURY_w7=qMPK*T zOMiL&pu~xFg6X#SO^s94rEfRRkx^r?)XChzEF+Svy5m#MLYW1#ye2sp1f0A5S;718 z&s5$U-g%pBnSHLG^H%>qJ=~kj9$F@UtQ(Y6*vlhNHW6?I9 z{ES`l^`%{l6LeR+Vr#Z><~A2wQX` z|9o(-YUvhRgSq8@PCJCgZ9JHunbV)k7A>B$t=hr%XRBL+>yDiJ4YlVaYPr7j)oR*R zOns@F&%_=hsMa8+(%;KedDriR%mj<9Hf^Tbrwg`AEonP)s@mcH0S(reiJ$ISDy+Vp zo8CCH$}yhZdzobigXGwpsFxRIDb~9|vwtE;EZ2tL+)&Gvj^=sO&!bjPV8c zx1Cd&wKFs|dA3<+B+WiJ>$Yz;pV{@ITT&ag>n^>^vfFpgG}hZrx^G#xU4M1c?n22d zT?u~2?7zkx4Wi4oiLyy2>)(_}*l{++;>7$n)9f2#)@kf?P}_ONi1QAM{$v^UpFvfp z3kqyAUsg3;DNbC;zUjo=WfD6~KlQ!lHk%joO)5i2d96`E^ga$==1h611>p|ypQLZ; zPAG|)#&kff@|IlV-ZiV=7;TtSb};Thar;&tkNBT4ml)4&+AOP)Ub|K_Lie;^<%Ezp zy?nJBuZ=vd8uumsoXK5g@Zw~N!cx=8TNRdiJe|srbyMcM%!Ye?S@|34qGFgoczM^q zWSaA=w!0y$?C={V7X3)C_ZC0)v#7pcez)Psuh$N{cHa$>+avbyU!K&C%12LhCtUf} zQ0NeGYWKy2-@02*a|OBlif~ToPAk)O=-Q#QiDOCo(rG6j{5o|zPjAQOd#`PpPH^4! z)#7-hGrx?{=;n1Zv4T%iLmC!zi(H%Epm(H5!er~zL_>p{_F_LJ_8eZfJeqOZ#nq}S z58PAXpX#`7gDW4?>NoqQF;ucTt#?|n)u-nk-?rXcF>Jd%^JH1WPQRB+VTn8$>d71C z5q-1n!qQLH;*FoyiN!bGT{7n>>-KYY1^b&T9R-UOZZ?JaFx`9PTF0<9tI$!dVE+jt zOQw5&0``7jyujx3$*OU_qQ&~<2icTGzIjCBA9y#%V#qH!{}mQkyWrc;Vb-Yu#6CW9;>IepIN< zvHpH=ucQmBaO1xx8x|()J9;zZ!ppMbe9gR1yWX;Tt7@-dc~Pake)hq0T2UM+bE}WW zvCVwLwd=3K&9^N+j8-p-C0^)Crba4ct=b$=I^p6bLlfRDlFUmaBJ|(auH?Dqo1AO0 zqt9qc^n-i6{nN`GUjOiY$)U3)`;)0hZKd0}#x+kWCHakl>^A*Y_?kK|kKv_S{VAh@ z#V4PzG2aiEC8Dr0w^5YM_9@eC-c6^bdFgM^nKmQ#t=9YK+6nG&rhH1cdDrPRN1m?v z-oFRjt4^G9{;+K8W?z9_XI_W##+cSkicXL`q4WB{rkBh0c)oe0Mayo8*mfgt!mUX@ zaSWE{^(3%Bod0p5RoCsuRzAJl*5ts;6t)$*M!gSBU3wP?w_e=FxY zUjM!SvVf9tv-2;_AM@5-3U0WedYW;U8&fRf!J`ce%nMp0b~9OIZTR~C%jwtu?dEAH z_*Wbf{`a**mG$I-uvfdc$^1BX?z7>Iw7*xmoD43i-4Ia(?q}U?q0jq;dP{RHe1Sa zy_*NcHl7h{jO7Wt>AJzxWP|Ae6F+0#7n4HvPU>59rQ*>g1pnDOK2UyT`$q-!HFI9`ZE_N0_iYeg;V#N?Dw^}0 zV?f07TiOYGbs{IoE}8x`S;nBB^_!xA`s^ph3l`aH-{81*nAL_oDA=oQQ^Ov=N?lf? zE3#?Y8PU-Se;wU|IwTX^xAwL*nyXxkWtx6kb@F_Nd5@_;hxvVTFb(rfL?xZiuW75n@{=v5A*1d?22eiE7|1#%fZ#X)YsqYknCDWv1z7LcY zk}6klt(z4sVZ|uq&%2(xK;XX2j{Ftr`c2jnQ7VZaoYu8Y7i7}EbTqHY{@C45_t}5D zv3Z!Bczx@bDVxv|sbEL96ACl=d#1;)V7oNyl%uNxf30$Ulir8dD;P>E@11kLFlFK- znUtk1cPk3^xBm^`+F)vRPMY(Q)`?dBiSyTPm1q4`Yq43%!#mT-YC_;O7dzJXm8&0% zvb=c~Kb)vN(@g}=Y_nd9_4=Pm|M(};=JhDk>$)?-tg$l-RxcwSSO8M_t z6>K)$e478w1@ooiKTO{yN3%~{IqlMaffqW5U$PiYv(2;I5%q26Ip(&TTC3TnC0u{2 zcSFKDJ-9JNfR-1MDpsi}ID%%Ms)3V0s<55`+PcJ;##hTM+ z`k3v6Y`K?T^V5m>Gdb4D|6D3lar%Zvx8jLcWw#k8?7MgUn)C_}t9#6{3TxM>2iQ)z zwysI1$n_`hl7^=T8($w4e`nTl%I6b9>DuO&gMUTdluA2(JC(euVcU`=b!=9%RvHQE zbY0&&zj6MP*tN{xcz3?>oshq^wU$ZfvE7s@52PkU-D9{G$B zVfBu;;P>43_n5C6uM*vUU{l5ovxeBquPYXu{k!m;%m>Z66=F+-+f8&08f$)O6?7jJ zV~+Cv%r3}b_G7IH%bRI4KZ&r2c5X8k==o-T`*nhG>4Ixa|4n@*T@LhR&e_I#EJ84w zxyohUWoH44TJ;J!3l~+si4xa;EfG6mKUbaWgz&r%E%mlR0s>6h2d!&(qwcMXth{hu z^Qh5=Pf^_>3*@s+wjQwZRE=g*dBJ7Jb}T~lHpjO%JNMWHGR&In+xqsdVP3uP{;kys zd$)9KW&iX-e-CTf1jcLZ70p)P*j{9uUGw!IlWi8`2Oif7Hl6mpZhRB6ist$%%uJb+ z!c^(ccd}Yx@6t(M*jB1kE)_cASoc&o!HD^;v4Z6t`8>4?b2p?gXggP*ulGafI| zD2VvBkxSz2?t^&~?!JqC$#2$i`JF2oeTVFC=nqBir z_C}4_W`RTIcRqjOND(n!$+2$rZ@vRxljh83e=_ayC+!_y9-S7O;2mT?M|2O@LSaKj z5Al_{?T*)WyOs(4nD^U?YmfJo&20}(?UY=nYBNu^{!l!xqnG6AtObj|sIsinJsj)& z;oeuh7Psa_VSzT4MDqn-J5wg zR@O0{aM|CuoqNt?o`vlKnv#(cm3&Pnjqd#ye6nQeKPcB^QZ)Z{)dS}1o#76vPHtakb3!NEgMGufl&9$%@;(VM z1~i7Rky1JHo!w<0tL8KRjqe?bs^p8=zWiRWzm{X2y>l#w#6s1XEEAJIa{pmWn7>J5 z2FIrU&moN5H^1+ax|1ud!?z;&^-K0=?4@tmPDGu3_^#P^+g&s1rX;hwb*#PH58jek zvcLARE=%`R8^^MO-nU)XSWYjqf3@AgaNbfm59>qk(kCP@+!G~U623vFLTb;mg@^BP zKd4*Ou%`8a!-SK1jnz-~ajtJQX}$c_w^nk=o;{A0A6`fB=d;~-f2O>1UCHLA=L#0b zv#+sOWKCVlnIZT)XjRi`Z%?C!8t;0prae#gp3D~bT5~0II$M+WtX5-%-n)siEJl&j zHcNZF-|H$iAu>+ZoBzutPcx33zAINaAFSV&k#xJ==DQqKFV-)(y|tm9Sz@L5 z8|e$JQ@GQZpRQQ0t?qPvofwwp~1X~%ZHGMfOPO@*3t+;yX zR_ccAZ%d~#wMDs~WD9Zq!T4s|-K#MNa$XID$D)HmbOfqKg~X_g#QLx@8?yGTZ;B~@UPl* zYB#^pxp|8uS>yvv7g~EH*G9_TxNI{)JK=rc>G!N<=k!i9CrqnvzLoI%g}eyMrrUaR z*-SP>=GkV%W~;^?j6WxIm+98Ed{KLT74_Rfx^8vqnf#YL*H2_>V)?#%4YTyCzMb0@ zw%q%DaV@*%x}#exZbap#H#@AFbUC%b+V=goiUN*2lYQHM+&VvDk<+|;D_=4H&e^z( z;nj}NceMqp_tw5seIWS$WN63gpF6%TjuNo9et7L~>xVCU|0K^7;t(^uCCV^2H|wuM za%^^3(vlLpNY`oG?pte_?FGJD$4Z{hAX)lB(y!_N=$~rG;%Wn~##a}rtIc?C) zcwE$It;N4cidnP5H@k`VnX5K)U3BE5Px+rffiJw9UP*fV)Ofn@#>xkao>abM-WK-kvW$+8 zuqL;OYU>xazsI~YcRD=V{q0I?!koVArxTiM9_sDjdT9HJV~v>hOn$v-B|$td7Pmi* zOqja*WLd#-m7UeROEjl1)%+kewUT$wm!!C=2TImEIKpnbMKLSP+st;uDlgh>!;?3g zEfY?$|6!_&@4djZ;-KYQ-ncE_+glVX0&DB-PE0Sl9?k!5kFBod!M5Ozs0Y`o-!3u! zuy@MCWR7>y3Gt2By6W{lNvix-z4`6MsRwU4WT!k%Fg|=VSKtKCvlg$W@M&)r<}5g0 z>Hej7fxU1JV?^ZKRsPK8FW=5*EuVZch2d&K*aYz>vlY&zGFE%;yU2e~E7SQUhuO)x zWlSE|FNpkQ$T5x1u{xpcsJ5PGN8T=obYbTv`q^vzx+(#VzFDAt6p&K)lK6AVz%pkI-Jk^mtY>D zm@9aPReII=NTUtGyKHVSr##e~-@Jr_&9hcP_o^qiL|^v*%%)c(|dXXH^&n(4V6=ZjDazc4VLb=2C+lhDit{vctW=`On zI)gc8x=jqbrsDD?EGP8L!^;|1>!ik}7R;=8VZ=E5$%GuH$oEM<7**O&>l-`xowA(C z+HvNJtHNy!7luu7X@#O6_Ri&DX{*|}jYnc)KtJ;WdDSQV_xV=7Up{lfBR!S%6W>&n z6hBy1cQWn6Ezk2Gyf$o!D>D~fvvH*@o9c}Bag4G#^{<>iSihL_Ae|%XrjjK8H<|Dy z7CW}wi_&D7X1GFpzu241)35a!s-{euwcxJF2B!yWx8Bxcn#lf!pO-ar;rr;a4{0rR z+uk4Cb&EfQQGc(_a}JhM0&b0=7u?O{bR-Y&X;TP%`1lt4jF(lr)(O-aL|%LGHlbPW zPTi9O@7NVrHHN$NwS8-NT2i@^<&*VG70VaQHhcLevejAaQ*&A0t1I=DyXAjRmCRl? zC-EH3)e1KjW#(wUa42_?YnGipf2nl^>lfqeGBH!`Ub?Jsa^=F)4L8fKWHZ$?W{6GD zjVu3nuqrFkTy)Q~Pr`Q{)Y<1Y%~>G-+W4333B%{NHy?Q3VaD)(mh2*v4<_=L1fB#> zKaX80=^)5M`>GAEG5A=TE7h(PB^8AG5gt`hI2D@p&HLO9ic#Ie;65gM% zoKSw3?RF6Pg@tx2N|o)t;JmBXq)I7NN9}31P zJmC(Pqd4nWnK~be+_?U$OPB5XVYy)8CEJ%w%3g3kN|~!UM)bbrhjS;&bJ{Mb=I?en za4m}cy$Z|SX8o<429EJbVjgo+`m;FiiLPH?%J#pw^I7hM`Oc!b@;B}!v{v))c*&&8 zGG*fAZG07hQUVO?H}~93u=UiAbTB#{F`NHV@6mT^C(?H6oOOu%!uDE~qxQ({xbP44 z8$&ipr_7sv^S40CGSO$XAIf$t{=;@}`@INHzJuximOQX~Q6POF;6caIi@P6e-FWPh zMAOb7?_bQmtm}nO$Vho`PB7dyB`JaP*CbQ+X*(m<^WUg0J6+Zo%3btPcEkDAhYJe0 zV{AS7fF43%-^ty2gAvp*WX0;IvjS_nQfKH#r|D+kK>LgV42`UJa@S=Y$&UOqTI` za92h~HP}qM|I~1YRoljwQzV^~Pfw|(eSi{!|yQ0tx$E0rB zPLRDb^`gRh<4aeWuiv=Trq*a4E7#KPsB!MK#5%@@^Ui#AV_f?-RL^nAj^3vhKi)QF z?B)K){mt=%Dog%{6IzY;&)(zHb9U;<>(ybc%~}4I<3{c5^R^T2&gwgW8Qd{EnjU0ZYJlh_4rNe=@w z9eWFuC&*sm$Ys8r(C*1EvUKtDM&IJhz5WVY+^s4fAN)N>e(&>xqMxUEM|bpb=6)Aa zST%e1j7)YK5kxy zs*3PC85`Pj5;!u9|2nrhZ8)=NiNp@;Pm5ESXX-0AF)o-NwZ)V{xJdE9Wvhuo3Dr4U zrZhe9E4XjRx_?r=!49Upi7Lu07iS-Syo8~}y=Ac_=R*HIb(>^kWIo5(oZ!?uSvldU z&P34%%O>CSW#4(sYA2hcoTcm&Ms4>Sry93z+rHK`qA6}O%Yw)!sZ!0ZKdaZu-H4J^ zv2Xgf(s_@Oz)azn>+2hIp0J-~&8rVeJm8-*l& X#KjXV`{jT9ZelyA_JA#W2RrB0zwQUmKiI$Ga#QYu zyOXwj(vEl+P`#Sx-rE)XCNVih-ii8d*}}JL<0o#9sPBAB8$Mdy_%FGpG3Vl|g!30W z{2rv8;P@#QBkUsIU>>KnL*{^Z(Zy{IA=BUPVJxzjSaABltxSO&#xL(wuXRSmhfw7V9R(gKPo*-M?lZd@sA_ zVf%r4x6^L3oD2ew|7bl>l$mGCy=`hp7(+)YzjuSA*)<-Wxo@|>IS~Bjfu@kIf`3=& zpUaht8f+eN`u_+&qI&i2PseqQU$<3$Grhq!`$T5K)_2p)8yL;*$1$#6{QYgI!q%%w zrZ==!=dW&<6S8T}ft=_gISZEV^YFf)R~5IrVeiS>YT-9K{+=u3=*wz-pb=pdn{u+@ zY1#Cr2RL7z;Vk%iB*r#jiHij1mn~IaSgy~Uz0TOf?`KdEQ}0u6spd1K_fN%4;1-#{ zt?)5#pGw1Q-Rip<50v#hs~1c$*;;%cHCot)Z5|(2hUbG#Zm-=o=r^yO#(yXC?wg|K z>d2(Ka^TKQgog_0xH+E@4^9DT2guFyQcnR$uf@w-eL zOn4?~AJE9mH#{H}z3jYG!!pi$Oluc2Gs)YeuY4-DBROWu*$Aa^7;S z-#mLkbncdKY}UCm-`KqPowBi8p|Zqoenai*=Wi4v>NAfAD{!BgY^kt6Gq#IacjXGx zgKHC7wy?e5Q5|2$bp0S#)!u`9b+Yr8LTY?hk#yXPE8E59(8^`?~FlYPvY*>g4dj1KPG^z6aX35n}WPAG4` zAj(>PL3F}ph21kAdo{&Qzx2&ELN7Z@pD{nm_8v>LyPP)fw9{p;Ehc33Ocih35x}3# zk+-_$jV$x_&|4L2Z=2+ zVy3qzT(wZQc9{M$X*J)w#@8N}9JMd_PX1PSyp?;WcN7TW-eo#^sR9Z{{Xd&X-M<3pW4N zImpCO#m@gFxZ@1ZG?!V7do@>Zt>eo#6tVDMlgIvjXUuZG2w7`Cn}{g$^WPd%w{~rk z)tR<*8-s+((v;j=|H;8GQ|_y&AQnf z_OwQF%$j)elgW+gw~owf`w)Gx_^`R+u563uY%X)Yn3L7I>^3#e@;?7ge)#N-bZnk_ zv$lMUZDlg^9sj!WusQtG`{QR8?q!RSDl&Y2_56dRmE5)KLYq&k>x=CVQFXLl4DBVSnPO`sKfV#iORLE$TK8IEa2ww0rXTvk*xob8zojhRE zyO8yUTl6tE1>@%@c!JuF2eI8aWxK8EkJ`(p<^?DB_~}WWa6PFd?s#fKTvx-Jr~ZNy zx@;zNJ$P0<_fE|O&7zoP4B;8)MJHS;a?xv^t==2MwP0CJ@%kp6i`}10CiuIZ6q|4= z=vEd(;U)(yhL;^DSQ2KLO zy({kA)1Nhg;a`fTuq5xhRrja9d@vz@&*}%qHk`|5aa8=y)$6!#gK8d!!L54}4=c3B z-c;Z{;rn}T?gH6ZyPr}T`Q4JtVW)4M{(L}8-G}Y%+uFms73Ocg-qpOscw(o*UCA?? z3ioQyT21)9&i4BO(`U}xm_*msUf{UH$=S-*SbejVgZbr*^S=e|Y;5=`AabpyrIAx- ziNS=hGFqK3d(F7;+LAKC3%{duI+&yrWZz2N$oJW(rm*%G1T*eK~-p1l7lS(ISTyTmdmU-HB(~Wit`Y*-TDOr5JUn+V~ zUqDlE4dd<1n`?9mT3)TwxFOnh+G>LEC&vBE>~|HdS?+MyEmjJcR=9+Dn}>TQQ^~aH zo}9P3m+fLnSv}o-mh+U>pi^!Icb{l6JIGHweXQ~Ow$rcGnoAySn9craZ@8zHbAVIw z5-AVwPaee!)~E!CDd|QEWOB|rX_lq7;pIQR+5FSa$LHR3?7p3$%^7yHj?b#`?xX`; z4o_x#9BbL-khY3F-LdVYOg5WZ%tJj7roAsXzp=_1E)eC{WlNbhrDT4y$d_wE{0rUg zEuX^Hx#RE>=9PNxPi2S}#sZ%ih=fS?_(~tXZC4E>``SuC(gfd6Ta`8RA z_f7>^alBnvbwRx1rcYL*w|e%I%7mR;6U8ffo%#1HnxLe$EcA+LMaZwfz3mqTa%ya> zjfCa4f9yN{zWu_7fW8k&9cyKR^{eE6%>E*~ntx(2|1|emho(+ncXjVp&PDh5*O#AE zFgbeKSK4F8tPkEPf=6U}{;IALnb2l(r8Qyloh#oDcx7qr=GwBoBAVIi(54pwAHK62 zYjPSL{npLwyL$E-)*BYHlbjv-jBaW%94@v0?wGaf);IPWN#|aaZZJ>L(QjI|C21RL zNn`J|xdJX5z1^F?8(GGA zAA^YX&3eZD=N(o>HGN_6O!v*z+R@*9Xr1;8N!vBg8=s~;ws{eL&E^M}g#*)lM!AFa zE0Vvp)HC^uOaLzksd7m?!t}M`)F-CawR=q2`P4&;{@s|j$mWLK*+t?9&UD?(-LQ1q z*=bE-8_l9vzbt#kVdiwfo1fMDAYWvpHOs8NU$gj{q;@P|yCIvMUw&ZK&DUYP8OgD_ z>IHMRd@?mS`E|LLM|7n|?!nXVLO*S4w$?sBPP6H|7o`O%oE)7^sODccJb&j zpQu`Qnq$KEOvU%jE_XsY8_aKLKgnXMePjGnC}rN&C)^8)0}h!n1-{cuKe&(IRNXQn z%q);UXPIq+Xh!O}$IVjtY_}CkX87Of7O0W+eBIY@+c!{}&GDE=y1k^#+qyL$-m5-1 zZ<<~mw~zPX9K-BGi5|OEuZnBhx8Ldg>~s1jkIVU%Bk6CYW{wl~g}iMgO9uufv0 z{4aqm*2-Jej)eN(m^gv^5clE9d!9F34Vj{)ARlzItV#OOrOM9-?mlPP-f-N}sFFoy z+VcoDn`z%ISU)wNSz>X*=KGwh?0p|+99KA*y55>OuJuD`B*Qh;7dhsQ6T{l(I~4zz zTgj(%L&}@sf3##GyTXaI)KbHW`8&Jj@fT{#y(wgoy?%4N{S^jvl49C-H ziyq`{3=nIoj^7+@SrPK@VaJZU0$;t}oS0FlkbdJ`QKS8twF}f(PN%NEFF8ZoHbyF8 zcIZ=!AM3NvC0Os+_enCg@w$h)_<>o*4*3ngo$4;=ADH(jZi(3qwaC|5jv1Zhr?Yu< zuJ-3D?KrpabQH7BT3c_o1v9^XHfwyktwM|8?^2be2e~2xU-Op5+ii4zaQ3_1c3p+N zrme9|zfagZDi-veoYy3>fk%jSQ+SQJ#0pQ|N^TY7l}i*q>}L7HH&NNAONQ~k^V)~? z9NVs4yXCgQ?3T(3&VTZb;cq#lE|pCaS|Y!`kl}}O*o&GA5!O$iGftm#dK<@#g%>w5 zE$RP#U?1xw@2Ypr_m)V!b{oHmok@g#M8(kM|b85Sn z>8~2kK5@s1BR`@(82mi^YzcF{(*)xn;ii9__HJ!XSB&%emM}@N=(Ch6n&_+wxMKTZ zTDAC6UJv)YY~Bm)lX~7OJiYSde%o@#KW6o{e^x(;<$KQgwrxdSVD=uT2V&LAPb4mc z3a_!;;cheG@q(Eti65+4Zr)M3&AZE1LPOQ@+H2)yOfSFgW7b*n@t5U|sNF(3jNOy$ zbGU9e?@V0ZIBkPfnBbiP&Nqe;kuQZzn5C7@u2H@rb~8DjxmT%^RY?Dq@-3!~Av}4F z^JCd>@F$#oG9iTVzlHGM?+OwR?LUeBIGI%u@3`U6Cw5^6savl4!Wq%G7cX`^mKptB zYes%P%lqc3TQ-J?m~`7L{;F7UWa$H;32ph6JWG~;6s})bZwD`5jtCrUA|*WJ!!sO^Iq5LH>m5(vz&0dY_gsXXga#%rSLx<=L|YJ*0F{2r-;QRRXqhNa96 z6E7L<;Q7Tg`wiQR?F*d`W(Rz5T$uCo^ufo^<^B6=xF!2iYSj;ib{sic`?tU}W>)3P zFHgA&DqlP;G8a(_6}qJUrRBoL3+d|wQqD}V%sv()U~0Q(W@16=x!&^p@HP|qCI7Ad z8Ygh}@Ab$oc)K@zPiH_v;=k!dzXj$cX#QYWla#UeHPg1U|E8Q%uzu5+&6Ko9`FwNA z0;d(4J6adIKa6+WRPi;|bwhL6Aw9;Ena8UhRL3Q1-Pmq+e;3mZyLaUmdh*($`8j#F z?)=JfIy-SMi(^d6GP{Q3E8p3De;~Re>gB11?A;E(R&wVtI&Z1akhjU1`@!*$c!B=6 zg*IADg`eL|_x-5pB4yvVv0I?j{@e5wO9kqbSLN=ojHvYQYHDUyjREfhs;+Y`k+$~^+%O?$yOu(!rkCD@H(@^8 z8vQo^2;iR}VszsegQjHiP6zu5 zSh}EBnx)8L*J-Z}HBk{#jpwc`yTtNBy*h%A^D&2dPve7SwK8{DqMgg5*}tW4xLd=q z=xt>#r`60uyV&nIte^Foqo`+b4X0W6&Ld6XS7&S9Y!v#n@F~mBxFbRh=|{yKzI*z8 zYW$a%{MMc0RaRCs%RP6aSr-?SzcFJz5L?`z!oK0EmZ8WS=~dBr6DCcWw)%jdR3O(1 z^~#?TDLTKusBYMqGC}M?t=3MblMcIX-~PtjqUCz_Q<=lDDTd!CpJ(6h!u8VfgB?%* z1i1-mg}bJm+|RaKeV%VqPrK1OpK94P-KLw*3G6+2`)w@S`|c0raoQKYh26+&etRu= z6SLCxMVF7iW521G`#0>vj?hErj<+g&$*x%bTlCOzo6l*CZ?`t=<(d`0>cwvb)i35R zSvNUF84It8PFXvfr|iml9Zm=1>_gKN_)8|uW8k~lZ^pG`<({;DNAuf}e;IQ2{!O~y zV1KXe=ih_BFRiNTR9)vu5rG7*LQZI>Sui?BY+c4w;j4i*)bzh6`nRxaC~n_&2B_B2-e4ZUWP zZ+b$uYA@KoW6?I2Ei<=mPjB$Gc+_wVbS6c;oqx$LaBHTkg^}YUn_$9>+>M(OGDDqCX2Vp|e$f%i*9KvaL@)y>&q{BM-+X3IC~U3NQY zbU|&_0^f$8-}atm*Y*h4=D4CFZR)u0^PU*?m#L4Jito7pg#WwriMo{ecFw>jDntGlgu7v*NQj_q93OZtGX1i?|0wV;!*zp zbTqU0{b*-xxV7^p*}(0KFCE$|p69pETHsO8 z(W{*`oRQUOvNQIHoRC~^rj?cG*Q~?$&Z9PA!P@PN2Q0VyWijvHA-_~8BKBwRQib^2BHyJ>B>p|M zn(H8Q?MV@#e;jHrBt4?7E`Q>2Rm_XLbJTtJgS`9f+vP-=>wE4i2XVwW>tEAmUuoxI zX>;`Qg4(bC-Az-zUsgRbrTN6Z6CZa}e$e>Ytn+>Oq2|(ZwR^@Mkw!O9rWj6mmN-jO z^A_Xurt%W~JCb$JkL}%l;9Qk&uBuJN&x6O>s};Ynue@6n3eWuwB!Ch3Tf^ey^ejJj*S3r`@mH{3oF}BI)zNsNd@|MK5Ik znq4m}RP}Pk3Cj&~lg^|y&3+p6SvKIN>;-NG?K{l7Ir8@TE|I<9P!zys@wDosc*5eP z9dDST6Q-+bUx@H7lE_G|UMm;SP`-xI;6PcBz=`bFdwd*%CX3@MaF&>a@Ok z`N6f5bSH5;>9@pBcND0yv|9Y#c*&eORW+NK1U@JF>|1g5fQ5NSlz`6^&4U&fqH31R zZd97)v#TLz!+a-34{zDWat-%3z1t?XVmbR8r3k;>OO_v)wc}Swd&2y^?ot=ztd3r5 zo_{A_?n%3Xh|eZPff!yh_OA}@H(%f4ZK}0$_}(P)28kt~vXBV4wD zHK*zH5zh<4cQ$Ar2wtPA8WB;yDI{TS>CCN6maoMp7bhrxozcs1?zVGZbKO}#S(bx= zn_eV-V80bpufW3mMYmpoWm)K-w1nR`^1B)Nr++zCamaome6d@SP`vuIv-h4b{?N{0 z`K^#4e8_zVtGG33GMA`5Vj$+fq}^JiLv(y%#K4vs7Wn^1b|bFFz34qZP;G!D^{5 z>QELu`+mb@ktur=rasxThGqMS-XiV;sZ(!H60JBjRo&dfEK*VZz*qIPCTuU?w0X-- zP|WLm%~!Hi`DeqIZSyn}{=QYe%~KI}FR0P%;gst#CF^aQ8$`InUT1!o9`*Euqeq6w zlF}mEzHFYKAD{0k#sEM$outKnOAX#&>fC{ z4ZEe^_0|^f=Bx>O;>_`1@@r*=P|Ky=5!0E3f{vGW`tubQKbmHHk73eX(Xzt|zEiL8 zPrBQfkQXz#CF_Cc7pZFPhA^wi-xXH%`*gozTi9~p#f5%_zcXJgYbx2IHILsW-*>4^ zKoesM%X-P#PO*(5ViRixQl@M%6aO=9+X+6WCFZPu-w23odh|=yV3PDF-W%QTMD5sT z=QckPIT6{lv-FSso4etYuY_Lz@%Mey?&`D|l0`zCN|G)vO$|?aQUo0xCM;Q?BI-Cn zK~mL&O)y}R0@EUwlL`t7O)jd>=X_rB@9kHam~z|o4}M*bEw!G%YMtHNTk6&8<2ZM| zyyg)<;jzpS=N;4jJz3XSb9ntH;S(@hpUFIl&avMe`E%8*y3z3{AhnI%^Wr_SU*jkYC#Hlud2Jjpre-n3LV)uMT@;uReD$imK0L{Z?2IsmAnjNy{3hO&`})T{v50@J0Lv$IR!(4f|6h zgQqpr9~El4{h{F2;~UFkAHJJj-goajPkH~{*}oHHT#haO^3nO=(y6x>_8*MB`ZI8T z^S8_Hf$9$fHy*DQj&E&$DR1X9gSk9@NBOslVjLXnexxn1T(aDbF;iprSJ4l9bC&Mm z+Na$6l`TU5--fFXezPdo*BEv+>u3L7s~6$@?1sw(CbL7O zjC?P<_waq1=KFCy!{J)L*<9bcbT-~~kgay*a{8ejt#9l2>{H$c!HC-UOXiG4zwe)5 z{?=`qy}x1qsoHwcrnO-jH@NmS_I}}2IB{t9L>&RnB|$0;39YrFYZ@M8Zm_oCi`lSw z4wHy)t?vKtOQ$6)^_u$XzPfJUk_Oqtsc%^JAG`m=>W9>}7S#jy?b_H5)Oe>$Htt=v z&+NisyR9Y(zL}Z%&3oU3-#VSZ{Wmn4<4XdoZNu8IV_uArm5m=A_~kA#|8QUQlRc$% z%4E?6qIcY*7@w@!r;zT*emYB%N6J^URN>^L*dT}7uU3CzJK=P8_2&k$m}RU1-ciBa zTU4a)o>o}zDOSl_vMX%*qz87>gKn{0BiTQO>HlHK! z&9y9Z{=)unonfd#TFRGqI+hB>%_kmDIW8b8T&l8)nS;j6bzSZKh`{ zw|w52qsP6Y{Zr3vmQLGA>}qzc>Y4H}K0nwO^}RTrC$&U(PF_|{6-WDFOXu?pu1`Cs zv_9F~IX!5SLzVWza7EwE7d8j+>^PCXiEEPnvL}5tjG1vf&pd-YJbg69uQYz(zL^-J+ns3T<3rU7gGi5I(3t0IuYxAf? zMC&#AJl^(?XJYLn(^RPqS7sGIviE%#Ci`Z_l#lZpW!T zC6O)b;!}Pe+;??*j%h}r_0#C4J!|YEf+w7e@}1R?J2|_ArEbd8OHym5Kb=^maIz@= zSToN<|9cESXP&R*e-mrD+F9XG>%G2rynmMN{K@er*WNTps$=Rt5hk^lT9uM=GCSle z3uJe!>27+H!2ajP15TCy^D6Ww@fwMonqggL=<@$rS8IGr%akKwovR;wT0Z^z-CQ2C zp6j~I5t`TU7&(7CH`DqPgU~*|HFIvrUHaw`ul$ZdrRMjRL?@mbUfidpPV7>BUHnq! zN8_`#ayN|Zk}XcC#mDnC_{(qERI%ZDsw{5<|J$Vr;t`Ve*QT@CpMU<0ubKq zf`-6*VvVK`7|%1$t<~*o6x~oE&6u+_=$hD@#0`h$F~t80tzo=&*X%O0jGh_RvX9us{Ou5E5HPSP6!lIzauBImn%bzgZ)p%K=e1iF=4!^_mPkYa? zo?La`GwgwX?&uq$ZJg-beDs>qYkqT{7ujC)m}#CI0xjj^A=d zHnaC-@46`vu927jCwjuN;+k_mHVK&OopRT2|Izs9dJ{__m;!7+U-bIx3eOND`R5gR!u}0v& zs;}LN35w2bKh{n7dG$=RkyXdtDQda1P7B0IWr<8>de3VVl7;LY43z(dM$FT#V)0d0gEr(SuuOT&p(>A zd{1oVpR~GA8x_&lsF$!vt|0iwsWB?==gTmiVnIKlqu`V*%GPRibtw&Zu* zePYf6kqHlWv#=i6_4|I)`-a@Guz8HW-kOzM>*PWM3#8x5h#y!x>FyP#a*Zp;`<%WU zTd^sOd(pNQv)lFy^iP@Ims=sJ^Wl5L=85uusvUweS1~uFZHt@Tn6_zJ3IkWbOu+*Q z+8Wx8LNSxKEADaMwA)bO{;BI>aw(JUr-mN9x4CAU^ckI7yBbYC9DQT(!_D#k0u_~> z`d=11g;b_j-qO+JnYi2YysB7pd)4-5j~7me(SEpj$>)Ol$IWj1{thL*1^zEtF2=jf zeJs-Q{q^GlxgB#}R~>pAQ?UO1-{5|^!%ts{TgS??Pp^uxsd>z#lJ9X-K83~dneY;A zkHVd*Ot<~%smmf?CYmg`ac|-BGq;rd*GKL zmC1DR^U^8T+5&7RTtBdWYK<9dO}^gm#DwWu+}{zdES+CTptUM*Y(ZqBq> zJUXy0TJAQ>nQyLdIc^x69iGp=JHtGeFG8{Y!nek|Uqb)Heb}5kb3RwuDYxIj4{p!; zu}#2)%Zj!4z^9ugr`gR;m)+J`z)>G5IKg0}5L;%ur}cu)EsHokcsnObDG0An5N_aj z>B*#J)L*3Va!t#T=8%OJp$>JITt5k)sH}|FYo2m>-I9hg>3v(7&pIl;|=Gge8OrXi|73dbTJ+*6MGyR5l;3H)aQfQJQU^8ri0e%~)3dz+A+%#zhPHbboyVhoXv+4KxpHmf7n`QNwUBo;7DXUq3*r#)~@_?(Z z%r7RTt4(&ZSWfCMnX}pJLHn(OSq)!5e_v{DaIDHpc*Fa(+upG4Qwc03M&0yD$0vB47TMamJ|xzvg|5V`JDS2)~&!`GB8x z?p{aZ6G^@-`xYA2H%#4L6v8-dL+{fE;)O^2+4kS8e{JsURaUhurG3GSe`0%>4zk;Y ze=z*9{;B(FzJry2XCzMu@~it8-(W2%_4u7b#tvKkqX&L{>i1=3TD4JR#>0zT35TBu zxHg)6>gLSo;@Ng6;j7B#^^D)<*nUxOjQbZkn^Ej^WxR-mpYCjy8p)rHd=D<&^pWN< zaukm=opAQkRDHIb5`C}22~3wPzet_nlT`eBVBZ#TCDVgkhI*ekbmpmV6MVw{>9+9! z_bEMFm|o_@)|woN?~IrJEW6~l_T!yf?mGuR=i=Q%(GI6udG!Z}mk6lXfNZE~5%?s{)pP{Q+*nZ^g+-QxVuR&br`r1gXBh_Y(trr%q*-Z?K> zEz(uex?sj$F_Gp6srT(FB%9R!y%>O8QaNb588|H7j?rz{Zz!qU#H=i`|Fu%H`lgaaG)thNj z3im(FD`C2+(*M-?!sVb4qsDthbDuC@2(=1lYxtKpJ;nHh=qImog{H~X3fe!sHZ@;O zp8A*TUv5{V@C9bEL=}T8&rR7s?b~xa;ecm)TJzK`vIk=xOnCC)^}(&vrm-B5;-1KG z>B5}ZOlEOuhWsyyKnbPKfq#7Xk4`4YQ?*~8Cz zX?f%Ooo}WyolxD?eC>jAmG~d4_e*Rxgjap!>`<1Eb&%U_|ANmf;yCNxM)qA-`WpVe zN;|>(;c5(jH~ZQ5mwxUMh`p4`SI(-nQ{X()dk?;yYztkBf`Sx28rx0THDStmuL#Qt zcC(KEW7(Oaf1B$AcMX3WOZJ2GAl{s1f3!{?)H;!v%^%f$-hxAK?(G-<*y<*KE0W0B zlXLl%z?N^-sw~$uCiSKYXi5JH5popYVP_^|ams&^U{kQ8t0j-4uyjhQVui2SZ~lhB znCUVKi!+wZXPo?Y%@-zV(U|SC7u;O3G>kp+Ob&13U4HdX92M#-GGz~V?0cond?>Yz z$*^w2TLw#&YzdBSea$_rVIO6(8~NXuRxz)A;Vd|T|H#Y03uV7H<*}c-QRAd=-jn?# zw^8@UJsd$(m}A)bJPJ9RQzpO9VEM-W^{rflUGWq4{jH95#y@r&os4Jwlw>Bg=SG!0 z(`>EkwG0~~o_W+4e7=+Um#N7_^W{=)kKn(NSxq6Svg!`|oOW=OO!3iSXglfqS9pcu z>OW=^-u$cH*8KOa`e!DOP4ks34<3({f5~(0?DOyI8QN=PW7&62e)Wb`a`og~vw}$x zwW1~6H3C*VWpVC?Ts}W;%xbPz+51-a!Je<%&o|mjKA3#qYN79w*a_K1U3VX>YEcSf zzZBwoS@{Z|oKAI#Vxhtgr-ivyl1Am{geI?KP!X#Sdh>FA!S54y!{5g@?)bPdGzijDYy{S?A*Jp#`nlEn;_OZ@vx~`y+@?T|ngY4E-cIG#{;(H_> zX#HGb?$Pyy?<@PgXHx2p^M2_&CpSoK>NxWt{5JnL>l-}(wmfeL%Jba7a3hB`Rq93V z*)&PVTbt(2Oi=uNyN*Hnv}82nwUAfYxgR`|Px2ppF4CdHXq_xLmvi4_*{_Qa{D15H z)A@n*Pq}@v5rV5zS`X}EVP46!Z{k!%7MZQBDvjq~#V?U8kjmg#;_C4a(&^!K0Cjra zFSZcbkrQ7i{^NGs1|^3*S3j{fd=WdZyW!Km340l)on93w`oTG8@_w#w=3n3DFKE8I zh&e)Ju@2W7d;N&|4Qw^;%oB8jroK~9-?DfP1Mlks-sU&crk9v!Wba&fw!uBfFRMY` zGfIugw3Of0@%xDyJC=RRChXxf^5(iI^+YmOp<&J>p-qAk8vUJ&os)J2aX(qbGhxz# z!%w|4c`x;HZ(>`LE|JMODYSn3Z-?reIm!p7Z#6pDEg&oYPO?~DPj&wKaz%5Sw;xj; zU1n7{zGPVr<7vgGFF8!Ef9zq_e#!pPZbw|zC0)jq&T*BZ7U!5`ZtzDnIV)%iU7Y2x z=-GwT1)*D}tmpnV*Jl}XL7dmFO$+w^^%N{{Ueoc8?e@C$r@1D4j=K7cA$@6=Hp8V( zzFX3NGEXnbbx8UV+2;5{dHcP+yq7L=<~Ogs(0b5g!c8;BTTVrrm)tm!IN|2PYkBr_ ztgFo~?@U+xGufx?u;LtbEB`C9Cno7v|5{_K_%A4C>6TcQ`^LIDvhmco4zpLTvGmn{mBZkORof^gr&6pI$X}?{>yx0nWO~E zE&u7euOHm^A>PAs0)LIUB%jsk*p2QAAEVlh6ke;ei!uG%82E*4|I}qE48Q%1yrnrl zvFV?-+Q7DN=WnT!l^Pornj=qVOjtSVXexVcXxdM)gyKbfw~G(hn;lrzcz$xtjBO6V zl`8oR`X^?G$;On6bDFj+Xu8U))x0FJ!-job|DJ`v75_w9^nYc1yZomnf6x`P*BS{M z)|Q=bJd?cZOl!d14+=Z^r#vn;Y8Kt}XP<_FpVBwO88<`R95!fY2+1(T&#SR z)&oVCMY;7)LO<{>Suf8m^L75OdkNJ+VQ#0IWxob)<4akxPpS8y)TxwQX3u&xxn2>b zyGII7HQi5)tE2g>!rHRn8LDX=rV%*QKHmxmT z``Rb01yf^YzxX}jlR;%qZtD^&&uXFgmJ^2_1YVGA5)$E+F_^Mn$GRo&g^(tTXSi^E zZx3Vr{PbGp9Up$&$-e%rsdNI%eTkIjE#)^IyI=jzv%PV9npp;?*{Sni)fJ1hcLpxe zv8j4~@~XPxi96=e=GA;R6jGbo4y64&a6ku&J+Bf7$tU? ze0<)Neo%ahP@O>pr@XIDM7sC;a7K6C>bsvo$L59)tD zTH~LfxpxM`9(OsZzxnY||GzI`Q&<_a^w;-^|9wn1zRlVGHeqAR#w`hg_fN?+sIqVT zCU7IcJ4bhdM(HHSB%gb~}H8 zckIUiyMn!U&5O8dgnwTVYUI4-RmcA6x3uyAGmup-r4+M)T!)Ea&Kfc)tepO zyIg%>wxif)wY5X{?u_01M$?o(ar~HJx-k5}wx1vOaNlyMn|$fPvfb%-JT_FVlY8IT zd&$R#aqUt0e+DmRZ@Rvp?_*_h-E@YeYsdXOC$miFinFcQYdB|Tt=7{+l_xB;PfzuU zRs2?TXXy>^327-lmreFNtqAzia>4mTk@n})k^fpeaz5RABKbi6M2J1dl8Jk=<~w{? z)IYg*!Trp@?`AGnIZCIbDJ;EvJBaCr%eNN}1;tBROqjpj7UFEUwoY)PLTYTKG|wk7 zV@;NA;j>S;Za5k;RY#%PWVZ<0ccc2B(FJb zTd!>2YIb2}V30^kMn=O_=9g=~-eA(~ouQuXcsTIuQq3K?UrOtnQ?TH@ zbdb-Bo#T1&`TE0xCgEmK35(5 z)GWHKeGNK}0AHSc-a#=5_$m)DE!?m^pa4xb%-9tNCyp(LOuzs^VcgYcW#30x}oJpO6^ z6St~9<&r72vGbg7RyKpUUQSNn&<3IY^WDfgRiZ(>?h*2)*DIAM~_M8AY$&1d2cOOgu?-ea~Fs=PO?d3~1UTTz9@^EQ1_ z*qpN98iT$5jr64l=1;2nCceY-m)c~kDUVRUSL2y_{Xd_(Jr_HLnNnJk)I&W=&=i zV?G)C#N(3OhE@MIEoHa%;Pz(7i|7uhy=l|DIdoFQ9zQya`4h83)f988**S~}B%j30I zgx37ucZESEbXCZIJH?~|hx*sf^$FYipYd<_y>0641aIBlwS51qcV{*qn3ujml)?Uj z{s!jXsKcd6{+F6+^dz4YPdGYf`cZ}bFARRNrYv1E zajJsq3l&v|T^_6_`F6$RdwEZo9pxj|`0ZhBCexy#F6Hw`bIXY0Fz3BG!Q{cYm(oRS zIrDo;@|)HzY?{MZe?9yYb3??H{QJYyY!%bfF^iFKT z_R37&lihzam^Rd!9nW$o(0%GqRQh4PyWRKa4f9-9Hq^gb|IzA3{F;fe3;f^a8HgR) zdDz~=bHb-seP*&vsTH_GowU|F#p(+da4N@V|d_K5OLT^M&jx z`)?nqC@@Xg%U~2=?#VVm`pkp$34w1W9)Iw!Wcne#KT>DTvqqkt8jv<2Hsso22eVx_ zQa0E)YL?5?oJ-h!m&tR2`#$>{R{Q(@3CMituVDGM`|8Gn4tm^gq<$Rm-)8kd`=-Eu zmW(HxKd}B-UAIG@HRPe8IfLTXVR3j70HVwt|+~Ic#c$ z7nU{3Cq&lBT-B5;= z$HJv}jXKX5aX#^#t9sT^`{nW|@s!EyCipI370_iUnVeR_eDiqav1YzYHCC)o_AHs+ zw4nK>`o{(FyB>X#ei4|f?d)*w249fFxlC5U1*@h_5I-P0wQ!TjlUlhO$scat@D=`` zy;A3{!|R=*zeIO98(vNBb3Sp=H?OgOy3RhC4Uw-l3Oq4gowM=5<=ZLurJUTq%-Yuc zVZnuOFI+xU{>`zu5UO@6K4G`+@;~X#f1VdH{Bi8MEHy*@d;sT@&fmRh4uwB0BY4-D zDX-xCbi{6p+JQ8!AG!)3FDYa(?1<63_Lae2?}y8#yX}cAq zO;VOSuxHiQ3a;td=WpU-wOnS)qy^`HWI3vysI+)K<4(Twsg+aYC&@d_TKutek-qb% z4GSE*4m_UNzuP8@wST$oN>AO6+j8+n>vUY^_rH>l<6S71mm5-AVEUI=mvx_6^c(x& z8OvV86x6%_)roDmwdP6Xg56K2oOfLHS^Yc{bBg)q#RKF;sWQ&vx0Ix%g+jC=VWea$AHx8$FZ z<2c${m1)zlys?j^JU0A;mWQsaYUhMYSI+ZHYM8NLf+*wJeE$;e8ys1;3ntjkiu}gp zoFXjTaA3}nrfE~Sqqrm5E;6lfG=Jvk_1~lEzo3)*p~jQdJ6W zauc{Is;RNI(Rg8~k;E6(+W29_=Z4L+qxBF7r1h4OcTp$%;o| z&3oSD|5!Vrz$(+jNyTWslys5_oY)pqBeblvt76o#jEn zY)$K~+Hd8Es9U?x_Tb*9=Ueoc_j^mF2`@lK(ik9hTo(UBy)~Ra=H7{>G|X)d{XD)1&$8_UVh=;=0zp?x3-PbLNWmO#5#+ zo@R2itMBtaIO}J|eX&1B7Hpiu(s?nhO5{cQ``_h`t8QD~WZ8DP?1u}-F7};&$^~RT zwAOGe>DBk~{_uYDsq<{o@2qdzDO}4E=04E&|E>1x|BGPdNuY}`TlNW+YGIw_;is9u zY`y=Id)r0O7Lgva<3>$)mn^%>Qh&Pdcl3k!P3eEV6PVX-=4Gk+thl#v?P!$-5KY+>!r)ar?b3psOC~>K z-TQFY%g6~|V;(pc%)P0_d|)bv>4n`2??13AJJ>&!{3&S?$-YxvK{mBnhmq@bv>x}1 z<)5b8uoz9$Gm%=uCp439&T~Uihdjf!Pr|if^C;= zKNvr;Lzn%fO7RJ`>qT>$rfP+~q*q zmgL3fm#pLzniMyQIceFFodR{@kJyeJJ#O3ZB;eC_&HD!rf9=0!E5p*C&sNX=S+Oj2 zmS$$Ob4crowvA^G=5pt9@8zvo={u9_;Ig zzVeLJ*2Zaje3Uy%wl0imTp*a9YrR8L=xtuZ?=#DGo>!QmAKvt3!Q7JN`#AT#t~aat zZC7^cSi_lR4YzrHCYMiM&&>L!@d^u9RB0UBzh#rAFsv5(sda-_&-+JZCPR^T(JpO= z^4Cez6;}vu`LwvrHJRMaDD4wFoo&sY z-7ou{zr4RVb|!Wbs^ zB;rN#UEZ+i8_gtjn7g0KChYo?8LIf@#Ie)L6KsmkdogWX^0cz!*P*@}iV2Os55H|w zIJN&s42NE%=UN`S2a+rKQm#7wlM6U1KOrmOtnOlY?sfA%-x7Y3JiYVXfz){GEh10! zc4{zPx_OQ@Qz1lUN-#Uad9N}yW_DHa*^bw4G3K&Qnz-RK*OrM*R*W8>e*a0`aD3D4 z>nv$EV*MNquI*wAK5*=p+a6xW&k4KFGDjbBD{9icFsq6`rR6Q(S;pvR+g0n?_N8c97OQyxKahE*sFLOPvE)pP%*;Z%_}=${oL(K z6}WHe-gZ=;$fA@TMFg}#Fq;jh5KGtdHj8XiMhn&8csQNo1Dpi zgl*>i{IU6Wx8k$!HJ!ea)4b$%B^1Jd5@YiLZa=Y*^Wjf0T=cc?cfrzZnYmGguWED;; zOnUQP-M{Hb{Jd#?f0&ihC#i3ii0NH1|4u?jti*MzZ+sciFZI73?7QLH&AQFowvtgI z`MaucL~`EFR|)@j%+IyBalU9K?}F1SRyuF0e%uITv2x^r?+?7XJj zZJc(pEobIVJ@uf^qkJdxJ{|E|SA|n%!Gc?+&KKLqcP(oBD&{%)uWvWqnS8j8FW?&2 zCgC@wc4hAy%aZ%Qv3VYMRAXYVcwgUEUNT+jfJu;3@vA`1)Azqq9VCBSSBNY5OFWBb@_xSgcG?Gd_LsFX zH%j*3eAZz9%=-7=2im_%zFGb#zIL;(G5+cKAL|bI&ig%INXPRnnw<*Y#JWW0V+^}nV9e}>Gt+A}i7uJF$E$w9 zB0d%U6KR!494F{MwfgM1Z~EL#@-vp4a97BE$|u?Qx@S|U!o^!5-3@%jer=4)cWAgf zTncKkaVXO~mpS3+n{B%ea2+aPvOHs&<~RC3C6;viP2IX+?qTgx z$rC{*52sA9o)~p-`2y3Y!q=Lm1?f9^MvDuei&m=ESf^BuIcc4wd9Wo8}3 z^G7EsMg4}_Tt)k))6y{$79H3;sc+4L{u8PDC70~qpTO&IZI{S*?irJyO6;tw?w zcOm)&{)^?`YZrpRRzE4HNFNA}E3v&ootVe_x~D}$f^ksPvX5GeiBJsKNXUA=7x|>xx=6u=O$g@0F}I9F?j-D+ z>fdNnvu^hR{f%>;u*4ta-4^yhE&AmL36AyCcL$z!xP3xblWEtzwG;UjuDCMuHSE#t z&b8YR;daa9L;a*^+Y5cxfA}s<@G0VZvt+%Rf5Wp|4r0y*H!uB~`yjk1T&*!WYx`T( z4W-i_EIv^6S|OV?=!o2niVv-8r1YCqUG{xn-yol^$i?J-B2V;S|B{8F4GZEfL~=KO z+0ORQ{DEEhpK^uP87bQvx_HtwSYE#E_|G0=8u>!2G3P>sC6C?3e;*?o+s&>CIf%W? zDr5K=>0&rx<(B8=8w_PGUrd;vQEAThX2w4~?gsxiMH$u+^7l`dHH2>7GcoT##kyM( z6VfwIdrj#1rL^^c?zR1ErCW-On>7lI?`oaqnP}d$+OcYb`Xr^v2kSQRuhqR#^S@n} zJ^p&#=h6eSSoTLGZ#a1GX2ys0KUwSaSzL4H<+dzX@Lp7gt;KX3yT&Q)|C=WmT&)j; zwL~lybuek4FuLXxaMVwj@z*U+F$Vt=zE%oz_vY+9khS4{9^>1$d~X}~EK$`Dh>DwZ zX~FNM6WtnFZiO;m=&@Ps>KJ}PIF@nJf?id|C96dyUQ_tHWlbEzU&pLdSrb$zxmP#G zO#8A)zGT{$$+8RfpNKbMdaLPe$@OdIu~StSY~H0?a;=%D|F*iIIX2i__D<0DYr3o_ zHKbMKO8R7`1tv^iJFAE7osjrV<~l9CrPe1j{j%LQi05?mw4Dg6K2oxvZ|fwV1(l1B zomRYX`QhqG;#=hBmU0=KJ#}-l!pBL`=NkSW-nV7>0hh{HHirAZoz0|c`0ii)&ffib z=Slu;F?G|^6+12*Dq`OMLcC7t#dOEyN;aLvPm_39zM3}jEC_X4q}~|%ZigPLT9m6K z&#q~9b<8i!CwZnbm9JHwdY~wh&8+d-<&2rk-`4nUu(+TZHR0QXb(z_z@17a8{+)hV_3- zQ4x2FM6PqP!n&A0f41Z^_`jVN*D^)vr0gX726l0|wKWA2cdiCpyD(i~uHLtwE3_Zn zfj~@byctQ=FpAGbw&o^&sl35bnBv@aCq=m zo)%}Q%6@kte8K6%`t4GTXI1UJ57x_j=GQkLYyW-1!9(t8{bu(C(Mpn~N*<9j73&++ zvV0dQ#u#1+vttgAPxs>b!Fs9d%mZ^n;};AUm6PUmTd_{EyL0v``@SBVrAjvzELh0f z@*(Zl?q-3?e(s5n1uko~&3HLslg*@)n;e$pdaXTT^Wa>|u2W?kzT3;c@cf87So@PZ zrRxo!K8vk`-b&ovbrf{>mO%cEvP4@VPYoAhSo*ybC5P z%>$;rk7N&AyXaHj}3*PRCZ_td2C~NwEsVvLnhtis4`G6z4b|pE4&XxGd_hq8& zRau4posYDbxWBf^F`s?TbwXTW{fYWni?UA{#d2(lloMj=oTghO{wL_nJ*NGE+l{!M zSUj&`a0%>KbKtwrBe@$IaTOp1am8%|=N}gB zB>g77DY0d86$jsM-;{7M?AUU4w%=1Hag-gYd#HIsEb7Opgy(BpMVd}*N${+Y4lpP> zxQjVGbzwdC7DfA?1`(;N^FJ#VoqDyA_sv`nX*-7d2V*y!e$aMVZw2qA3o_>!te42Y zZk+RG@~(vXLVF9olBF%FO%Fm}>N9C>zH##TgHw@gGkHI4y=TZQ{m7o{9_!59&pB!@ z(oR3$)qIqFa!1>PsUEennQmRnon&=j_rh;22mUAZ*BEz9;jH?Zso?vyX+C$@^|%d- z63W-EyUS)Rb!W@sgynzcmvJ?f?W?%J)>QCepFQipZM_?~9$aT-|2tbj^-lO6ZVk~O zV}<{Ban8SK1oouHJdWP#YR2&K~77&qL-#Zl?BW#SpLhg3||eFIB&b zzHr9M$+-F4Q|nlkUmlmdODC){I{mIuH85gPgUp?R_su@(o98jEbQQGb{}eGTI=0~U zqDL!O*F}Gczs3G!-IeLg3BeZrVgl*OInqC3H#rwI>Zi_*EuP?AH&2X3_WgMevkQg) zQcv<1MHZb*Dmeezi_cNxLd3V`)oI@R%bsGmKNl)%iI%5%b{a>e6=q8I$uakOx~RLW&h zxxxCcWx>{?Y^n$HA9elbyW!})oB55>wf)=`+chk>*Q6-@Z%COGyo*Odm-}wUglp^E zehBU8vzmJ6f!@jW)8wD*3tgsuBJ1ZlH3y%foEV?N4*l8hR+n&D1q*LcwORRR$KT0u zAD;Yn-*3B?Q}g{28Q(m{X~9;vLMJqTYQ4sGR?FQ<^q}~M&o#y!F(1k&-*)z5eC4L+ zEHJTY3!CS|oC@9#lS+79n`2Y<__EIwI;DB|0GC&BwcMY(4BHa6YPx9>B5t=OaVcVC>#tx{4-&sK|JR~pjuz^{hxMJl1nw3N<#mdEqrROrC~5L~mG!O1yXUFd&1IX|Uo&$` zt-$B%J9loJo={faCY!15c&0dW*3mqsYi;{^d0GE+OUBy#sQo1A?3nc`#a{YLs-}Y6 zmvys6t$2QQO23O^7W&Go$rQ6KHHi6!dg4vKWmbW+8fLHkD8!n#-fVh|L;TIWuLry) zwM{?pZIa~;MxUqA+B{Rz_UbU~zay6=$1y8X?JUd0qejkl%v^;hRyF>47N5-eqU2bV z-vV95`+|S;R?60}Pkd)~_C5RltLX~LKlavj`LbGNM*kM$*vZ$^@}%sbtuD*S^2y$r z{2MMg*578o6z_I3WWvO->-CI#GUIRGfAIXb=QWNs8{YeIXlU(qj#iL3#8%B+Q+fZ~ zeYW2dUOqceR&Z3SQ8zI#fVo30XL3VcQP`#C77300jUiipKViClukLyJggm>W=ULxM ztIQ@@zKKq;I z1dI30SHI6)Yxg5{Vr`uyORmy;)?9^%C+}Ih6)xGW5G`qX(3UTs$+oXR51?ms{G|CxiAOrAVqosbx_!Sz5!{Q-Oap!4%?yiPc} zmLt(1|N7hX1lF|?jGxR6KbcSPpXF^gfu%O~Skt%5zPI?FxM!-{GhOsim71Zsb_S=( zWAT-oYo4WRF$R~e&t=nmvCP~00du*BSYyZ%_QMYAK5hHM=W^06leNg_m8Vs~@~4x2 zOQAI<$~CJ^EKB-rFG@e~J9YF!!3CLHXLXtOpWnuM zxZ&(N5q^a~J#(h+KRCCq&ii)r+)3PD*nc0}o~W52oc;cHGk;DmO9F51{MpQ(HrXq5 zH{N|`wcWAr;}Ln*?|XcDT^{tLYd z+)MiXSiFVbOxpP1{)0txnO|ql{;Tsq_N7z$hGQXHc@zF_>3rI}!1ak!faDwgzb%U% z#DCR~jC!!FW|=0ipqXVj+qz{~iWD2urA@kR5R6SnWmnBO$@)6bP0zp}kpXLAO*)r&UV zIkSY}MZlyj@)Mfx9AUbf+gZx^*KyWv`GePfPhOVLsVTcwcEb_TMT|G@&zkH!;fAVe zu9x)&-Xa(7=GAZ3@0#^MJK8y(>(BOYt)a}C;!;c29-PJc*OEy@`H<^`i@$=eoL8{^ zGxY(RN9ElEN)wFMan5#l^z&3+bL`~9VX{hXC6L;RXhOXeOi6ULJ5yY*8Y3I$`ijgBn8eCNUY;5&Uz zFI4}|Ui0AH%lLhQ6Q=I}lK()%cdhn~lKs6k5BhJ!?-E+Fb>75&hv?rmVNw}7%QG2F zrd~5+`C{-s!u!K_-IM){)%`lxB-iYyJsaz>(zok8!$0%4TEz!$1^UG`p6Hyi=o`nH zhW-=B6&Bx}ZYFR-aLJGD2X+A>4P*7Vbk9!ei}s}qmM08P@2D?`?0D$RQNEnh|6uCG;#~qd zen<2g?@oT>!}NN=(KSX9rFRoIH(I=Q5)4Rbb~_;Byhcw&yeH=4am7#J7doO6>VFIV zQ@g>UDf@?^uJ8P#`HZulao;fcu>0+f`@D|v`|V$FJ8n;?HxO9U@jlh^K=`IN&kpoO zdUvxviu8+8_pov2T#M zT=R+bMey7unhxcW4&@V=f}-a!{ytFeo4r9lWl>7|g;yU;I@>^) zGNMIia*J%b^iT0d>FT_t2X;joM{}CpPv2U8uzbSjFpekd%qD+#Go~S7FBQv%)qkTW~X$|EJ6sQ{FAA9`f@ZGf!y$W_H-o_}8;poL~HBFFES4 z?6zblb9H;04Ws;x`PW1;G(XQ|tD7CS2=peLA1`I=*TL z*D>yI<2z{QvD2^liBCfEVb%_ZJHHEUlm9mqG=7L)n6IeW!Q)x<;fQ^2Rnmk@mOCAn zFyD}lIxf}Fsk?DAH$Wgbi?N}^trJP=(M#HP}9{Rv-zg~m~_4WAEAFO&MSyRPd!<4Na9@&5m$ z=KcGyfedxC;uF3C6hKE_}+7-N5$boudGKW`|V|}fgghZF^H|X`;IugI&{^^L*jC)dVHb*6h zrnXOIKdF0LgXdDvYu0LqMK2Y^9Y5W0nJ&9#;Z=_PY-OHC?R*X^*2he^C*b#>Pqq2o z_I5L_AMUNMABYS5xSB9~NmIFl{m&!y{B`qv9~iLQXM0-f%W>;=YBh7y(QmBh9Y2(w z+H^vSqip#czF$o-8(8;goye$u<9wiW8|%#mqch)f4@!Ael{Mv`Pqk`TyL6p4!!Pl} zUk!I?s_tO+qPfm4jXKXdav4_p zSy!{JF?(E7x#4h_;R^PDlTREw&@6v2mgmom3!Jn0%Ct(ZoocFGlqk*mZMSQV?1$Ev z=lLJL|4sNU-SoKe^Y#PgoLaxs9(c3o|F|R&@}abeOF&X8q?4yrL8wN=<$(CtqqTt_ zuD`9VXa3z>zT{^DU$zFL!CfMkZQaZ*siG&0P9CUUQ2*NPTJyKdJB1qR zmaa_^KatUPn`1(1$)$3~^4-_Ba!u$y^Mv72M7Sq&&UaBw-k@W=6U+~UZokTv5n=Zr z`vMnBk!VTSijWnC9`i3)hRd(nq_0?c;1m1Kzv&O+3Ywd354`@Pa=$rs;SxKRv#;AW z^4A1wYfM#?czv7gr=p2%gvd_rCy&imAIK_fy?j90h>u<9#AY$>cM5YexXUhvurpgP z-IK<|^^2#6ZMC=dX%4;FXD*~W>?_!4!)P}#ErsExpVd$9oQ?9ipB--n&ER*mOOUx$}W}wxB(mPMrDD z9OhrQ=bvHgI$^zqIomDpw@8j?Z%w(!yi=PVus-18|0(@t(You}0#W))onI?{%DrF| z#HO@ZSZ~>zrVEC9AFwp}f8F~)>_>#wuP%XID$!2n4wo+bh&5VkwQo_+XsNIlU*hCt zTQ4MW;BbM=iSBP*b&N$$LQjep$d+pGe0iW}dtiTV{_pLLw9VD z8yl9LYZk9((EP5ne`!qniM>G(7z1*^x3vYoWMTj~k>9&UnI^z^c@* z9IK#_&g$InU=-`-rMT}4KGI9zS4;fbm zmN}ea`+ev>llQZgp^UFsub<=yIv#eq_&{cC(s2hVkE(jIpZ>>m`qh1Neywj!>p8b<(cnHH z_G-Qiljb7*NY4-cF~(=PU2>MbW&3^M{DoZ)PHplPcX$!LH*c>)s9Torf)%z?oFCYK z&%D9+YuU8PDjzIwB>5^>Xf8Ej-ch-&>*s>~MtA?Qocpa9COl)|%{B5YC$C+yXX^W~ zNuSAVddU>V?01)TO@2^3^9S#OyD_fU`EpV;7U!}$|B7I^6dpHm;el%jGN%vj6aTj=` zn3Se$cRBFk|NnO@LjIesl$;Q}o7g7r*OhRTOB|bY+S}R@1kO+PB!hSUS#U-ZXjM8-_c~3s`PzGESLvdkWLNiLP7N zCH0g`OkOzu^kLd?I_geVbBd~ZsKa?L>$7|@2~4*g*oBpUJN(XGcH#O1yTY?ym0m<- zy~-%qDfzCb*-UVImNTIZOtU##Pow3GQE*~NWLA-wtpfeGcg zePt|36#{YR7daq`aD{ov*(VPRPG7Bl^K}mZg6UH*8b>YXK)LOWozo2d{B?K;D*{K^Aov$efS!4E<|18Q<46fD(q2z zQ}6Ds=60s)X_LO`{NOmdhyC6J9uua>=W&x4C3x;SYtO!r z=~sVlLx9J$VA+yIQ@mC(9Xz_=+~vds)27hQ2LV?-( zX3k&qIwB=YEo9g)u6MQV_{EWAw5(FEO8kO?i0B^9FNNJT;T)!Yx8$a>E;4fy@9}EM zxcK*?r+}62{AHQE>)O4pSQYSY@v~r!QT%T4OYnuVrDz{>n78$+rr%K^)=oc^zqd+r zW-e=wO6{KqvI_fEAMD?tp2B+nhTliq6SH*^^dl1F-Qt*b=dw24 zcl>taWI^?ZXkEvCrpmP%k_Y!b*d@SUHHpbg;LU=o{*@e)rqBOVwxR6Jw#N_de}4Z< zXF|}~I?i9tDr;r8@E`cT{hdsEg85}j&NrLmRoM;nB%FjFIA=SnEl6LoBbJpj@F;h| z<+OepLt3J_>d#PHHPPldY-!dDf^Qc3bp6t$@}nGqxH2%GS)9hb^Ce zb~t5RlXzJn==>`WlNnczOcNzKwM$U z{l5}bEDO&&pL{2;ApcXtBa7|H|A|+4JXVFQ{8zi^-{*~AQ?`9QaDGqv?}K_bet%QH za8)WQe1V+k5C64)t@xV0OxC-^zhvRGOKl3yPge3U{+x2?m*xrCol4~n`=*#b^`6lG zWm64{Rotf66%(>#re!HOYxuh==**8c+VT9Q`c22~zv&a@9`DFK-*_y$el5?P>1mr} zzf|*H=G?Mo-Ic2f_MZ8XoNK1^NAflKOf$%+zIXYXg61A~?*&u-C>t)2{*pbNHKs?7 zqnt%P`ErczhL=Ge>zHT%n3kNd*WwV{1Lpr5*D>A9wN;hN(POMU`2W>?1?C&BZ?l3M zkJW8^!)9+2axs=|t*g5|(-Z&O593+1Q;j8gOD4Tv!<(TgyFl`wX}J^Q2IHQR`%JD& z_xrN$&fi`s|7OkB4fh+>cQt4(SpTAGI{y;RL`@E(wr(EwpCNjld=nf$bv|Lex6Lm~ z@J;f%8^bq<;#QEU9y} zoxs1QS%lT{VZQq{rw^LFf2=mBIjYX){?@KKS?*1!rfd3!WYXQPO^* zaKp$}^YQ_)9m}psY2CN>!L&S{xEy&^`A#qetYi&r%F=H{c>H=sQ=71-Zd+0 z&v7SoPW-8+D`Cy~Ft3wrKuez1uc@72uiPGlglT;@OV&4=Sfl8U&CY$ z?LT2r#K;cXYCXA z=)*j14gc>P`(-B(W1e5FkjlUNZ@R+ewR4`bs=0Z+l2(wsE2h>|y<-1~+6!k625Pe{ zS!gfWFiUf7g0eb`@oqkM9OJ4Jg)bSd-LHzY-!M64>*NK? zD?HsA{Vt{cVho797AWbIzx&a`Z4bWfC|#&?;Eu) zOV;a5s98|&!G4qRSKGU5P6wjC<}lyXytqj?MST4Uy@W|VDO?^s(_OZm3r!wtd8lRG@K%^=>UFKbl@x&wE3jIW8y>mFN!VE?fj=rJ4`&+t-gK0EkcR6A=YlcJ?D~#tM;>$ zsLf7eEfcuGvcSZ%hdo-z>(l$jy=lEq@)Mfxm6#rI`@#6l_JrzMg<8i$Tc*!H$aa(c z8s8SF=$9OqF23b%ZTMt#=^snf@0E6vIgR_2Y#Y|*e0{5+k=Xr4tmWR-uT@NUo>xuJ zJaGPB`me^4D_on{zPR67#XLJZ{)Ni}ZTmaF82WdlCOg_)z9+&`KQn8Zpwjcda@MS3 zqA8bNCd}^&V(T~&Fkh}jZtD*Q73tg({S#TMWh0q4&9|NCE^udS=Qf5t{O=;wHu%|X zH)W`fJAPYq!`yEt*ckV&*>B3W|A0|}Y|5JZy3!8YT=9=Jh>5g9W zo(JZqUN4y6qSf+ma;9dMwS)eNh-1v}FU&H0@cpKyz4)Eun{MVcyuBv=R3)He?KaUr z+k-;ZHDCR_`>E86(;<_T6V890p{pSJBlvuynwRKlmVG@>IJ+8H*NKH5tWV<9VbXrR zZf91)%-U-wUO(7=TVN*lwg4qFrZ36++uRrIf4P1N!%sJ>3EUdzidHkJY`ERloU?IZ z7~_)dVN+BS&fnI!&2a7T{I6>rFTLhC%koS6tchI7Y>_Ro4r#kHz8{FXnHk6a?Y2ub z(>|f;Z%Ze%ZHxQIkoU{wHB+8Q`xc=Srj=ft8^pHhWbj(}(R zoDcrUWwRe-sy%MjU=}uczd}gtzH9vl<`vwPXDkYs*Yx1LQB%eP_1K5|nKT#8zfn11 zPS7!Sg{i(3Y;QK?FM6%;lTG!h*^YaA^gb?dR!UsuY;gRNm;HpQJ^9xiygY2{*qN*| zZXS5|>&`y;AFXQ?xo_q=n=CdlMj>;D>TJh-lbAj+M!2qiY~Pl!t^R=2g}!NT z<~Q9-TF}EjU-8oobB5FXH&-X5N7$N)+GC7_ZsyLzI79y zEl9k*?xo|lA7(~^ZzMARCQs8HcU9zPI`W&Tmb6?3KR%`11ds|FYqOT!K}kCR3eV z^U=KCFIYZZGh}U02!rK=}fP52_#Le%ZXhTm`-TMhrDb0 zeI$0G|H1N0esvZ%RHxmvop65b|Nki(@}8a-7`Gie&zS!2?+M8X&vQCVm=`r2vskd* z=5*?YRGlql3ASG+*0U>xs^+|JeqwrcyW%P%@w>+z!WQKUduS(_nBUM8+G6OE^g_at zwaCFNi}6Bq-Lzb$|3){8AM8GDvX=eT(xeRa7h?Ms+60{I-)6C)rl?K%0CSR!gIo7v zt_|_buNk9`yL~jD;5+Yxzy^1*Y2O<(>(u5td^&Bsjpao4;uUOLCj4E)cJ|rqXx|NH zCR^JN{<1$W>{uWD)zoB}@?pCNbJ(5GZ?%qXuM|!)zVnrRcaP=z z3DLq{hoX;L-!O%H{4cxg`0I7rOa4u#rV28zeVz1_ZG&;p-0X(sf^Kcq1_IZ zK7$*p%kIaqRv+}b#eG0<-Yvldo1K#UpOnvkQa({(srcA2`HN~V<4e`zC(0APg-lIy z&^NSXGxPbrfv03e^rFcQr%sw_vKRS?o#sq1y}Cx#gLh|JL*Eo`9j23~#Vnb3%?;co z^=00>>st>*Rvz%1V6|?t8AGM>YH#NWYLlF=H||+gH_4lmD6A+P33TpFqhO(h+Zni@PfFL`u$aA>G5 z;PCREzxn&tXYUVw|0ld}e_?-NbzE}H{{wYK-_zH8ow?ewKlI?UpJhfYhn7B!4-$W3 z{w|2`iF5gdea!FJXRl$qVIRcKdakHB{y^9XE^p>J%jTr9M@!WyY;RECbj%>TMQ>3_rA550?K9-On3Grei<<9)KtA7VYT?<%f2$rIHm zpCh?e;>Xn*XH|~bzj^-fUflZ7cpGO}XEg{|H_%S1}Hp5U-rv;rM3J{@3Y^Pd{4L zY6VQ}f5M)y+jn|xLirY_4@w@|*=yY!*mAc0OVB^yLIKn@z^77xK2u5B*LH|Cah=yiMCoB1Px+UzdbQ>rb{n;CH*-_W9wx{CMIjxO-mnPg(xSX=! z4fD#}m8o1Gj9X8#*=#-kG;%{!Sp56OwNqYAV$Io+lBf`H?8u@639A#W75+@C&}!V% z@XBVw+ZA<8yX1Co9Mnx?$#$sUI_DF^o7CODoE*zM%py4_{+$)Y?)aonq^E{&;*@Kq z9n9suMUJusoS*7G7-wYfV%_P`9(eLDo7RsrpM*TPen!YM9FBN+k1aZT?^692Hh)9X z8}?|~T5xQ0NNZtjU=B&O44CPu!@PIBYOd)C{gTZZKVtsPInNVzC7!3AAyOw)NB>35 z&2u^%X8mK5R-7X~?Te`58Hw2sHaot#8llLo5TzA!EYV|+xU}G0j)#G-)c0|3y2ba4 zLBh$8tzp{5xlwF)yw<)BYr6gF*{-z<_Mg=J%X=sB?IXVCxKi&gS}z=~$FVo%mF+C$ zej$8vslNkP!E~ku$4i_i>fKYJce;(a3Z^{MUz<}VY9 z7g(0I%wzUl&7aB=5IM=)`hmEbN7aNkv)Vizj4h8dOxPteK~mxNmL)6--&N<{VK^5! z+g;(T>zC*Y{%hweW-ddQ#*oX3!OmCL#{=%^PLA)a4l8H}0X*+KE-93ltolm9bGoJw5D zz0H|DkR`*{_B7A6_(`9Ier(*-pOau;aONI!*T-It7s0>19yiR7Jaqqn!y`$Ce>{P^ z`GpRqsCjt*ymP8)kJ8UtKMtd;nkW7V3)U>ty0O|MK_o)yZY1xgrn}dj66`kyvaDJ4 z?pof0P)o_1yj7DJC%P*fSJ|z?FnPuSwhz&7X5_NmKBr!1J>mGf$?R)p$ZmSBpd%^A z#od&;snd>Odi3#Z=MLkOGSk_Gf`9&4eq6zEk@zBO#dS_|`1=@QpDN$v`KJ6hRVSiu z`-Q!n-!|4;i#6zrZTsHZC^yNujv-#v%shwpoBxI@*BO61aW&A z)sAS%>jsl}VwC@{O=H;gnsYzT3k=zzPf%U%|4i=dh2~|wxe#8EGqt8Wcf$A61?av#3&$^`7Sf}}W3GNo8^<+7K1nx@%>P0d%P)~8rRxLupbSK%Av$ziv+ zuAynm!%o@u6Sb?~%wjb4)Qfk1BdGZ%n`3=plvLB3gEj6O7EM{M@+0$3-`s|RR-Ocl zfV*2iem-!X_xY*93#W_X!kCj|3oV&)rl{@{deQ!mr}e;e-uYAg9j|%#{S^9BDw|gC z{KM#IsZ9&_p~xO4&$rsr3+yVw-I&*VM`|;F^R75~jH$JRf3Bn3rE8y>R_i!FRhV%9 zktXlIWcv<@f{B*5nBRogdv!O7P5WBJuw=IAhPVgHVVBOcR6BazHv6FK@uj;lf2q;V z1OMONZnr*I^ji2=!`>t7KImNVJlFozG3u4~CdQK8yI*{B`fwnZ?^@Gp73(cRCm7Fa zvO8SfU2baY5vk`9IUz@HcM9{_wVGdjFDOPinlrA{@X|VC}Qx!CS%o509FOOipbTs#3<^ppGXSaf#U;3`F&sFeUYT!}G6vLYjlBi;VsT1C$k^QDj08H z!|>x>-Hh{GK7D&r%MaeWtZpPS<89of;|cD$t9jV6mmc+Cn3-I^>&yWGYvtt)YeOb~ zQ}E86P|NPJcIOlJ=v7lQc`si5S6tOl-xC=;VRwq6iz_N~knVoMsO)8sT#wbw8OG{k-ryKwGp z*J#d6yY##N)orZQx%bFWmSgtjl7<@o)T@4*t74PIEe{tzBH{P`9nFNb<>= z*=p$r&u`ycr+6b!?pLxy&;U)->KrtnV>eg)}iOkK8_dL|M^QYSlyex!BOE6*9H-Z z^=~6Nj1IpG5ZSRte9_c{lcM#t8g0_`3uRJP*sY#?@cZQJGkF~|Uphoiu;ZCfnb2>U zs`x`s=F8~=&-wjbem6}0JYSuM{nE!h)$BQ2_2e4`nC7xu~Nyv*5Gsf^(VFo8k;3KOcJNwXZqB8ZtFpXwQq%vD^#eYSI8&b zJds^~(ERwx(htf%yM+@bzxCW9mcxHLRq#fLwD>$$%bfE&JQsvV3aK#9UCH`I@dV>b z=kyEh+x$d+oSy!1cjNRY+P`^nc8h-YKTvjS`#v25XFl)x3F>Pe=x%uOu)j=t&4Rs- z)(YM459DkR-^vx;c>izGCx(wr>$$h}m~u(1+1&Ub`&ZmH`8BKOZauCbE_vK;gY~j$ zQis&TwEsw|wA(JzJCX8Vr}E&jTdu45zAe`G=6RFQ;el@9TWCE#yGV>;sY$ zioa~We&Cj2@3e%+D^JTU=(yX>q_&Sy@xzVo)4YFFA|LN-O<1|+@?`;@$@`nCYtEleXO zVLroe(}M1U=P&#dW0cN*eUc;SMAR?u1#eU2cRQ}TDgMy?!19Yl7n2`|XK`9GJ{0;N z{Dqezc$eP6kO#X$KNc#mFWt<;xI5wUAHEBUlNQ(>(7jOctXU`gc^%7J*VszAl)ioI zUN?$P>xz7k_T%wBfru-we$PJ`zhDUu`}X5cd44t2s^zAd{jfh|pQLEecF#<7h1${; ztU)oGJrWC2Rc7B~{q|#15yz9xHIvj6+<#o`W01{dTi^Is^V~m{*%^_!OcAE@B}!QT zFRaP9e4y>?g7JA*NyKPSYL9-wHKe*mB zJblcZ(C_o<_Q7*M-tN=9(AHGL{c!#t2d)kM5~=l#_1@x9`5#ul-Dkoebt`5q@1dGCzo7Fy+c!3+;1Az*+T+>ZiJo0J zzv=6zN3)yWXD+}=mp1n@ z?_6nbB)+6A5xf-PoeJ}ktiQjmA z?xXdE@NUog3#>*Cvl_NdYyQOcqWT->?SpbRa~C<3U(0e|ASC_OuHc^3>+=W7%(CPa zmX}QZq;UF0wr<1TUpuF=tb7@lsGZ@xQuVB3+K)wHEIQloKi6+e-SYUK*oE1edl-o|#jDMt?^?!R8od8hknw^~zg|JDw_ z)+6_8uWhhzF-JNH1YWBUEM%&Yx_`x@UTJk8+wC%Mam?Zell95)Y4`srmQ zVKVh==N>knZLUQuPil2F?jBs3^(2_1?qOMgV2UPdirER@lUn|b-Z2l8FQji-@}H?m zylH(d?+&|zrMvmot(~22?BLbedcJu{Ps-L=3;rJoU#1@+eb-HT!Wl=mIOgvsa#KYz zcd+ubxf}M89c^5>g1RF4~ z*|#K>=LXX^FQp@PQ@z(TWjOzCTP^VWQhn}A#alOC-LU8o5u55-T)?b+9C5ZQPrA$9G{sjQLbId3v-o0VlIq;t>hreA{a%S_Hq zy{ol`npO_ezTg{e#qd+=*ra`Q&Ji#9yp&=UU|^%7SmehPiyZhKXus0Den5bIi%aJ&zHSi;oAv) zhRSx{AkjA)zC98=(si=COf*MAUHRrg-AR)_zHdy~BJJp3@cQfF6G{r}c?;h*D%(l< zu*^-^pTV|4sMC0jalzGn+ul8RKf{H+A$CjQ#fb{xsS?Ewk6caFKdjrWH`!sHN4h8X zE$6V;#}C-~E|EBqHgEf(1?Dled-*=CvR2A<6x+#Ps+e)*X)yaN=jd$43qKxgh+~+3 zX6?m84?gYs_De8hkI%Gu3ja@ht}~m^^J)%DkHO3PpX%c;`Oi}EoK!Eb$j3PE1FL_7 zaLi+s3!$ejitbR1eaiMCcGj>0WK!mtU4HL)${^IM+4RcpFnJF@L#GL^zV%#T zu+HCllPBzeIQMJD?!Q+1S#P|%>d)}&)4a6|Q8&_R1yiP`Tw_ys`E;&Fp+a-5QmMj; z=TeuXoA~tB>oT1@%w*3rBlUdS_J;j`o$T2xG@bv1J23h_z0YcYE${a?hsc#amK)yv zE56lq_RHfRJU?z^y|}Kx|F<@m|H-w=BH4mXcheXze)w8od%|kxEAJ-Jjd5n;Thw;% zVPExmdM`uuWxd~*9@I}{4P!kN`E9#Bhsaj(b(yCLH{k^ zGnmQ_`)^;D;Ql53KJSmII{V~L+|TPWWvUK8ck;f&qTdt457hPjNoIN&_&4r(Lv7w| zQ|=p?_OWqo1-)m|xR2DSo%3sq-L!fQ+f7|d6+Vu&4h0e^>(|TgmsV-E?Ks0$f9e0L zMGNX*eNEs!;key%T{GX*Gn0f$=I~8$QV4LE)W|b!pHJ%pmx6QanZgZMq!c&W1WlQo z@cE6dE$_amyccB`Jbtq+ntNA=rJZ0*xcTu_j(1k2uuF2EeC}?xi^D>^M!#PnLT0`j zzf;ZM6Yo46J?2?fF8;B6fpYW2D%P6#|I>V!YgN{6l{L5{7oERB?uwf54);rb)y;WR z-#6(u+H7-eV^TfzcayZvcG2n90?#(-l*uG9pAh}V6yI8LpLtE*4x2Y@PdfJ{&2Iep z?TR@|+bPr643pa4Z4!!!jXa;n;da7$7ULWC)!Hi$%>VLAyy5NkJO9*f6xmH;ShJ{P zqRRvQmx^pxHfjE1nXyb)#r}k^r&>Kz=2yc^htE;YXSqMIKYq%eut_J)=t8^B7slBN zfwLL@wS|5)Kk)xx?SJ+FW_Q*P;koNHH%w^V$MwQ+uXj~~2=__0eJ1K_IdACKndBZ+ zb6b7dXTzE2jX?6tQ1%>`P#eEg7p#09&o$}a$etfW=gV|WX^os?BoNpJWAsw zYWBSK)nNH{B;7lQ^UkKHlf=L5+p$AYK&D4qCX?0i>JKwV$&QSes~rt}0aqs~Xj*FS zZ`k+ZoM+U9ro5eLtg#Pwz2LgAeBA=A9r32kVyt(ce#*8kXgv1Cilwpo`c?L`3$s%V zUoe;7c+ZwA`6SsOphqV~WhL{c|DHh%o|9Bg?)UFuNji{XWh|MpD`nD4hs<2J%nPq} zd97-mH^r$+NJW`hMY+QxEO5djDgBoRw_Xwq;!s&SWm1a5%O_na%(G6qZfdSk*uKX8 z1zY8GO9kB}jISNEb~nD0d!ci4Y2kvqk?J!Yml?UAY7V>N9K>W|-><@O(nYwIuOiOq zwzR^@8_!lZyt%qohkepdbB+W57P75ucr7-8qp0gnk&ucmvnsQP+DiplhhHc4=P*ht zo37>kuqo#mp~$m~NY* z>9G5SXI1l`B`x6dUv3GLtmLE^^b>=QXZUD(TZQ0b-I9On7+uRdY?FJ->QC_?n# z7xBg^X2-;u!<1W}v1CPC%kqcaxyK>Spm|t5F|Z(c$==(Xj>Wl)uO9GCU!TSt{9lnT z!YOLMY=oZcTmKEew@g2#Q2Uibzwx!qoN2QKzHeW0agD%Chp>}ed23WQNUd3YXM@** zqf1tcvGe|LV4kpO%A`dJ{<*!jtk#O=Z&`k1hkU+%KZpeW@7(|@V%Z+x1f#D1`xW4DBWUr*XM{bpT0{twI&Asf~n$kcwq&tU#E_`lqZ z`+pv9Z@S9eKatf!`ECx=58Jv~^Vm-d%vJ4fsJ-a?JDRn0snslk->bhN#oc-!2H=l04xq!^+UFntSlq zd+%4!@y(C+^S8Jz<@I3lTjapqcai1B2`RItn2A0`tSRmLl&l+SF3qS>=>1}!T{zj3 z^Cr`!?);_VC-kpO`>e1$g;~DA??U+(;{#6`a~M~h(v{^f^58$6uz>UEQq3D#FHhv;3&eY-l%^<=(mVTn)nwR56LV~ zj!%MfJ$Y}r)n5@_FgGRIfMJJje+kQpfW7jkxqG(Ar;1)UdFi&nhWke|_^ra;-^%6a z`!LIo^%AS;625}A)6=9a)b7sYx@DVW#i0|MQ}agU$Leb*ikf`CZvB(6VQtvWeXQB9 zsz2LqaLGDVl`!wkv(ODW4^Nj#KG8p$`MBY#&0)I*JL+TA8cL_nF%zjs$v5U(vdrvx z=?DKc(`{Jh+x$6lkL9#x^y06LUvHf}#+>|iUoX>3`6t)Y1g3nje^v59yTtLBgm$L1(8z2^VTFMnD7c>6Cqlhbbd(j*~?X$=i~ z9=KiPD%ttvq4$QUGIbl?O+C#M{S-=^`Z+@#}WLT-J?ei*8PBy1~uUC(FFQ*T_ux4@zUVgFLkzKR!qr;x12^<2G4d*Zz&rF!!xT@%WRnxTs58es&KcfFJ zoXwElD*vMN?W){^;>$Yg*%tZN?)|{|;{2Q|XBT`73omNAyCJfSdCi)TO`8(hD=zXi zp50iU%h!_T?##Se^?h{i1kFS(N$zQr4jOTktXsW$bwl^9qg{+!*V@ZFR#@9S?ryY~ zmD?`tV17SZyV-Y}w^+meOx8o26*g-m+cPd-!?yUKTT5(?`j0PLo-u8(k6EJMv^Vwn z66S&{zFg0nq@NrA*rM<_XI2ga+va=c8qZ9$udUpmK4qi*!Ec8BhXwey3huMI5q59V zHHDM8{W2`EuVzOIcHG%^U6nDXSLWMJ#cc;p{Z4)G-80zC@x~eVzdReJA3A(dznN+vTTqo(+J|&;qJAPWmmxc3x6N57d6Q`aOb^bU!Yxix|AvU#s5bl8$74K zwkueh5@5g?Q#V)jt3z70(MsN^xa!xM9Lsi9&654nwr|aD2mTE0poYHdyg7_rCmhO} zdak9pC`hdR_>Q6FHk7|Y99ChA+kX3;qPjYKLs~Z2TX?>D@Vc9HK zu7)Silvg#VO;--$E}2p1X_;WyzLGP@^Zqk73w3=@^@7t|*bOJ7O$zsF=y}3ElcVZL z={04A-lc1&FyA~9Q^in!iba;ehi{fg`h(R8GF+SdH+u(9DCGOg@ucy}RCb5@>fUD# zS@s|8-}^`C&T8J0ro`>|s@VTiqG_@*OrO|w^?-N92>zx)2z><$q=whJsxwg)|`xnq`kB&>9l+j)Pj z{*Qji_tlMGAI-^SSQRPd$#_l0EUt*b`q1SX4o5S)@BEFk!VEVGwM?)5yWG2hWz$b{ z2klSop{+mSAJttH5ty#`Z`PI89sSB*mkPXilQ6T4#q#Kh2cZhv_Y2Koip+oS8q~DM z)xTD_qP6M@^Q_NSOxI>>U6W1Pp5*-2nnV4EYn9|5F1g!P3o7qOSo4YSOa0pDkUj60 zM8Ji*&w&QK$D`slJYU0^-#karI9Jh3^-lKL2br?7rLVQBw;z&EvQ|h8jnGpQi2M9G z*Y-!xM%9gr1fFf2mT0yiE>Z8l)sNQ?XSCc-2>o;XVeSUGjeGZ5w&)&u;y=Ij$l8bd z)K52@-RQlsI${2?eLZg*j+kA%dQre7S6lG~v*&5;1z$7Ht8Pfyr@FlH<)@}|3@r#cKHA_lviRMgOWd5GPi+M7q- zp~Th1JxBeA_rD46dG>Ai`gOK~jCi!5FRxwn74S~&M~fBv-XH%fQp9HPuVR~ zu3a&A{C82VNKE3$`x3_ML7TIbC*(v0-DdcEHBT;4^eYnx{a#){GaI9GzN9qjgPn{F_?x z@+?0r@0n(?VcR$HwGM7SeW$RjTt3s(ctdzylJO14e~$!jB=r@GODs9Pk}KrG*Kg7v z`p+NW-N0qmCGwztgVr~h6N-{5rybg&+2k5dzux1+e%-<&?W)7JUFmPKH!Rw>`8^ZQ zCc7FA9eAL$WhdpP{1TP7#PA37Z4|5|+?JHL_5o#`L6ZFbBz(63^VldZg^SH>n6QnAPQ zq1B(@pBFpb=RSP*&3*T~uMNAk<>YtH7k+0_SMurL{Q~p9kH7MBAJ{9{Ut}xC{{K#; z!Jm)SAI^7H|CP66FX#SW{O;lAg70^lZ#28?sD5%#{`2HJ9FdzAJ%;<=bV}yN}eGWriOQk)-ldk%3zlLY2E4BGl${*kLoDezf z?snGt^XIvW8}}xOuC@R1zNXlYx!&>H4b2Vin=YMa^!m(N-FP)dUDt#C-A}OtdeKs4 z4AN`wx9oLz6``EVyUSn0y|AMk&^L7wBAntg=& z2KmA=mN!O$)~x$lQa9=-R9A|`Forj(?P9tk(6xcDCSO}Lt|j8^hutrtJmyTiVe!lG z$Tih{BDPF*v5)TE2%8Z8CqJj@fwsBD4bHsuC+l`|I66IPyw` zy8ABM{~P*T+Zms)QM~SOZ9~jD=G4Dt_97BW$8#+{xZh}RJRloCVV%IbiCJYLIup`U zwPtMB3=;j5A2d@^AUf&zp8}58!ADLt#cumQ~6;x|AJXjBfb3^rjQPPE_6J-?G^6Y4(TeH4NSd zmPLogbCxv3eLCt;zN2y*=MA|YCJwgOF*iBiZPM0T?-+19^rEFguhP_~j4YcUi?NqF z+`D{0l%=};T@0hSgWTy1j&7A_UOFr`4>_zAgUoL}_TXuX_;j!E>Oo)sjoOpsAH7o3 z7tUm!xJ9%4cyC+E(O}P4q7#xlRxMg7?X+sw%KLt$@s(G9hTpSuHlTDpc5qW!YBVE$EQ|*XWI?yYpnMR9yijqR+3jzb z-C&wfV&ry@?Y_B;lv(qU$DQ-lm$YT*trphh-sttv+>UMEl=aIq8}IRS>agy7(Cr?_ z{Q7x1d%xrUX@?8fH~u@axPx8$LA3|-gz`#-a)-~iSTlJ)tz`>hT(fSrckY7)_s=UW zXbOF-JVE+kdzJVbuMaEZ*d8_cT;g6Lvbs_@Bj5K;@dM|#*^v(4v-s|_-ihG)vHxKI z#(n#kRO(y>3KZ7&szs0#P@YCJP>#yYn7*St2f>9BrYqv&D3^VseK@->I@ z0;WpuVsEmYb%vMy?lQk?4K+f05?4CPzX*wBmAaa^i*rlOho%2G9m~S+=r$=df8)62 zc)^|NEb}K({S#UW^1n~ASD379`FDT%6-LN_>Y0DRPVowJf68oG68-#C)0RnrlX#X$ zs`)l5r7Y#}@RW3AD)77I9hktzKZ9$RPjQCWh5aR)wD~@5wn@M2Sf{#~mqk}idW+861I2?pVarw`vt47 zerK}#68R_f!|Bv!S!TJ{-8PK|i}N4-XPmjNxXJK9VWpdO!*3Z;Guuo}A^MRz9E~*lS^hSt>y$}0F5Aa^zTwP-OLtiR9@ES|xcBX>)2<)>9`>|iKlEEK&GNwCjNhsD zCn7R+E;l}XShtVC`TMK}v1^N?BsBb(rZ(JvYs05AmvtXU1ncZq#?1R~7tWWTvJ@w4Any^uC5UcO;%!P`5<0(M*S5A{EMcd}FT4{8|o`=a$z}} znz~)gHLo|AA9a-Ta=y)7&^RB-=5^YvT@NTn+)Bc?5GbnpQi0#+VRr**7k(%H*)tGEqCgPHr!ugY~K`f`TiHp z4|d+MrU^TuUq5f^RrdX%zTwQhPd6Ki|Ajj};C|J5ym8AK&R~ZfH)ciTb4;E*$MAsf zBxC8ADfKyQEYg~q{SL*O9QQN4&Y5|5-htrRCwUgM9LZpsc1Nj9#DU){v4AyoN*VLn z73Nz_UOd0#f1JVi&Biqh+2=Imo8$tnDhotsPrRY{A%2somDnMZcjhly_NeU7GJTN$ z*RRXbXEEo-tpetblXg!(;P?A}4$qB}n<<+Q9J_e@9`oJzY1v*E)^C}Ah4rn#nFOX6 z+c)(rdLZ`N^E>Bu-A_krQdq>#(r3z(lwyuu_lDb9>RbIJT1)|>0LE|K04^Xzod1m{UJ z4=eaS$$iV3WECpjIQOca8AnRToM*xp7;m|lH9WaKQHuGjUgS>Ryaj&OvKH`eo%WA8 zWUE~d>!(XT*BQj0D918a%v9rCz@4&Rhegt}UaGNYvu7Ajg~G~82A|1XKFoZtkIS%m zWtDq1eJzn(qrO9_;<{joU8Ra~hW&JNGue{GHa<4ob-U6&_&fHoe}5u+fwjh8{lit2 zwfi|ADtG;qTA=1$$$#SceZN24YwBh=b~ok~O#hm#z<>0u^oF%H%i|mURj(i3y5Qy= z-`@-zPnRrZ$X1m-yf)!wjkqjB&*MFH%$I`mPS_{trr6$Wl#ZGzsc`p@rX6!c^Ns$0 zd^cp-ika7}`=iC{_-n#3ex_|6+4F^Sq^CYHI-z%IqTrS$4toaQLorWSBa;okHBFs9 zX&PV0+wc-_tJjFI;r1xGdTQSmhWQHnH{FWUf}S4vwX_Rm?=vY z@^5p0GV@S=tJHg-RqtS{z&6hIw%7xoKJR_Y^uEUVH>1*jm!ccF6DszeTJKP_^Hn_C zzEu9-4cD&C?{C~xq%cY7No;way@HWrm3Qcchu8c+Io)t6cWHNuaAEu5^dQXb-vn`m zkB5{w{hdmDA2md1Z}2ynf1ZEkGL>t7H#k53ek64x`9j~X-Rrr3O*>bb?zrq!MpXmP zWUf5fg2S#idA{(*@$G8zmirkR)%5?wwwu-o=RXy#VLB0h&9}Vi|CI?Y1@W(L*Xcx5 zW~#M21igB(!9GLdvjpQoy}i#)H@^E=z3A#eqn&E+%r88h72efgGi}o^!4JIbOBp=W zD^U|2=OoO*(r0+hN6bbHuivNm%}uO_nQcwv-Wvgznv$tP@Mt zOwVz!iZ{5!GDUsnTox~HQ=`V5X{whv*4StN@wm`;$@P5W@1Vl#jGKAv*bOe9Pn7!+ z|6?BGgW_Gf`wW+8EM0upsUqsd#oPYdo!{KPaknK%om z>x^rxIX0=jo>ZgXa{uLtBRUW4zvMo#AOClMH)~fa!y3Y9gy)PH`zg#u; zpci=Zc8Wyc1imSg`4p}{Vc*GB<0+`ceBXm}R};^LeZTZxa78L9Q_;L@59cpd%P$5mEN?#j)L?V% zq7etnOvT*})~7i&nJxvGofe(&$x-tzTaZ!30^J6os@^{eg`c(`X^gw>T_qc#AsoRm ztB-LO-;-rF>$f-5ToPxz=CSdE#R(O2!!<09!Yf}DGynYCbt9u7^6lmV!4va;#p*Dw z*_;3Ui9ri*?vKOg1g=fs<5YFD<27^r&A_}Q#EBtjqTt1TNAav}M_dIwRU&mPGeT`e zblE?x+$WmL8#2|Zj>{n8x$A4@_opWwd4J&gq@5RA72cmboDsU9N&0Dav#omR;cW_c zP3-otOtQWqlg6m|t%(%WHPk3U;E@3;OlikWX`2V8-=$=4zUZo z6ui^~IgXx|VQWK&kd1W!yR?N0bXkI&=m(6qKYZcW9x4kN#9m`pM{*9clc-_`o)=OS| zKc^~)rfN=h_~nt<$sYYM|D?`_M=HN8CQSdDX2E|#ZGVK^gqv3P^VxSwUn%~p@Iv7K z>fc;39~Z2A$M9GA)AoIgE9g`TXmWzu=c<`mB-v&i(dj!Ij zZ_ck1bCFY>pS)BtL&E%EUh{7wqwC+>cRZZP_fPXkd~x=khSy@|jSm+*ejL=^e=xN> zhda8_TDnGHErXR4zj{c+gZy%xZPHDi>%uD-o8s?mi>NBdjM?~$nxARc1UYvDtvvEa`-X3Q`@7=$H9KhChfsB6KVvO=uCQ( z^(T5lRn#Z>$$uGFiY>2DG-A8N=lwVP&iV;C`Ae4WVPEk+d3ry~yQ!YCJ*ysgXY8 z{ZnF@%tisrJkuul-%Z~oTQcR@v@Z&CcVzWC$e$|M$uP;Y>Q-LD;y-z8MjrPj_&c25 z`RtdfhhSysJa*2nl5?3qO?mi>=>_x4Wq%bi&U3uv`j?&OY5QPJ%CuI6zp=V$4PUnT zf8j0gPwnz%+jVR0rKRsz1^_ zr-UkeoaL_7`D?`r;?6}_e>nMv^S9)WxJ#!LH^>$F$FWN({*x#O^ZOuBlWRA9okDlX)@h7( z)2_W?h;&WT|3tea?jAWkwP zzTwOCF9!uS%-m)eCH%>CenEZ%2u;`*)|w;Fz5u3g-Hz|3v6r~ilY=9e`b zRj1mDxu1yYrvE)yzUA^a!y6kjCj4Mn(;KotEg<%P@$r8GtvP%rc;{bT)x3`H_$j3c zZ{G?3YMPtAK85}4irNyn9bKDF#j$^HW~*U)m|b>XpLMmu>!nI3bSG3RWUp=f%dyW` zJ6GWc+njId2YNR(ZWDXMot?h-0GEgUe(^Wn(wV*oyFO0-FPGEqyHPRWaL()=*2-|L z?DB%tfJ)|^j%{1Y70#sXT**J{P+LUUg1rt^9N$}(3&*qm^qjxL_dwkD2HiimZ1cHpG`~`NwzZ zn$+%Z4pleblsEZ4k#c7+y*f8x|E`_;R0@>#Jg;ZId78J9H>btrhwz`i4Rib1tzX-3 z5P0L;7A#g%=lMR4N%OdN6z7&LPdRrrcrUZx!J@F&Y>D;Yj&OS|Tc>WbFy6<9*DN^i<>|?g{;PR5+a>R)*FqDX zg z6*4buqjXF;LGfq=|1D3aw_KaN)}JhzU{w_Lieas7;{ixix((*%c#L?#qY8Rb;%KqSb!lEAL_>;B5 zevCU$=g#H0GsXUiRD_+|C+QpFYaWX~h^Wxo!xp7&cDtT;->R5Rrw^#P-A+l^F;|tZ z;t6{J)1mW=SapSe=>JLTV=g|Sa{Yk$H^~*e7r&OxU$fwJ^HsZs2URJHb=i-#E%afj zcHnx=bEMFho$mTY^^{b}`{Gt3{RpM18TeRZ;-z1WtSf3vh3 zVk}nk{M#s7viZO_*XI#s1>R5FmNH2$jyK?3leJ~LJuBzg`B)iKi4+)_B?T<{1B7 zW6-ixWq&NgwoTD>%=<6LB{J-=ywp>uF!K%fOr|iGRi3ODq~95pHi&Ly{KpxQx_j;6 zgG`l&F{^Xe|f*$YpPx$&^|CX31jPu*#u8Gw!X6DPiF)sT3 zYQf%@Dzy%ETeq)a`=MHNFr1+;;PzXF@;m(79xpJxdtRrhK(*eBal!t38?HX^`ndcJ z`+p~kn|{tFuWqaAv2<^r@=cuOw5jV8(F>tf_bVs7x-+HhKzx*!cH_JWGpza6?WsL> z{6LLvj(zizEBF2|Ny*1t5m~T$%gP!~5q0J34csw)RUIGp>%(@|{+S$Z%k8_pM3Dt#uGe;7WXVwJt1-0DRV-4YIC*dj+EIc8b9W!EY|-K zUn~2Xt?0(I$&wjI?aG?=p3OSVbM0!@t(*--+alPT-n*np^QKHlGZRiRTsiZkU-u)wH-qMiQWaJYyICt6=Un_H)l_rbI8U%+^F1A=V?Wn^ zvkCB+mnwaucBf}t!eW^R{v4}VEn^%z&i3_gU=!%$IqzlJF^BKl$<+#R=OT;~y8lY` zHq<`yJ|cNS{#9)DgymnRr#pIi7-}+Z>-E{jIz!R?hv=GS(+hVJ?th5X5j{~;efd(kVJgjWQ95wzA_2Si*W14yT;;XQK>Wz6Hs%^Xj^!;U|JZey`RL4uV6||4 z_d!%)@7X>x=Ijj9!)*z|y7yi+uTON%(JN@%uB(6H%)Up_3j1qc6*Kj}IOX}_eUv1d z!;k4Y+XRG+)%HpLWi1M;d=|BA&jcOCyox`X8=hX8=P6Jpr?+X+1MX9K*Cloc-r2_Q z@aFVHg?x9aNpJz;VK8ro!ol7_;s-4)(rke;at`JP|p;Xsg=p@QIaoDXWK` z?%H;T%_sRMac)@=H&IpL=9B1ijFTlYR2|uO^Hzy|iMCw*+)?Yr>`e?yI()9kK4|Nj zY$;$f$>Jw-iSpvDA|83OycsuqDVnlJA^mIQBS*VZ-zTXTQYLxvdZ;C*h@H?_`}$h5 zSxMy<84uCFj`N$=P5fiSZ*=6_Y1s#wub*(dsCxanj%lrrVI|APO&47^o=fO|Gf$t{ zPX4+l%Sw%xOVl@<{yO1oLi5a2+k?}$D4KEqkb0UwL3dIR&yz_rmojCi^=fczYqI=i zeIxetQ*S0=74zN(!Mb@SEOr|ezp-90s8JVVxcw(=R>S{m<(q{MXhvsnADH*5T7|(? z!`Jb_gq-IFKiqw`tr4g`n4Zl2!>p{sNI`S!lC=z#NpIf>UQoEzYAg`F{N_{j3p|DF zk^ENSc8}60IDTtNWj*=e{s+Ymo7Y`)ned3kPW6KGvl|{0Z1)_=+wkOw2lu_aXx6g6tvrIEMx<}Lzk-q8yIe~ymZHmS&GCYD67QJjUEQ@#$RjpWy|f`$C0+W* zrE8a_vp?H7M5#=`H8_aE%p!ZK55 zE`vw<&RG5lFO4SbOXyx|oPFRu$Lm1(rk*eAa(6l&OxnwG@YR7Q$sff3n5|{}G}nh; zuE~Dd9{KizTAnc#B5xYbCzc*K&maEkwSxa$t#ApInfu=bH`JfI)h0MW(#TV<@#@By zKa4Stx3TZYNw?t2liONi_G3=e?YQ;=z8ktb%Afi5{a&9?pq{meXT2eTHQ;66+6Pi(Z@HHLo*dvjS%W|gN3WQ6_o;HkN6u+)~xI<+!c+~Hg16;l?k==^M^ zM0qP?;XC>3W<@jUE$>omnA5F$b*(_%)90UfPJI5hI-fr$xkI%fcY^9P#v1czQCXI2 z6PK@H*{$@mlI5Rzb|nzV;47;5FX>&u&rg)nZ+3VKuvbbf(RPZ@O-{##A`jAJO_tnvlf*H9p z-4qj|0uGcmFPL=teVWrFr~gy`@_pM{vcxdpdfUmG3twhMw>9T%(0iwM!fby_ses>> zZ4Ws)vR{cs^X*&kCQ)KS^OS!EJ4}MqBG?xd%~-wQ^N+Y$4dqWR>m)4wGkXtf&7)*1 zzLFJo)8;$QJE3)#>6gQ#o%;{eYR%u#=O8Ybciqv*Ti%ju)5)(>t{iZ*UHsE={}lP9 zmM?tgIwfCt=jd6>H-}$pcYH(SmZ^dV(&r?7{r_#lul?$xL=2Y<$?w8`PH+`F!{)AUyHjkqD3AvwPTuiK6yQeU@1f42a&~W>e zeDjf-lTm?Vv%ujc!_L8cHQLb2hv`MRdUzp&D)-IfMa&<`{vXQ!gm-x zO*m)O?6b|MhFN5zVx4J(`)#e04ebx3{_9ORa9)KeJ#_^itF(T$1j9A87|GepRkw_O z-%4;_(^13mX-=r{0i)erJWSP{(H0Z9%p^4>Eak-dT^V;p;e7*H+^2yaO3*Mm7k0z+&90p^1-yI=AvbPU^kNSr0w<;=9^ z`lH*ooJlCo-P6lDQ*mx3v(9q;=jILPQ;zG{o~YyYU@JS?bxO3u^uo4{zZFCeWnGL` zIQw==E@RqF_A;?WFNK!%SZmDuN=} z1ymP(J$Qdn!(YyekC!F#3g~bB6ENX>=jBHUlmCbpiM-iz)Y72=;rF`o>04e zR_Md(A1C*-KCIps{e`(;ZK`_#o5%Y5hO9Zu>!+PRQ2%23?^y{qHO})+xV_7BThl!5 zZ@c0i7`gl_5p}$t8NHIX%q4EKy+hcmR86Kwv1YMMlU#3o)Ln5|>=Nf2*V_0^4WXC9 zzcB1Lc~Dx9|HNjaxlRs_QEF=+{(bL#t7)o==cM}i6WCoG?_Jtw#JFNz@Jsef5ud!6 z6{K&U;9Ihg*WYozhxSU=sKBCA=?`vw^sr-^boo$Flke2HxCWa~Q%n@5eyM!PvCE(9 zh3y6JO>WE=t`-GHHH%G@Im3KXGjfUOiR_Z8c@9RI`db^^w;jC8mU$xNEb}kX_?@2* zuw@?1G7w#36_ML}O8!8h_zU5Jy;}tH9Vf)uUvYX6RH$FUbt`bOqw|Eab1&y7)SryM zCSD_@o5b2^J@NGumhjuWPt7+xG}6dn`M2usSLTMWjhn+5|8P}btz);&cv{2upzEOe zgY34qhm#)MZvDBpan1E7+c=&m+aAe!u&ZMa(?9O)q~HVoTa)(iO?f1-x8XwF1CA5i zQWs?2Y)MOy35aj=lvQA!n`81r{O|jJOdGDX2{7yG|NN@m@!Rg5Z8g`y`+3W2`G2_t z9r0?CyLR50|BvVszW)p}->>@Nsc=40O{#gx_U%h845m(f!y@gPwVV54^}b#6xy|lb zTk_ip%imP~z@B_PlJO#2=lho>9cg`Xm$V+F|6Ke@-9!GTUwGrcr|VwUC3MTL^lrSh zWTKRU{)b66-2e8he{|mYQGVxo)wq_7Wf`k8>N@UzczbBIKwP5kPDLJzoVy37Z<+sx z^MRmkP3?mnrkj!;r2kCb!@VtFl`+GYY^_u64sp_{Uk`peQGSKhI@MQ_yKec`4HFOO zYQ9Qu&d=)olbW#9CV78@%5<-50zW*`ZUsHy&df1)u>bN|RoNqNwI;X2tef^B3|S5N zd`9PVRat&|-@kN6VP}dY*OZAqUzq;3a$9jNY5aCEY{6vNGKMGH-fY!BaJInIlu^k& zCaRqCkHW3rmmhrIq-MkZZkbEdJq6{QDe=5TlYiFn8r;11`J|)uFZVqxiN5id)0yor zo%+ppz{C8IS>w`aX8S}xi22^&oYoWkk9Whtp3c2YM(4KOwn^Bu^_#b&ox*;9eyhv# zruQoNN}h=N@Vw^5=f=>^wVTopgtfS;FvUD-(`hhhzn^}W@$rm1j4#)_KlQ#4S=KD& zAXxTOywQ2h*Ru^#EjZyzRB^W)A zC*BwQB0Sr{++eFw3ai_!%UTJVIunW$if10OKcFl9?6>oe0P|mZ4iX_BKR2>iX54lB z6_vT0>0r+7GyDQyE^UfpZE`F8tJJHgSD#Yy#N$e?82ex z%}4q!ez};GF!6&)wL?4iALezdr4KU|{JoQF&AG0B$ph;NmO0!02)wykW5gYE@I?78 zhM3DImcLV-Amr#B$sHCLr=%|a@@OzeUiVCN8|#Iry4vJ zH9_o8-~$-PIVxEs-h0a}5X;|j$AR0+yK;j0Q|^~slN^h^>Mm4SofT!t^_H?^`y{yj z0<(&7>kru!Y2%liBA3;^S-%LJ9`?DpW&!_K#uwkK0;8K&KbiGn`-54ho=357+4g06 zy2Gne9xIurJWpQ6)8X0fT{(=Fi{F0XIgv6~)xJsQ%Bx=@Pa<_!nL6B?Y!S)N zGwItU@g>{j(pg{dPLG<@{BOn6r%E3DpKaK6Hm?bjsgaL_C+_-t3r;D+=e+1)PLsHnf}Nwvo@2*r| zZBt_JxNNvLX>xAD?aU*R8)i=mvTDkIrXIw+sQ&n?&yp(Bd}k=KE0$@0Teyum{!CuB zV(-hIsQ-LHfWl0hVkSv!Ij*T4ispz$UHD`Kj;_f_*=4uTkglS2_FjA zgdDOKm@BC*xUK1kugRC6ie@vKWmv5y&viZ^VwbLagJ*Wy(}U($&wNu-N!$E5N;K(y zq;j6qmIXf74-33@Db?f+yA(I6OQH4Up^OioC5}pY)Ep?_J`nfFwx}U(`+-u1G%w+A zjLwEwMk&7N_hnL#PceS){P>Y*z(K_=2+cgi%{`m_*eRW zu0D(1+Bux@Z1bba+mAaeI~{bN-9o=LN73a|L||w837gx>&IkVL{qNuVpJ&o4F7bWu z|8KqaZXSp8Im_n?fr}baRJ^BYIC^#|Sgu&nu*gGfLXS$IgGzHliip63o+oo2x4hfC z?tAl{=X0K~eg3oTomJ@ZpP_GWncA9Gf9JZJ9l!aK;AXiq7V9@Ls3jZdm>yg@^+L;kQXB5yXUd!L`;aYT-GW&zOe;*FMF`y8|_c6x_mMVHhVH}xxRE0cSk|`Q>BVbX7jhv0za&bHjA@d zKAx?hdSKf3yjf-nr*q9C*^18m(Kzl{HtS^&_m=HG&y^<_-P4}K@_X_1o7b51=dVm# z91w@q&d|$USZbtmpmy7y4eoTE`@DxJ~NrIh$kY=Zc?Yh>@!)i443?wzOWQ~o|^qw zVRh=XmAspJ&L3s|r;xz#)4T1zaZ8J82{Y%0;CT%*RYcE*A5hYoRkYyq8O>Ep>pi7r zH7rTzD?PYn`cgCb0=KYN;tKWwZdr|*iv4Fo4OTy0^@K4jsBe}+{fy2amK0_8C8`t7 z&iUNURQyDLx_rt;Zx#ED=<88MjWQEuqquyE-+NoK1ziZ)gJl?lH z+VM!Kn%I_YX{th7ruAHtcU+$3$Jd-Q%|l=Oh1Bo8b6Dm+@Gp=(XmgGGm-&vMJ6F6G zyms>4q9JhU+%nS)L*w3T2TiM!#tQoNo{9>aOL(@h%seakj9LDqf5e65Qyv+#r2RTt zcp&Gk1NVfwO9%Z}Rw}f1uyh&uF$5oynHGNVN%CGx9*+H4tQ#_Y;$HGLMpQd{F+4rd z$$b5O?fU%<|4vG5XFH+%vQl)$((ajzdCOmmX)(UJzNxYEfT6VF+lGU)W=~8A3UFg@ zkk+;};rsLD$|T!_`V!?Yj3;6}y=5}iq)fRp;mWz%C+8ae9IBcwxTmt@x{kto15fSd z_-p4^A68I&-|h49eY5PN1MMAq4|YymdyvoMdaCY=b{T{3j?!7x=KM=mom63aQ_?%x zH9?xCDUB(>c~+Lzhij7OYdC*xo~A4qqyJsShI7lV*UNq%u-$NfxBtPIX%Q<~Z$*91 zD4nn_ZIX+V!rrs8It=%OHJ`|`z06AbDfgsRX7as+)i0RWHI;qHf90Z}62DVuf&GQ+ zqRjS7drGz+6nUz-zWIn}lOK!u;gqS03g7=M4C3qYsH|ov*%k8Xce7YQxvzG?U61TK zR$Gm&la@8TKBw~8Jm6sbf=I@tn=K|}tW8O3ob`@Jp2;|;PL}ynkaf0Z!g^_?qYl43 zLc^G9J1@UKXn)YBn)!>fu8Vm=u*O=$1E$6<+zg>Rk3~-K|6;nXL1mL$@_{``(`M;B zShxGtC$108Unb35AYQUmj_Lb>xevu&#C%@6j`h<^{aeg4)ce0Eewb%yB>slCesKxw z<{Q1&v==-*vD(~m8@t%dtp{Y@i@)Srx4=GNcGG`PjmGl_&zMB_HoZ}q$G5=oJ9lkk zVhJxlOYNzOaQ-i>KTj}jnH)NWF;l5Kf;Y!pHP&WE^{tcBm?Ja4eGr|osmk$I!(OG` zM;M=<6{_U;v~r88{tMyEw{fhR*{^2A3Pc(gs3s|9=>DClDeyw(g{%bkqiAh&hjLHG zTBbQBr4oGi-cLH5!npZJnbCE}EDy_KSrxxelf~aun;lxlxPI!+RR4y%dADv{b#Ua` z|Aw)0(jDec_w_9qJg4d0+~2=1$M4dFZfS=y$-|x#*gk2_JP;$E{;YYYhNut63&r)G zq7(d68ci4~LlbPTNNEmQ+}ttmOT=RVkx*t)`rZcBM-2 z0h82uHjJ7FtD>4XH@NO&o7~T|iSN(sy7Fnw`|td3mR|6(Vom0Om~TbdHXZ@HCbGR) ze0M9WKtXb;#0#VE)fti}Vm}uy5ZDpX_9|+F<&HCwAC~XX3Sx=Ku6xeS;j@|5gZ3e6zs$$&O=9a+lR=7#?o_Tanff>B{GIV6kt%wC#Q7S@(NORU7ReeLrH~ z{NcfirFv3I=iE}bLt2jHPoDQ{fAe<3P0Mrney!aDHjyvo=U3i`Ajl@r#MV2o#-F9Dj z@c)Vb2kXX@%S=x&6iuocTOu{eZ9t}s@+aoc2XA^vAm z>bxa4l%6RFYTEbm*Cj3={gatRO?xiO?iHR-;HAbZLs%OZ(A4BTgKE8JXaV&33nCVSL@ZN`~R0)PHKyfRDf z&g_K?-|2V+|GZhlylh&JS+Zkzt8pOjkIr>%P+RV1I6hxnQV;n zU%OrqGwR$X@Z-Rr2f0lh=b84qU1><@PWbzP zrX{Awms)tJe(rk1XdT>l%QfNs?OWe8XGkr2W~w0CU%@7GK;=B+&-c@18dSWey}7R; z*I>~$iR(#^&XmszJ5{$nVLh4V=Mgp`c-i6_{%1T-e=_^%)=gNIuxhqZ4TIU%D--V@ zs7sZ*xg}xK?Ja34Csu!&_=aVrqTnRyH^-Mv)$p*i)#!GNo4P27Yl#W(liCM6Vji+g zxOF5`r5scOUSRZJ)<bn&0MWFNpHm)bNeV^nvCJncws7v5K9@ zy~n!iG>g!I5XUFA7Zjg`{^b24Z64{>IOX&FS&R?zHuX3=m`2azN?7&Eq?W-fY;^#u ziEV9)Vnz$g5{nly|1`JXeOA4acURcwSMv`fcm;Fhlouaa&2lN=*sU%Ho=y?we+n7> zOovN4Yxqj0&0i|?Veh{c(FgcAQ$DC-VlATYA%b&rL{*I_!cj|FMdX$IE<^g)7Wy0ea69p zNpdXKPE{8CJN*1@nHEa?pD?BEL)qG8>zlr}&zrXC!L1j{f0>>3**p;Dm|nd7PTB=g zlWPViB8&=!@9ciaX42kb>FL?2_<_6q^iJ*xX|b=kG`kx2NpCgZAw20`*MIFo?K|_T zzOPMJcKDU;{a?2uNBF8n1fS3@H9!9wjQ3t96y9c-6sEGr0Ytq3WiO`t37fJF8}m-)W~vn^>@efyThUcN;cUP zZ+F;VxVe-0`V_-=#UHG*J_pEu*fzsAPRN7bGt;f{%|qu$4!=IHKEt57oO27KvD@hr zYzvGOKOXqGY=In$(Ju3?iWzEuU5lE}Y;k+el&vzIY18#H=7tYSjp9#RU6>toQ-oFb zOa@o8?n9|d@@M=uhle$uJZ2j${f1%k3QLBJ7ebD^FE^b$;IQrj_m64sOJy3~Ocz^l z`{bjth9f(BYnV1&FiA9>F)PUJT=SarIl8=)!rD(-7EBDf^_L~c^VzOah4|AI=FBA< zcCXiUxPOhm)TH2ehZ#%ed8XTvIqKoj-Az8qm!7iP>Xj~4k$7VnEvVOPbNzQvR7dW7 zCHEMq9mS7+Eu1}5;r05RYENEpEQ?84`j-EoU!!Q_p9x!(9y&NhUa;GAd>*^tcE5VY z>kf5^0u|{l%rhUPY)k&>*V9yDZZ!4m9#*-3rRxv$Qvr5h@J&4d}UedCLT&uC0OcZE^&c*ROClP4Ts8#4a3ZBe-C zp{(yvXP_kQ(0AhbGvx)_*Cc8bIL+F`evpGscS>j*3i?RQSAg z=Y7s{&QiF#lH=C>>V#R%J=a?$1fKB5G4?)@w&dToXRXHLgCX2kctTn)oL?xo zaMFWY#S^YQxb%W2m_g#nzh|}&Ch(-H?f9I>JlDZ)N@*VN2CsYRHWv=nhc`X2OIcO* z;MA%1$-=Lex*=bZ?9z+1mE zk~6B^K9kAF<=ttQ3rh-2*RsBI-hRQ9`EpFvZ1aZr6$h#={JC>+&w^=!Roq+l^<8s& zkoNQM-Kq=0dkzYq4)Gf!(@ma=c|cFSSN)ofiVoWW$chkeb5U<>s#ztWzPkL%Vy~R-eOyzQlnkS_hGHI z>`bvcrnQTDADrByo;>e9%kHb^zJ@t+5QH( z4b@BJUxZwbVsFwFIFTRSr2l7~^ns8|tBqJ!yz0N+bwE(^`8p=vt!BF$>MmV=!(O{$ z|1QG^lT6y>8B-?QV`((oVxsNP67()~f?$#R=BA%rTFb0waNd~`_+U}W+-in(y=A3` z4^F*wZk?D%`R1v70&A8nVpVuqy7TFFHq(&lDhW%nbq_0EF?^X`FMng_#OA$84t}S) z*n>RlOrJV#n-Og!FmG**&~F`em$$tU!ncLymDYdz5GiYM^IycQ%U2)X+$JtPubw^f zTk3D^%T+ss%Qrum%Pz3nY+dOczJl7rGyl4H*xKh_HDOhZzMSK}`5zZ&ws5|RZu{~` zxh)koGV^RJ zndz{I*Gq}zmPh=0-U`>Vrt39k6n33HaU^RRNB zw4BVnWYwCfGZWU`6#J%k;&zbJJ+_&LN+sDRC3Ky#neg!gtkah6?wy1#dE!EqP{k{J<2Y`6+Dco6N7@Yd;XA-#?vUlDpar?E~tcrcGh# zE10xd;bYz`6NYWApC@yD*?U3uFXN0QzrTcExVh=jef}p?@BAw;So);&zfOjo?%V53 z`$H;p)ervl3lwL%d;G+Q%m;1LcaL1?BZ}~Qz<1w#q*!ym8?&E{6 zv*fGhD*lLaaoS+a;?7dWdc3h@a>ylK6Io?b-4{x{FNF$bu6*RxP`042jv*@2{%U-q z-LkE_d27^U@0w1in7LxLgVv19J-kyU&evf5Br1PO?tx%rj!MCO`Qs`VI6sA`GccQ; z3T0XMxosJfx%;IZsteq=Ont^$d;9cW`HA0`>|Qc&IGfT^$uX(FT880zhss}(lE$>F6CXUAe2bU8EyGrp zgXcBJ=;svd^**~q+T+p#5m z^Eei|{d;&JT|h%(uVxabO6+&{R(^-1tw-*0E>|oI=~{e|^+5bXHPyLnbKm6cK7ZiM zv~6WPKP+RHtmb(Y9Q(Y{XItsn=?VHb&fTlzc-A#T{tRncdu&3P!-967?^O-oPyBmo z%M^X9Z0phkZ9b*h2Y+AH+i)@A+vdpsOg!RWbQb)7BR=)b{_rAxk4-5j|J6Ems5OPC z9eI9$&+0_Tf~_}Cs4QR){waDUwr*NYLXyi9s~P_`dFnOfD7Rl@Ja&Rz)aPVQw*mg#9HOeV;r%@tw!d0|Vs!3mk)`}XjET6$7% z_raQj@q74^!U4aMb|Al|@eR2Nj!rh#yA-R(KUex;Z1u_>J zetu#2?{MN*ee>_=?K_ec-rvxk!=^voQg5&xC9|b!g~Jcy3;BQg52rUgOKbjbe#EEeoV*}+-Q0xu|NI-=edBjaJ=wTD;<;l& zk&ESTZ38d2iPCS%)n>~w&hk=<$(-<6ZL=oZ%T3GXPhG`0;hO!5V#QA-Uv3;oEy(_= zY%VLYRC%lAj9JTO*Klu&>YK5?q54a~PDiO1X`1}&*nCTQepqhZvW@lf@w?ZjDzx8q z{>yg8^kv4ohTmuXPirKssd;{nebvpfe*y*Te=OHzl5z~}WHr9?#qjDuzU1xU5=%C4 zelkD7_crrZL;UQnhwB@--j#mWn_>PYqsn;)xAzkUgERRrH#I&#t)bPl=4r!8&Mns^ z?sD50ZjR(AV>(|dePLSTv+RP8M(*y7){Cab=3aRCNz0xovcg%&aoJ9(I6(#Nm9gp* zBBSoIH1@4Awsx$a(x<~Rz5PT%Rl(*jQ|2=SHOF3IKdYR%g+D{MzQvkX=c)8wt~j;7 zuG^ab=_qfJ@UV+^wrhBCOEa5g)4`SP?Op%x@)m`izGoV)vt;$mO0PQ}1^0iSb$+h! zyZO%7O>szcZBs%zN&uTXPZFZ;sJd*wJ zp^DAiN#R}&UrPIlfCc$Sat-cUw0SShd;+A z^XhMNda^z6Yfv2PZKtm4uMaERpWl02IH9O|+e}R}#~(o%+pC%41oah_SniovD;9`% zDDyXbJX7|#vG%T?>DGgHjJx-oKdArc*bi5StW4LHtoNLLcW)Osqc=IDj(zi#h@Jdp zT~`)}-LRSD^r`8N0=s|XrHoI0FaH09^^YuSbS6Zy6|kr*?YtC!utx3eSr&uoKB1Z1 zDN`l-m~6ia>ouB85DSu7vLt2tHHEWFdsea-P3sO~d$B4!leJ{>{v{k6rY~D2l@Y(K zP}=2bF$LH~&!; z-@A5!=cYiW2k)=H`zKehHs|z`Tm^1btpKJIDxdELY?v6Asms4?uK1uzSN)cFk+*Iq%Z18Y0oNIK9$Ngw{6q9ErgO}9Pf0%%KcMh^nLUq5;Pn>@ z3iC@Q&r}G{_0HuhxolC%eWsw;szUrnuvL-#jMt?PIB)1LoqUq}S#nyS>>hvRb?znX z(>8jPGkE9j*~6OGF{kXSqudUr7=}}?=D+hd`1{m&E$650wak1M-8}9h@&n1@S z-Ln4y+>PDa1Y_7GPsY9E*cB$8S!{5B##J4Lotgj3G7subD3?|E8sitqWOVb*LZut0 zSoVH%i1nOe^5EU8=l`BRm{xd5pT}rA>p5myjn!Yn6KrjLWirI(I-5?=O`9=YKF8|c zkMwx4lgqd+x%Ue*8u{EyH_u@G^ua8{munaM0&dH7KN}T`W?oe|YoVIl5d2R0AKP3F z@2#RUYCo+sXPL2}cMt!8-bvT*R$Mp~<#C@Sc5(O}b_cdfYa7uL-kW#X64q-kndwmF zE!NBU_gHFzRY0rG6IQv)eyf`Q9!^bAT5?Brw&gFSij~U#yDn|`cJWpF>thaoeR#94 zo@Z-q7ri^t^@a#-~sy*|uWP!uAS$8M=NX}T|-83ck(pitmTqUdGN}emG z-r8Zc@*1m5V&f_zX^JJXV+c%uk-1Gk6n9#=wIbM3?@FR z>_<%d&dyL%ow2y+To}W&%t)G?$>`#dN-aeD(+RW>3=L?*8Xv+VLt#*;@9q|LYI`z#vkI4q}p0OxXOz_Jn zb!(b3abu8BgpTu5nFS_~+yB3GdtE>O34==Hrj-A7oqH5GGt@(0FcjUMGSw$R?3DH~ zW=RLHD2Cl%nPxX0dvbB>f@zcVxGXYmML0b2VGm>8?^T@D6f<42NXm!*Wa_M@o^;ud z2NEw|dCU5FvWSu5gpWl5^^BE=GCVo|S>~#iAK*=}Yp^^VwX0E9{jLVG>E5y%@=to4WPT^%`u9(Q`>B%kwi6QCW*v8s zG(Ty}`tsfRrIsGNKPMR{7_+*2lyT@>`+CoILYUN~s0W*V$aO0wI$gSRL|h>6yTyRjT0=g+TR^7zwonVHktl=ll+#|Y94wVrhW4Ts#w+) zNBre?*tdAC59_(i?E$3;EWeksdPsdd6ZE0{$9ZS#gHuzI9;sYl4C={m+|c$_`~$bj z)uYUvX0uB*0=&Xsie8v(bv~bO&5}8@xtFo7RTaH3Wty7uh1fDy-WSH-JkvSC`PXwC zZL$wMy+e6HSlh`wgQLFoeT+Xd9=>Z_vuO1j*15-W$|df&)n3=Y(KkL zrqV?R{hx8i8)v`r+kG?PThL9H#y>f;F32otTUA%(n&sH`}GOS z50vcMdHjLc<~Z32oi5KBw@uu*j^RV|@7QF9)$3>8RM>phHCpn_!2>MOqI;%P#Y;8V z3Rc|hW9+;6@}B2{?RT}~n`)O_U1D`YIJp1tg4s*@;#kk^kSUVLPz~P7JR|YbnQ2UO zk1K3qY>knx*L+bn^R+np_U_n!Rufn<`}m(Z>v>rlbT3^|!uYR9as|t?mdL{b2G=xh zI<)UR8Ny`0__cz2qm<2J$sO-KoTDZf-wI@B7Mv8F+;}rZo%KSb{zO3ym6bc0x2c%> za28xH-8P3w_qaS)Mxp$Eixwv*HS;O&otUR!6VSVm?rCX$$-R;O%$6vP2bnnlWf=o(MGPUg1-F;Y!a! zTZ=o(l@zAu?(t-bO>2AA|6qHudnx~pXSe+A*(^_o7K<%eqxHP8StX0}ft+(D^S3@1 z32v*Y@lF=CzAI`%Z&=nZuH=4G8kzmQDg2ht7q+Ikk9oJ> zcDS_JL(ieY`O?b(Z-cq2lVlX`XIkFn+H|ERQ_1n)B9otu&cCMGF+KV@XDxKlq}X8+ zc+fR;Qj)GzM- zX5KVE&Un`s1w$jhOU+xZHOeqct_+WnRbZYtORU5u@@j*KD)U^2`JbkqWZn0IIg;~X z$-CU0obM(@YtD2Gf2nhlAu6ujQ07H`-D2$%7Qr6-nXS)fYnM0JpWzQ;cv4)c6yGR$ zfakKbPkv^~Ip+M89<~L?zvjT3VRON%jPlsD_bj@#SnC3Zqamy zdrM}DFluGn*D=+;+MdeLVt-Zt@0ADs7q3L|e2Lzt$acql&J~w}=oqhF=2(F`5B`Gt zzm#8aJhXf`Rf_qaSneb389SfJ)HmH!Ej?o;puY2o-i3htlg}62e-#zUx@>>fUY1>s z=Cc+(PvKBG>SVmYV%Do0wA6b$n(H66L;;IT@WxhVM3>12bgsdE@6c?F%~eZc4X zh55mRlt(rJEfbB{O_oPLo7eo``vv>|>JQmJ=N=YUFtMsp3Xwm=@4VL8_Tb&C-|7+* zRE)RiXSnMH3Rko<*YH*KN1S9zdEVp6dn;)F7xCuTNqjB$+b&do?z3n3b9znj&&J>> z&$(WtFWaZhDWxm8@bQD99j6vPdvNT>+rQ@ozWsSz#dt^Z`&2RZxz87v?U?d#<1g(N zzjp4HFy~A6j;xR8Vfnu<-t?uyizMA=d=1||FOGHlpuQtbPkG1PI!$xI81Db+mzwI$ zY%bKx|Jm-$dPrAs&dKwFvlw$O@I`UmsdRkZRPBH7y0?IpOqQRqhm6F%2fghra-OCq z8D_=CncFv%U%&Q+t1&n0-ONsf@U!RBG+*#X1>9qaOn=9As@XP4oh6NlxwJKn)l1<0 zbmslSm48+~m^UNiI*-M{qPr}YF8w_rpP=Q%smYTjUj9^;V~wHe8C`>^du40b^;ZA% zb9RV+tAF`m$^`Fc0v{Uqjx=v6I=e+I!|=09al<+`S)QVEwLUs0)QyhnvK70@&5S-E zJ2$nC@#V1?O(rYf>>tJl@4wgjHMQZ}r*&m)e|^(}`L;~Hy-=R<_nqrC>_sz4KX_$% z{nT{-9`jT}`>RwIW zEl>SVGVLiaUt;~E`c6(bkBOJ~YrBL^|JJHIn19~=j;-!dX&saC4VRk-AM8z3tKdDT zoxHs2sp5wt(Q+M3`3+CDJ$BsZFoP{^!bxY%3ED;73Ny@)c-mAvJ!`R(=a})&CAJeB zyS%FY72o`p@_+55YDk$fx#NMurq2<(nl$xe&xTDXmppGcVZX1JCO?na{s4w2a-mDa zCd?F0VViX#ZWaUcR(9sbi0@9}O|#D>m2>zUsLN$osS&Bkoo1u!<3AyImN#E>wrYKr z-vvvT9qI}5Jp3;6p2*p?TlB#Fv^}w0BC5-0GF`bo!INpGhxSbFO=ny#GN+iY*5tdV zZFI?{Ia1^6nP>+uNhhrd>U$jB78puyVJ$eyYrfT?dbfKT%MPcad!dXkm#_b+>=5N4 z{#o~hj#03D#L;5ChCN<>Y>jCXKO4y{nI(5&-2&|;(!LJOQ?9*Hh+fKId0@?#ZC>0T zrvLh$$?DSnQh7OWqSM)z8}xOGT8kv z%WRy>w8S9zOzDRB!l<@}Jum$-1e~@XI__+!uyg7BKfGx?_h%X^Y<=2!g>9u*=t{05 zpSk!-l;`m=FUWP9d{;^~GNiNUiN&9?fvCU*l zSvP-`;fZRSC&dM>zVUVJ=4}(-F<;=)JLkg|cJuyGra+#l?h4!~F*~ewT+CY`&wgk5 z)-aa6GI9@YF;1><}0UP$~)2D*iPsK_kMnGta9rXh8N#Uu5upGID1I>Ler)b zwcNipmp$`NF#WlOGr)P?2U8C{n@FZJPh09(rc8K#iS13~tsAThvcG!IWO}D=c#GqX zb(MqiLDkIM?-EaZt0#PS*q+IFUE;~aG^0qz*C$dv8P9w;z3YC1(wZqz2U34q*EiO@ zoHzTsV_s(HW{ES(#i#eNoy*{x$+T{drUT=I+-Ix&+Ai5o*&Ff2mSd^rB2R5awP>A* z#Se2fZ;o9T(|F{aXW>JZjz4$3rE6GhzPVrE&5_T1F4ZJ=)zXIf*{h&Sc!>pS?>nVfzi{II|6h zxAs0`y1U|V3HOXUd8^nOLf06|GuL~@sWrCzx6Fyu%stg;n|He+-eJMCaDxJApES=Y z9tF;KY|4fINaq#4aGvlk!ma79;@lt;L4rmRMf2j)x=PLrQ;Ht0eQn{jB_ zOsxZ%(`HvGRBE_cCrsSqE;-@awjw?6q&GK3n`D*&y{zT_oePbvrYrF)FUwZ4hwB)SO?zaCZE~ zrHr)?takoApqKmHt+7UB`x5;Vk)KXlPWZG(`6=V+{D=#@Rk7v`zeD#G);GMnFlU$S z7S`{jd>;heuQE;SDT;O2e_i$+n}@CDDshK(8}2?~4`o`X^`Y3n5jGAh)Jxd?#cs{XXrm=|$*v{w1u} zE!fmlw);eJTgx4mFwh!c|om7ziI>&_h=dC|U zpO{*gF5qGAyHzK{RCJPeE>G04?J7*FFHg@uP*!`a_=4A!B0&YqvoVnjZ;EcK=i^O!a}-`{Z8;e3&Ic~i}j(xnD3q9-jm@1Q-K>5}A&<^E?pH_V?s$B66CyDgJ9 zKCsG2&t!O}y??sak`ogjC4^K>&|iGNa~6Z%R{QYHjt_sk{(h#*{A>G_sc#+HBvriWiV>Y%V)r6Tmef45mT8y%9*X5DAj*H%BlYjrzik##`xnkt4x_1(Kf zqIsA$=Umvrq?xnK+j4_{>Mmc#IML4&eHoX{>X{PVaCKpx+K=@+9xv3GVDiU@Z$q}k zk@AG*O>wgtEmO5-{Y|(&)8n_{i7aDZI|jdi#<>h9gD?Eg_kb;jSD6&BVB?i^F%PxN z!EViWCRP@4uF(G7!FKshNs@p^nM)pX?+z{B#;i21s}8?jn7w5FW^8_-j?1XE*ho%e zVY?>ZyJGv@zhw>?|H(gW>>!?1_*d-E@2|f5`C8&j6|XaWStozItbqIQ`d}HS&_C_x zoGzLFyHnu3V9x8Oiw?$2*88SE!RpqXH!O2}mNFL{zw<k4WKzO?C*`idEKZRtX%BBH z`0jkXsljdc!Zg7azP8P$yf~(MYQ0N(;bHDftR~#84zE6KyTZCkF@$;Vitw*WJJ!y+mCIVY>ibu&7lHF$+7*1& zd~VdZPHF#rfgMT9UaKo4KUF+?FyB#!UExn}+g*`@?O#`UGVB+9nIqDov_hKo4s_@M9NrMKvrkZB`*BSEf&FU!3i9<;9H{!{v(4YVI`7SS?^_R265y?9#I6UByHJTh(u?JGuq7s+Coz z6nD&&->Ut|w&R^tv`&`6k#$~$x@B5RG$cYbp57Ojto-c#jkf}`4SzXx$qHB-m-*)~ zEx93f+qO!?Wo!3I;j5xd&CQpaOB>aUqPiK{w=jhs6n!jP&XCf)ckSx~ZW||6B}4}X zZ*DyEp(>kg!mCFd5z9na5tlPCG2u70;2 z`?H^=KLuiX_J?h6I`%qYyX+J3?Nio0*!TT?H~YgaR*~DA=bsP#c)sCU$?HnSsGCP; z3OW>@Tvp#a+vT4MuJ-nti_Mmg27 zS41!N=%!3ak13xl=?Go<6l%c|dM@b(G+r%m0KJ@A#by z=8suacJbhY*bilFUd|tyJA~o{nD*a1|EBnZd)CAcxfR#m-#mG|zENKE-t+v%4=)3n z_KQFIuA1i6)l$$N;Iu;|L$ThmLZv0DZT)X{#S)#mP45*7KXo(zGxB)xZ&BnU-UYjT zRID$|?yO<3F@M^@@Oj&nsgD!trU~^hygOCr%ls&_$&Y1s+}wYh_wsIE%46Go_t~bm z2h?WinemjxCM=0%H|q{RoW{l#*yz;IKYx$#uX_8FQ#9lE1uZqP+~qw@=jQ*q!W0GT z9dqiIe~>XKz1w?X_k^<2U#DhnZrGW#E0A|d%gR%J2L5Mbe)64>{Jb%R(K1i(MCOL& zXOybBatb38yPJ6@-{Wrj8N{?q;e=nMi{k_KpPj45)iOu0}ICAscCJqf}O;4s`$9=|PdkVjaR>~`Rx#o2J;bzj_vre%6;GHjOw{it4 zu1BXWbAC`=eNUQQZT}Ku-h*otPEBE0sd4*`ibnbVn*Dn+6)vksnm8*bOxdH_ejqeJ zg1<_{gSp5#+FRa$Gic)L2mhXJ%;w(XZS2)p89Z$kf8&*uqSKC5mk&;1;q>;>Wt_9b z{`A4AUw>!ka%5_(mFMTV6nKg8&+~t)&oTcutzUJA(NfWRi^vSm6u$Fp*8}G7+Wa6Z zqmZ}h+#*5uhRsGddYIm>=S^kys502@c^As_2ZyD_gG~j8*N9=DKv{ z%Jk(9W_z{s8*Z=b|1;&m{ZI8=d%3TeG-vXt1)iRr<{&-mwAO`ro6hQUtI50HNk8Cj zl3cHKH)ir}1<|vT z(!7ngjBac5EZf*=?%$}brkvk!E~s#QWA(xvVhYn$JV=j_+RQUQfRxK!%*(e{=@lz?4-L7uROT_(RgRm0?u8ht}>eKinri+ zw&muVw+z-{Ld@+BTot$fzIU)MSRR{rA#_*BeU`n7*`bnkG8vq?JUS2bxa*`BM* zvHXg}GUtrhc6`4R?p<$YIXI_Gezx@iKF##^4Lyd=rLsQ~H6`wGoSCTmOZ&zBS#f1e z)ov#5n_g(tS9WGB+xqQ(6+4;*h4{|57Y-;dLXATm5(K>mlz^N$_ zwo?S(BQk^g zM4In~I}iS*ovxhUaQEfYjlU22eAs6g-yoje=#h*aEj&;H4N{)ams-g#T%bed5@0&TK7<(eyjFWOGbwwHPNjRfXJY)Vro% zcz{pWr!qlj=2GPkrtUlr$+-z>45FT@e;fHku17Ao{KUGPYsa$5f4(mGX_{?eYS1LI z@UTK^$4A=>J4MPQ9oP)6)vy;zeGT?W#66a#`vpNI%`i14Z zKG4f|y^Q;SrM>8O$D-YaI*gC*D)Kf>Theon@qdf8s2yY8R_(|8`R*+1na%gkpZCW_ z1^ZoWMnXQRXID%<=$p`)e{jkM+n0>@EN34H-H^<&i#dURT_GQpeazw3&Vo0B?>~4Ftbgb`$uo0CDa1(Jd0%B5B63JR<9bW< zgJnBEKCDylzmxlx+33XA;_D9UcD?-PF=3OHYjJb#yJe@1750jy@n%?t1<34Ba$TyQ z(WCjslreltcjF%g)0r7k4D69k+7tG@o0ZKia=kCl{)Eumx8=VCxd*yE8&Mt0basSatM zm;dDYGplaRy|xc5`&VaktXuT%lXSDl#F{k8ig;F`J;e{|)E6vuSae%}z8m3?!HK#qXS(@=K({0%L_o^@OJ!S}DW^$a`qSsfE)eydyg zMfSqAb#uL0_B;Pr{~=vLdaH`;LGSF%RrL+i@9KT})EurQ@O&!U`ek>YU1ibVs{3*& z+wal^-G8NWM1xO+F}`$d*~8p&@rk7ZVrhwnA`8cfAQ^$v=YJ@e=alc{OIsvj;HmKO zNGJOf^WP`>SIPTGOjYU%9>0J+l)}LJ&8ytUM$YENv#pLAyT|WLK zW)IHiVjf$n@A@(e#b{m^yRvI)o>ohB8+V(wb4Gp8 z@(WQP#8tQP_p|cuN}9#~B)2V)bIWF#>w6bSKHcZX@Zb5remV0OX|4?U2~k@v)GV02 zbmtbvzdl-?aSv9cER~<&`0gUJhp*SM#+b*#Z&|Kfp0b8P_U*CPVi(-sDR?(>_&su1 zHrcU`*({T{oO_8#=-*okOW&?3P&Dvc6zX_Sb=ExvU6tkEC7yK1aQ)+}nz*h}>44wK z?C>VJ$tGsfJ5=gdtYZ{b{;bJdwjn%ZKZAXB#k$jcc~uqNtC_R@-cDi8eZ78K`-A^) zwi$4$#QmSN{6MzM^%4d3$`eTw*34?%oglq&!o`GhJ1zeOKCsn&Db@7uN4kC5b!!_> z>85WXSLZh-2hBEJkX-S=ufhJDKiBc*`{#L!tPh$@;d^G5pfddmb9j)^?t>5Nm-`;h zc9=`%-NM6H60|5>BW6Eplq5O#i=sXfDv-AUE4b$sn-omFI;G-6f?B$?L3s zAAGj?_e)!4>-U;XKT%2=2w|;{(Ur2d$iRFw}Q4OmS&R514GVDm5zH3o}&{>Dc3==KSXbT1_ ze45h9`;X;bMD&$&?7vGN)ENlADgAlTo8|YRwPwzW7f#k?$up~;_0ajjSl_|NE}76<<(J_gkr*@=q50jr{JsW4Bm=xMTX~ zLw?LJbuPc*{^0E;_Fb~Y%OmwayT`2O{ohVEt9aOL=2W`o-nzF+VP#48Z|*zmE}XnD zUErVH%d8BsAGL=ho#UHLiW$v$nD}$J-g9pd%}M@kbt9bl``gtC%07kCjB?8suvfg^ z>v*qfgSAi9Zss#;D>KZPZyKhs$9!^Y`ERb`@8l2KO?=V*f6d8t&kGb3c`_Z+KexIu zG^@5vIq+U+(tU@ZPjfw4jn41W{_J3JZBc%;%5rm*=;{wLNG29}CD9g}>xgbwDUtozBj zWZKKDutvF(&`#!=?)85eQ<``tE^;umzx0(ow`+2LgYK3kLKhll{o;z*S@!ba*U$-H zlwQW|Fm*}qIwe>v{cg#wz2DAxa%}6ak&)nA`EL0=8FNOtH@j}XOqArfaX@f3d(d;w z&pN5W6%oJc)~g?FOxrYL%Y;?Iv)T)1KZrc5UK)3Xb>0r!AjU^kM_rf1u;?4@shAQV z@W|fh#AhXDmhG(f`y_aNt@~`s6_Z+iF12w*;Kpht{>J}{r zGJVSe`wQn&3QZl23hn3cssx!COQ!G~`@;0>g+eCpuAXn3q`v65?umJ@&FcB5ru&!H zuj^b;ezxXTbNto4Vp{BVrqj><=ecA4Pe7K@>gv=>$pT)prx>kdJ5lv}`$~pQC#R^} zw|r95^O(hb?}~%wWX3(d2VFLc32m~^pSyu&k8a~FGh3IiEayXJaq?|BtJcN4=r*F_ZZYf0m!0-LkJmomqbW&m6%e_ovnQuc~{X_j~Q1 zWd;8a{PpFZ?fk0b<-8-$7-pXjF>-$RZh~~I9Mj7GUe#t#y*esw-}!}@6>w} zs`KxCx|x3PX_ff<*!8@g^=f9Gf26s-=vi5nN9~Avb;{b@uKhRjyYqD=T%W>pzohZ8 zmn-M_@BD9gXI1Uz?%$#c{%f1ciGR=x_lXzPPsxwErmy%Z=Sbs& zK#PA*Zk*h_Skbv(r`BjnLrMDOr5?5%ecSD#KBq8TI^#E4f1T)){D=i9+rB?MFt1j- zKe{7M%veu};pjK9%S@*qG+0age{NRrHBgfOX??}lxk8&Cp3mpcwKHR%XHzFr^Nm4k zf1G9M%(Q}Mw^pi8k8j+4R4%Q?fb$f8?bg5Aybto8&sLVJ;WGaeFIn(gW6Hm8Q)eWd zR$N&BDBM%@VaTPGsn2&iANO4&2> zowvozmldBkZ&rAFz?fh9K;g!$q65=diVx@*vmd;-_5H2g59S(t-YcB&c<)Cuxjk3Zj(PK{&F?h) z+29=)8phzXz22$r-9LZxi2u^6> zY-u7ld)Uuh>0PINqU`!9)rOx3u1xJyXkDrjd%*OP+aH#)wv{UNjv>=u6l;1pU)hlG z{>7Gr`KN-TnUyZPySSz|A6V&7@*p%p^ZedcfjbftEmxV`c(`nKB+I|D?U!z|HkKXS zcaghM^FDo+(p*vA`=*>f za5hQmZsWmwo--MDO@4Wavt)VoiO7V7*QT>OM0h=RFnZ;z$-%Rb$=M<7v}m|&g4gH{5-u-`C*z zvSvHqp77h?od3(z18T3Cd%p$;OZ~Y%Y1yq*hfGW9kG4NDJvEOX z{3|!>u_)8*ly3KFteI-h-=uQr^SRz|dT?J|?*og8xoWPo$1jCH&Ku-jh#8*_Wt#T+ zNi^F!+^?Hk@qEngh$@S#Q_)`0U+eEYwiS6u6+?N_|Pxaa$u1r8iezRsme{SEg| z%`Ig#;N7FBXWpX!^RAM9Q_5xLZxSqFZgNXycZAkGTrqpXlnEbvkC+Sm(Vn4un0dq9 zw+8uwdn_dH>n>t=vFbqg4%P>`TzuWUjxQ=N9STWR$Vz`={;*o$-UD%-(zYdjo}PwZ z6|}Oyu+Qvnj!iwTHm|LvMt{?hFASO4`_IqdQZjOt?~~&D*!)=aTkZt@%J_ZlFVYR~ zHvc^+Kg&SFRlQ)c^Re>`6CW$x|K#5^UtiPcos!Ddg0oES3aP(uth9WruxW`&_g?n8 z=||Wb`xRVY&X9_fa@vsdA*$=l1QWA`L3c_$j4jqVtXr>8VfZ0(fsTrgUxjsQVn?Q| zwYq!kgN9#LtZ`o|4fS?#F8bg1Q#D&}gKUa-kf_IssUIRwtylbXT_k){T6;+Ap4)}x z2kw46eoVJz#^Z@ImDrqjCHz`*F|siyE&NW<2dT>SKb|VY?)$R6sIB2x;~7o8hGmAz z%z_g>O*Xl``FvZ#_KxpC*VrZ>7w!5hd}-gLRTba31e6WmDxG$;Snt;>!Jx@?JoVHJ z{-&!;rOlHM)StdD`BCx6ACb$(kA*I*t~z*zt^C~Deosch(BLPYvnAhr>sYfeYXS40 znG@eC?D7gZC~`4j-{w^1{|vgXwy}9Thpg@B4;E=Z^Y-%p#6=286%QV-xU#^p{Qlhb z9Z7|zd%_hh^oruoWL}tL)%SOTTKKFpna3StChp)5+QV@9VAvLU=P#2p#G~1i++9uj z=dgL+oy65L=|FbQ@k+^q)=K(&9xI$%GpRaZPOmv%j%xMwFsA%g*|%#MKF{?`-p6b- z+a!gp?S?N8qh*%*DS-sm)Ex{O*^{ar{{2>kw$hIOpV=b!@;Yxl^Sz2l$vBRe&(q$_ z-Dz?oKPPK4M<6HTi=6l4W&_Q$Anwv^3~G8x+a2VQLFmk>Wpob?FX0fJu&^R7;vd3Wk37vJ96K+8qM$j$#~yjGRbz6ypL7z z86C!K%a`emLGk-11!P#J?3$7oe5_pIPkXib8|NLZ-m+|8=IwW{W}Ka3Wq2UP?s@qF z-u*8U6!cRqd5k=TQUp#+Pn%K08`M~troO}J-O6m%g{K~J_ZQ>|OV?$EZA=@WyG z=;X$23+%NXPF;{c(`z5U(d=I;4Ev(}$`a;Ps@XT?X6!SZ>3DCmxexCIRWoj8hyI-r zH#w_f?VfYJ2$=bJT~nk_(-ubIlxB_%H$@j2%vj80T;C9zT6QB;L3GK)5C+eLWj2h) z$9N2^9nZfmeb4p6;@d382V56VFJ&|jKfinDgSf)Kv56BVc^PzgC zvrl-h^Z)SF_RKPHUXd&8aQl?3J)6p0Z`ohccPgz9{AVj!d3)(&2lkzp(o_Ym)xKf9 zd0_PthW*NnmaIZkW*N!mcmzbpE3M)3j`#l8IhgoAGJ9~8ZOzr^;3cjSrJ2XeR! z#h!4Ls;b@yTp;1p3-i{u)b%evzEbWm8mOv=3JJ1R$fs4l;iWk{V(@D5SgJd_vyA~>&Iq4 z|28~67<2EtK&5(w*)O&mlZ`HO%{aWQ-HdVn;w{TK?!-g{e&35ad+~+VSJe$bb^gz{{$^7$vZmlGJ_4)8{O_>sGnp#H$Y;`7PiBQ z2Ctu(8cUtA?cEye@O;;lJj)Z6mz~ubu3a^%)obc1+Gy3vj8l0M!Fd!t45n4cMMZo0QX^-Y{tE8Xa4L6w=>ON%ely^F`YKcwfY{g9pE^k-N75LZXm&d4*$Ls&u+Ud&76S0ER z6$P{pT|ZE2zqgKa%hTv%4V~u4Qks9y&aeNJpfhJ)^{d5%<$EGsklKg zKx+eIQ#P}BbGBp9tj}ty9xa7JjFV1DJ8E%!;mhN!VcPC?H>TW}f6b|{mlyCdxm3SOSfNjdhvMETo(ci6tjO#vZY&DoN2vQ-?O(n7(<&a()AxaC>?pzE{pu7;T!;%CAg zWRyKPL(=#{9n_j@nBAW43!E?|XL`kh9XiRDA2eSxa?HH-hjWwBmN+}6TPJM0nq^F- zpX$wcd{1jVZ;RV4<%ORT+-FIDJb&Qbw$pnaCq&JfZN|}f;(&CTUW@RO9WhL|bxV$$ zHLJ`oS5=;2KGU1`L)_Ng%-#-OUVD6)Ket_(81A^qQ+r=)wfz0Pe^!WmnB`U=Ht8SN zbH6j(J)ih9Sqi-V87+34=i%kFKs#c6Wutj&-#wO{o_l&2K7EWSZ#;8B-?y&7_bYF> z^dZ$dGe3wum~-sZ_5) z?%$W(TbP6zpKZEh!jL!1N=@nwLvYBt#*pV7VN5p@yk`451QmBM9|+l}ChZ~BE6Qwk zaLMNVjhV+oCd+G_aJ$reQeSn6$c2z+-Xbqt{$AL^YtC|{%51oo zvOxBQZ)8GN zzp%fE)|)+#WuMyK8it~-&1aZ@v#Xv7;#hv^t(9;|Q<}Qm9S)u7#x+ra%NuWMt=!4_ zYo*^KftJk|z2qD##QZHei)Ne5VK<)BpK5-Bv3!#3f#v63nq64`bx9?EiBWlpwMSaz z-E7`nVasQpbf|p!h^xsYebPO)>LYfOyc524JXX2Enr8m8@u!qoeM8?d& zc<5et!InKZv^*pFLf*5L%NTXlxPS06i8fzdEav#+=7X&rnG2S0UB8U|fB65s0o4Y7 zm#jL;pQaO?qHpo|_rLH5TAR=AWqTJpeMR_z^J4!T^qaF&R!!vgcr0Edc0xV$3)hPk z9}30U!o93NNPKDBy=;Ax#HO*i$GbHpx) z-|PP*D`56_i`@yOpIxmFa-YoF!)g?LZq{rCQ%Ru+2AiuJwlVEryXK+BkCkW3rPwNM z=ggaWpncO_dB$)4Y!mr@IDUWaG+}qjBqoJ;Z$n#FJ>{)m%`e>jHSIO8P_mS<&>8kE zQ@%XdzpCxkgQ^|+sk#A?w>L2DSU2$(=ZXF&*IqhAhc{U&7~fIiW6nRFZN&Rz*4(#2 z4Yys3?{fb+lk=O)qi3IH_yMs=+Cjo8O>0V>5A^fa%jdECSDk2DKK`2xrck%`k!~!F>Vg>+9dw&>Aw2E_pB<)zTDinT)uhc z)3@8>8-AYEGqrbmART=(n(<)Ly;TVkcLZ-oZ*T0`E}m*LLwj5MPnIUlW_z~Jhd!S< z-(_FAKyKz21`n>NNkTjR+utqt`+m|L=1Dq{o9g=&0*^HDUE!L=vhW>e>b&I)J5_>j zvIWgAEMYsT^jd{Gput4sf&FnN`EiE2icz)$`DoJbm6lUCNWk!+O#R_L8MiYa5SUa(~wJX6o7+&L?W7l?|`9 zOuz2n^`Rl>0Q(z7wiH|K2$mz8dfzr&aFk+pU21w)@q)<(y8z)c+RNt5?9 z5wDYsCQGf>HmqLqTxr5&p(TuUPB(a5lw26EuUsGAcyiaAlMB?B8rL_7ERu9>;Q!fc z=fJtigYUp34+)L+E5DRp@O_f5|KOw8UB*;Vdxi<`p3Zpft)Z}Gk;H}8e^1!7d*>`**m*@39q=I0LjGfx#>_&epgslwlxNA5c$$9j7^q-xkr zn0DNSp-=pi)`qC%*_IbpUYfF1;iU&l!rDA#!6iFOmYKeIGd=ly{k5YxjB5|G8ZlPx z@UL62%O_ReL+Y+8#}|w587!{^(?mZ^iFo8V>(iX;jd3PP<_`*|t2~H4_Q>J-XWqmK zTrr7e0sUqs%yN^D^dGS0n~o) zKF%!wsie09VHP%R6~eoZnRa;c&Efcchdx(kX=crba`#~$WCFq>tAL(h_D zd>5uD&0_k}(6>}`!sLf3Y$wk~?BqQncu+D&PwBL z*w=RPq=N9rrN$3F_Ava&y(96z_``kf_CLK2aaBR`9nYdjsWzMW96IW-8C(9v~PfQiZHcz+z|GCadW#SS0hrP@~ z9|9gop0DK7U}VyNKb^(GSBXU@X`vpYU-7Kf4lHsn895d?aJX|ASnxdHc-(N{^yB{q zOMcYb9FP`@^Lzb0{+ZK*)PFBdhyUZ$PU=t=<9HDFZNK`yGnVh4etr5>_TPC+PWcYC zSE&sj{C~~={N(9x-n}~g3g;RA+kdhZ{A?uu#s1Tm`MXs=`5*ZB|LQAMCPt=~Mv0~) zf2J4l?v3fYtjx-A@knx`f}&Cbi;}_rrF9N}{>NAG*a@h3cqudqH2x2=p7(cp5cfy_ z1Iz!UGe|cdWqkMawVnQlxMiQ`|I%z}2>D~Y&oNGJ;dFN;!-|H(3JxlqA3pv1er2`E-nih6i{sh^V0Goj-sGn2}rxMjcRzrORF zvta6x|10|0KEyrSP`i^c=F#bfz|M-Fb^keAbRBIJzS+N1`yV)e&vZ>Aha;y;-1nV% zd1U(h{v*>X*K4qK9Ic;OE~sxG9jJf4(}qpNUE)K*5(b`)Cr9A6B!-|dh<+ZVSQj|$Jwwz z|MTtN`lkF%Eu8!Q%yHl2)6YC9ZkfWf)ViYre~l9O_Ub&ZKV|>&|Mh!&A6p2zFpBKm z<}mFT%Vz!yEFB#!J*FFyuN@Hcu=!MDdtydHSA)#n7jyQkzEZVnQr?AErt`gbZ+*Ni z{%Wf2{ zy6#qJ|Nk@V@_t?T{mMJueE#bDumAk9-G1CpcD3_&*=v99h1@Rnw<&$Ivg*ao_E*XC ztNx0;jW)k}zV6H3TU%n^pRHGLU|?u4{Kw41z|bS8z`$@of^lRre=<+I{VmPaM>(H? zfkCM%B%;JQKQ}iuuY`f2;#RSqm#>beZ=l9`os$6t-@ouOFqqF}ebiq2?FRz`1B0il KpUXO@geCxAbSlOG literal 0 HcmV?d00001 diff --git a/demo/doc/img/setup.png b/demo/doc/img/setup.png new file mode 100644 index 0000000000000000000000000000000000000000..2372223ae046363f889c9110c0b4161f9079701d GIT binary patch literal 82437 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 Mp00i_>zopr0BWO1H~;_u literal 0 HcmV?d00001 diff --git a/demo/doc/img/x-ray.png b/demo/doc/img/x-ray.png new file mode 100644 index 0000000000000000000000000000000000000000..1311abff7fd26e70bc3909c4564dc89dc7741842 GIT binary patch literal 99749 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

`SGxL6zJSElgL&)lg;-w8?VXq`(UXh&BPD`q9 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 rWy! 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ศw0N`g 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 z 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 SKNuJo7(8A5T-G@yGywoI3f9U1 literal 0 HcmV?d00001 diff --git a/demo/include/launchpad/launchpad.h b/demo/include/launchpad/launchpad.h new file mode 100644 index 000000000..0982fe6b0 --- /dev/null +++ b/demo/include/launchpad/launchpad.h @@ -0,0 +1,231 @@ +/* + * \brief Launchpad interface + * \author Norman Feske + * \date 2006-09-01 + * + * This class is a convenience-wrapper for starting and killing + * child processes. To support the Launchpad application, is + * also provides an interface to glues the child handling and + * the GUI together. + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__LAUNCHPAD__LAUNCHPAD_H_ +#define _INCLUDE__LAUNCHPAD__LAUNCHPAD_H_ + +#include +#include +#include +#include +#include +#include + +#include + +class Launchpad_child_policy : public Init::Traditional_child_policy +{ + public: + + Launchpad_child_policy(const char *name, + Genode::Server *server, + Genode::Service_registry *parent_services, + Genode::Service_registry *child_services, + Genode::Dataspace_capability config_ds, + Genode::Dataspace_capability binary_ds, + Genode::Rpc_entrypoint *parent_entrypoint) + : + Init::Traditional_child_policy(name, server, parent_services, + child_services, config_ds, + binary_ds, 0, 0, parent_entrypoint) + { } + + Genode::Service *resolve_session_request(const char *service_name, + const char *args) + { + Genode::Service *service; + + /* check for config file request */ + if ((service = _config_policy.resolve_session_request(service_name, args))) + return service; + + /* check for binary file request */ + if ((service = _binary_policy.resolve_session_request(service_name, args))) + return service; + + /* if service is provided by one of our children, use it */ + if ((service = _child_services->find(service_name))) + return service; + + /* + * Handle special case of the demo scenario when the user uses + * a nested Launchad for starting another Nitpicker instance + * before starting Liquid_fb. In this case, we do not want to + * delegate Nitpicker's session requests to the parent. The + * parent may be a launchpad again trying to apply the same + * policy. This instance will recognise that the session is not + * available at init (because the actual input and framebuffer + * drivers are singletons, and would therefore block for + * Framebuffer or Input to become available at one of its own + * children. The launchpad with which the user originally + * interacted, however, would block at the parent interface + * until this condition gets satisfied. + */ + if (Genode::strcmp(service_name, "Input") != 0 + && Genode::strcmp(service_name, "Framebuffer") != 0 + && (service = _parent_services->find(service_name))) + return service; + + /* wait for the service to become available */ + Genode::Client client; + return _child_services->wait_for_service(service_name, + &client, name()); + } +}; + + +class Launchpad; + + +class Launchpad_child : public Genode::List::Element +{ + private: + + Launchpad *_launchpad; + + Genode::Rom_session_capability _rom; + Genode::Ram_session_capability _ram; + Genode::Cpu_session_capability _cpu; + Genode::Rm_session_capability _rm; + Genode::Server _server; + + /* + * Entry point used for serving the parent interface and the + * locally provided ROM sessions for the 'config' and 'binary' + * files. + */ + enum { ENTRYPOINT_STACK_SIZE = 12*1024 }; + Genode::Rpc_entrypoint _entrypoint; + + Launchpad_child_policy _policy; + Genode::Child _child; + + public: + + Launchpad_child(const char *name, + Genode::Dataspace_capability elf_ds, + Genode::Ram_session_capability ram, + Genode::Cpu_session_capability cpu, + Genode::Rm_session_capability rm, + Genode::Rom_session_capability rom, + Genode::Cap_session *cap_session, + Genode::Service_registry *parent_services, + Genode::Service_registry *child_services, + Genode::Dataspace_capability config_ds, + Launchpad *launchpad) + : + _launchpad(launchpad), + _rom(rom), _ram(ram), _cpu(cpu), _rm(rm), _server(_ram), + _entrypoint(cap_session, ENTRYPOINT_STACK_SIZE, name, false), + _policy(name, &_server, parent_services, child_services, + config_ds, elf_ds, &_entrypoint), + _child(elf_ds, ram, cpu, rm, &_entrypoint, &_policy) { + _entrypoint.activate(); } + + Genode::Rom_session_capability rom_session_cap() { return _rom; } + Genode::Ram_session_capability ram_session_cap() { return _ram; } + Genode::Cpu_session_capability cpu_session_cap() { return _cpu; } + Genode::Rm_session_capability rm_session_cap() { return _rm; } + + const char *name() const { return _policy.name(); } + + const Genode::Server *server() const { return &_server; } + + Genode::Allocator *heap() { return _child.heap(); } + + void revoke_server(const Genode::Server *server) { + _child.revoke_server(server); } +}; + + +class Launchpad +{ + private: + + unsigned long _initial_quota; + + Genode::Service_registry _parent_services; + Genode::Service_registry _child_services; + + Genode::Lock _children_lock; + Genode::List _children; + + bool _child_name_exists(const char *name); + void _get_unique_child_name(const char *filename, char *dst, int dst_len); + + Genode::Sliced_heap _sliced_heap; + + /* cap session for allocating capabilities for parent interfaces */ + Genode::Cap_connection _cap_session; + + protected: + + int _ypos; + + public: + + Genode::Lock gui_lock; + + Launchpad(unsigned long initial_quota); + + unsigned long initial_quota() { return _initial_quota; } + + virtual ~Launchpad() { } + + + /************************* + ** Configuring the GUI ** + *************************/ + + virtual void quota(unsigned long quota) { } + + virtual void add_launcher(const char *filename, + unsigned long default_quota, + Genode::Dataspace_capability config_ds) { } + + virtual void add_child(const char *unique_name, + unsigned long quota, + Launchpad_child *launchpad_child, + Genode::Allocator *alloc) { } + + virtual void remove_child(const char *name, + Genode::Allocator *alloc) { } + +// virtual void child_quota(const char *name, unsigned long quota) { } + + Launchpad_child *start_child(const char *prg_name, unsigned long quota, + Genode::Dataspace_capability config_ds); + + /** + * Exit child and close all its sessions + * + * \param timer Timer session to use for watchdog + * mechanism. When using the default + * value, a new timer session gets + * created during the 'exit_child' + * call. + * \param session_close_timeout_ms Timeout in milliseconds until a an + * unresponsive service->close call + * gets canceled. + */ + void exit_child(Launchpad_child *child, + Timer::Session *timer = 0, + int session_close_timeout_ms = 2000); +}; + +#endif /* _INCLUDE__LAUNCHPAD__LAUNCHPAD_H_ */ diff --git a/demo/include/libpng_static/png.h b/demo/include/libpng_static/png.h new file mode 100644 index 000000000..fd7ea88f0 --- /dev/null +++ b/demo/include/libpng_static/png.h @@ -0,0 +1,3481 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.2.12 - June 27, 2006 + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.12 - June 27, 2006: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-8 13 10210 12.so.0.10[.0] + * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.0.19rc1-5 10 10019 10.so.0.19[.0] + * 1.2.11rc1-5 13 10211 12.so.0.11[.0] + * 1.0.19 10 10019 10.so.0.19[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.0.20 10 10020 10.so.0.20[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ + png_error_ptr warning_fn; /* function for printing warnings */ + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + z_stream zstream; /* pointer to decompression structure (below) */ + png_bytep zbuf; /* buffer for zlib */ + png_size_t zbuf_size; /* size of zbuf */ + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 rowbytes; /* size of row in bytes */ + png_uint_32 irowbytes; /* size of current interlaced row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf; /* buffer to save current (unfiltered) row */ + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ + png_row_info row_info; /* used for transformation routines */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + png_uint_16 num_trans; /* number of transparency values */ + png_byte chunk_name[5]; /* null-terminated name of current chunk */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ + png_byte usr_channels; /* channels at start of write */ + png_byte sig_bytes; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler; /* filler byte for pixel expansion */ +#else + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif +#endif + +#if defined(PNG_bKGD_SUPPORTED) + png_byte background_gamma_type; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma; +# endif + png_color_16 background; /* background color in screen gamma space */ +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_flush_ptr output_flush_fn;/* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma; /* file gamma value */ + float screen_gamma; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans; /* transparency values for paletted files */ + png_color_16 trans_values; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +# if defined(PNG_TEXT_SUPPORTED) + png_size_t current_text_size; /* current size of text input data */ + png_size_t current_text_left; /* how much text left to read in input */ + png_charp current_text; /* current text chunk buffer */ + png_charp current_text_ptr; /* current location in current_text */ +# endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */ + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + png_bytep palette_lookup; /* lookup table for dithering */ + png_bytep dither_index; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist; /* histogram */ +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_charp time_buffer; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) + png_voidp user_chunk_ptr; + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + int num_chunk_list; + png_bytep chunk_list; +#endif + +/* New members added in libpng-1.0.3 */ +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_byte rgb_to_gray_status; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + png_uint_16 rgb_to_gray_blue_coeff; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted; +#else + png_uint_32 mng_features_permitted; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_byte filter_type; +#endif + +#if defined(PNG_1_0_X) || (defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD)) +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size; +#endif + +/* New members added in libpng-1.2.0 */ +#if !defined(PNG_1_0_X) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) + png_byte mmx_bitdepth_threshold; + png_uint_32 mmx_rowbytes_threshold; + png_uint_32 asm_flags; +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; +#endif + +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_12; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)); + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +#endif + +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_WRITE_tIME_SUPPORTED */ +#endif /* _WIN32_WCE */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +#endif +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated */ +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)); +#endif +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)); + +/* set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#if !defined(PNG_NO_STDIO) +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)); + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)); +#endif + +/* frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#if defined(PNG_1_0_X) +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)); +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)); + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)); + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* Returns row_pointers, which is an array of pointers to scanlines that was +returned from png_read_png(). */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use +by png_write_png(). */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#if defined(PNG_TEXT_SUPPORTED) +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behavour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + If you need to turn it off for a chunk that your application has freed, + you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#define png_debug(l,m) _RPT0(_CRT_WARN,m) +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ +#if (PNG_DEBUG > 1) +#define png_debug(l,m) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ +} +#define png_debug1(l,m,p1) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ +} +#define png_debug2(l,m,p1,p2) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ +} +#endif /* (PNG_DEBUG > 1) */ +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +#if 0 +extern PNG_EXPORT(png_bytep,png_sig_bytes) PNGARG((void)); +#endif + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 + +#if !defined(PNG_1_0_X) +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +#if !defined(PNG_1_0_X) +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +#else +extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +extern PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + */ +extern PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +extern PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +extern PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + +#if defined(PNG_INTERNAL) + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + ideal-delta..ideal+delta. Each argument is evaluated twice. + "ideal" and "delta" should be constants, normally simple + integers, "value" a variable. Added to libpng-1.2.6 JB */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (const png_byte FARDATA) png_sig[8]; +#else +#if 0 +#define png_sig png_sig_bytes(NULL) +#endif +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR const png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT const png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND const png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE const png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD const png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM const png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA const png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST const png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP const png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt const png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs const png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL const png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL const png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs const png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT const png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT const png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB const png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt const png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME const png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS const png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt const png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (const png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + +/* simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); + +/* write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point + file_gamma)); +#endif +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); +#endif + +/* combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +/* expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* these are the functions that do the transformations */ +#if defined(PNG_READ_FILLER_SUPPORTED) +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); + +# if defined(PNG_CORRECT_PALETTE_SUPPORTED) +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)); +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#if defined(PNG_READ_bKGD_SUPPORTED) +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_gAMA_SUPPORTED) +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_iCCP_SUPPORTED) +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sPLT_SUPPORTED) +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_sRGB_SUPPORTED) +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tRNS_SUPPORTED) +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_pHYs_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* do not put anything past this line */ +#endif /* PNG_H */ diff --git a/demo/include/libpng_static/pngconf.h b/demo/include/libpng_static/pngconf.h new file mode 100644 index 000000000..389470ec6 --- /dev/null +++ b/demo/include/libpng_static/pngconf.h @@ -0,0 +1,1472 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.2.12 - June 27, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2005 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#define PNG_1_2_X + +/* + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#include "pngusr.h" +#endif + +/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ +#ifdef PNG_CONFIGURE_LIBPNG +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#endif + +/* + * Added at libpng-1.2.8 + * + * If you create a private DLL you need to define in "pngusr.h" the followings: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#if defined(__CYGWIN__) +# if defined(ALL_STATIC) +# if defined(PNG_BUILD_DLL) +# undef PNG_BUILD_DLL +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# if !defined(PNG_STATIC) +# define PNG_STATIC +# endif +# else +# if defined (PNG_BUILD_DLL) +# if defined(PNG_STATIC) +# undef PNG_STATIC +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# else +# if defined(PNG_STATIC) +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# else +# if !defined(PNG_USE_DLL) +# define PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if defined(_WIN32_WCE) +# include + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# if !defined(_WIN32_WCE) +/* "stdio.h" functions are not supported on WindowsCE */ +# include +# endif +# endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + */ + +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __png.h__ already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ + + /* include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# define _BSD_SOURCE +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) +# if defined(MACOS) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * was turned on by default in libpng-1.3.0. + */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +# ifndef PNG_NO_iTXt_SUPPORTED +# define PNG_NO_iTXt_SUPPORTED +# endif +# ifndef PNG_NO_READ_iTXt +# define PNG_NO_READ_iTXt +# endif +# ifndef PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_iTXt +# endif +#endif + +#if !defined(PNG_NO_iTXt_SUPPORTED) +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#if defined(PNG_READ_SUPPORTED) + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following line: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_WRITE_SUPPORTED) + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + even when PNG_USE_PNGVCRD or PNG_USE_PNGGCCRD is not defined */ +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) && \ + defined(__MMX__) +# define PNG_MMX_CODE_SUPPORTED +# endif +# if !defined(PNG_USE_PNGGCCRD) && !defined(PNG_NO_MMX_CODE) && \ + !defined(PNG_USE_PNGVCRD) && defined(__MMX__) +# define PNG_USE_PNGGCCRD +# endif +#endif + +/* If you are sure that you don't need thread safety and you are compiling + with PNG_USE_PNGCCRD for an MMX application, you can define this for + faster execution. See pnggccrd.c. +#define PNG_THREAD_UNSAFE_OK +*/ + +#if !defined(PNG_1_0_X) +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#if !defined(PNG_1_0_X) +#ifndef PNG_SET_USER_LIMITS_SUPPORTED +#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) +# define PNG_SET_USER_LIMITS_SUPPORTED +#endif +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* need the time information for reading tIME chunks */ +#if defined(PNG_tIME_SUPPORTED) +# if !defined(_WIN32_WCE) + /* "time.h" functions are not supported on WindowsCE */ +# include +# endif +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof (x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof (x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#if defined(FAR) +# if defined(M_I86MM) +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#if defined(_WIN32_WCE) +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.3.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#if defined(__CYGWIN__) +# if !defined(PNG_STATIC) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || (defined(__GNUC__) && defined(PNG_DLL)) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#if defined(__CYGWIN__) +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# if !defined(PNG_IMPEXP) + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# if !defined(PNG_IMPEXP) +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# define PNG_ABORT() abort() +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ +/* use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_strcpy _fstrcpy +# define png_strncpy _fstrncpy /* Added to v 1.2.6 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# define png_strcpy strcpy +# define png_strncpy strncpy /* Added to v 1.2.6 */ +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +#ifdef PNG_READ_SUPPORTED +/* Prior to libpng-1.0.9, this block was in pngasmrd.h */ +#if defined(PNG_INTERNAL) + +/* These are the default thresholds before the MMX code kicks in; if either + * rowbytes or bitdepth is below the threshold, plain C code is used. These + * can be overridden at runtime via the png_set_mmx_thresholds() call in + * libpng 1.2.0 and later. The values below were chosen by Intel. + */ + +#ifndef PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT +# define PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT 128 /* >= */ +#endif +#ifndef PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT +# define PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT 9 /* >= */ +#endif + +/* Set this in the makefile for VC++ on Pentium, not here. */ +/* Platform must be Pentium. Makefile must assemble and load pngvcrd.c . + * MMX will be detected at run time and used if present. + */ +#ifdef PNG_USE_PNGVCRD +# define PNG_HAVE_ASSEMBLER_COMBINE_ROW +# define PNG_HAVE_ASSEMBLER_READ_INTERLACE +# define PNG_HAVE_ASSEMBLER_READ_FILTER_ROW +#endif + +/* Set this in the makefile for gcc/as on Pentium, not here. */ +/* Platform must be Pentium. Makefile must assemble and load pnggccrd.c . + * MMX will be detected at run time and used if present. + */ +#ifdef PNG_USE_PNGGCCRD +# define PNG_HAVE_ASSEMBLER_COMBINE_ROW +# define PNG_HAVE_ASSEMBLER_READ_INTERLACE +# define PNG_HAVE_ASSEMBLER_READ_FILTER_ROW +#endif +/* - see pnggccrd.c for info about what is currently enabled */ + +#endif /* PNG_INTERNAL */ +#endif /* PNG_READ_SUPPORTED */ + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/demo/include/libpng_static/pngusr.h b/demo/include/libpng_static/pngusr.h new file mode 100644 index 000000000..21cecd6e0 --- /dev/null +++ b/demo/include/libpng_static/pngusr.h @@ -0,0 +1,19 @@ +/** + * Private libpng configuration + */ + +#ifndef _INCLUDE__LIBPNG__PNGUSR_H_ +#define _INCLUDE__LIBPNG__PNGUSR_H_ + +//#define PNG_NO_READ_SUPPORTED +//#define PNG_NO_WRITE_SUPPORTED +#define PNG_NO_READ_tIME +#define PNG_NO_WRITE_tIME +#define PNG_NO_MNG_FEATURES +#define PNG_NO_STDIO +#define PNG_NO_SETJMP_SUPPORTED +#define PNG_NO_FLOATING_POINT_SUPPORTED +#define PNG_NO_READ_tIME +#define PNG_NO_ASSEMBLER_CODE + +#endif /* _INCLUDE__LIBPNG__PNGUSR_H_ */ diff --git a/demo/include/libz_static/zconf.h b/demo/include/libz_static/zconf.h new file mode 100644 index 000000000..03a9431c8 --- /dev/null +++ b/demo/include/libz_static/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/demo/include/libz_static/zlib.h b/demo/include/libz_static/zlib.h new file mode 100644 index 000000000..022817927 --- /dev/null +++ b/demo/include/libz_static/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/demo/include/mini_c/errno.h b/demo/include/mini_c/errno.h new file mode 100644 index 000000000..140a32478 --- /dev/null +++ b/demo/include/mini_c/errno.h @@ -0,0 +1,21 @@ +/* + * \brief Mini C errno + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__MINI_C__ERRNO_H_ +#define _INCLUDE__MINI_C__ERRNO_H_ + +static int errno __attribute__ ((used)) = 0; + +enum { EINTR = 4 }; + +#endif /* _INCLUDE__MINI_C__ERRNO_H_ */ diff --git a/demo/include/mini_c/limits.h b/demo/include/mini_c/limits.h new file mode 100644 index 000000000..747096f1b --- /dev/null +++ b/demo/include/mini_c/limits.h @@ -0,0 +1,14 @@ +/* + * \brief Mini C standard compatibility + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + + diff --git a/demo/include/mini_c/stdio.h b/demo/include/mini_c/stdio.h new file mode 100644 index 000000000..68e0b4cb7 --- /dev/null +++ b/demo/include/mini_c/stdio.h @@ -0,0 +1,41 @@ +/* + * \brief Mini C standard I/O + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__MINI_C__STDIO_H_ +#define _INCLUDE__MINI_C__STDIO_H_ + +#include +#include + +#define FILE int + +#define EOF (-1) + +int printf(const char *format, ...); +int sprintf(char *str, const char *format, ...); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); + +FILE *fopen(const char *path, const char *mode); +FILE *fdopen(int fildes, const char *mode); +int fclose(FILE *fp); +int fprintf(FILE *stream, const char *format, ...); +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +unsigned fread(void *ptr, unsigned size, unsigned nmemb, FILE *stream); +int fputc(int c, FILE *stream); +int fflush(FILE *stream); +int fseek(FILE *stream, long offset, int whence); +long ftell(FILE *stream); +void clearerr(FILE *stream); +int ferror(FILE *stream); + +#endif /* _INCLUDE__MINI_C__STDIO_H_ */ diff --git a/demo/include/mini_c/stdlib.h b/demo/include/mini_c/stdlib.h new file mode 100644 index 000000000..cabca3b0d --- /dev/null +++ b/demo/include/mini_c/stdlib.h @@ -0,0 +1,30 @@ +/* + * \brief Mini C standard library + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__MINI_C__STDLIB_H_ +#define _INCLUDE__MINI_C__STDLIB_H_ + +#include + +int abs(int j); + +void *malloc(size_t size); +void *calloc(size_t nmemb, size_t size); +void free(void *ptr); + +void abort(void); +long int strtol(const char *nptr, char **endptr, int base); +long atol(const char *nptr); +double strtod(const char *nptr, char **endptr); + +#endif /* _INCLUDE__MINI_C__STDLIB_H_ */ diff --git a/demo/include/mini_c/string.h b/demo/include/mini_c/string.h new file mode 100644 index 000000000..a8fc860ce --- /dev/null +++ b/demo/include/mini_c/string.h @@ -0,0 +1,31 @@ +/* + * \brief Mini C string functions + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__MINI_C__STRING_H_ +#define _INCLUDE__MINI_C__STRING_H_ + +#include + +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +size_t strlen(const char *s); +int strcmp(const char *s1, const char *s2); +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, size_t n); +char *strcat(char *dest, const char *src); + +char *strerror(int errnum); + +#endif /* _INCLUDE__MINI_C__STRING_H_ */ diff --git a/demo/include/mini_c/sys/types.h b/demo/include/mini_c/sys/types.h new file mode 100644 index 000000000..f29b1f4f4 --- /dev/null +++ b/demo/include/mini_c/sys/types.h @@ -0,0 +1,14 @@ +/* + * \brief Mini C standard compatibility + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include diff --git a/demo/lib/import/import-libpng_static.mk b/demo/lib/import/import-libpng_static.mk new file mode 100644 index 000000000..eba70123a --- /dev/null +++ b/demo/lib/import/import-libpng_static.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/libpng_static diff --git a/demo/lib/import/import-libz_static.mk b/demo/lib/import/import-libz_static.mk new file mode 100644 index 000000000..508a45138 --- /dev/null +++ b/demo/lib/import/import-libz_static.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/libz_static diff --git a/demo/lib/import/import-mini_c.mk b/demo/lib/import/import-mini_c.mk new file mode 100644 index 000000000..d6ca504b5 --- /dev/null +++ b/demo/lib/import/import-mini_c.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/mini_c diff --git a/demo/lib/mk/launchpad.mk b/demo/lib/mk/launchpad.mk new file mode 100644 index 000000000..86e54a9ec --- /dev/null +++ b/demo/lib/mk/launchpad.mk @@ -0,0 +1,4 @@ +LIBS = process +SRC_CC = launchpad.cc + +vpath launchpad.cc $(REP_DIR)/src/lib/launchpad diff --git a/demo/lib/mk/libpng_static.mk b/demo/lib/mk/libpng_static.mk new file mode 100644 index 000000000..6b7b4c4aa --- /dev/null +++ b/demo/lib/mk/libpng_static.mk @@ -0,0 +1,12 @@ +SRC_C = png.c pngset.c pngget.c pngrutil.c pngtrans.c pngwutil.c pngread.c \ + pngrio.c pngwio.c pngwrite.c pngrtran.c pngwtran.c pngmem.c \ + pngerror.c pngpread.c + +CC_OPT += -nostdinc -funroll-loops -DPNG_USER_CONFIG +LIBS = mini_c libz_static + +CC_WARN = -Wall -Wno-address + +vpath % $(REP_DIR)/src/lib/libpng/contrib + +include $(REP_DIR)/lib/import/import-libpng_static.mk diff --git a/demo/lib/mk/libz_static.mk b/demo/lib/mk/libz_static.mk new file mode 100644 index 000000000..7edc53aba --- /dev/null +++ b/demo/lib/mk/libz_static.mk @@ -0,0 +1,9 @@ +SRC_C = adler32.c compress.c crc32.c gzio.c uncompr.c deflate.c trees.c \ + zutil.c inflate.c infback.c inftrees.c inffast.c + +CC_OPT += -nostdinc +LIBS = mini_c + +vpath % $(REP_DIR)/src/lib/libz/contrib + +include $(REP_DIR)/lib/import/import-libz_static.mk diff --git a/demo/lib/mk/mini_c.mk b/demo/lib/mk/mini_c.mk new file mode 100644 index 000000000..bd3f0a579 --- /dev/null +++ b/demo/lib/mk/mini_c.mk @@ -0,0 +1,10 @@ +SRC_C = mini_c.c +SRC_CC = snprintf.cc vsnprintf.cc atol.cc strtol.cc strtod.cc \ + malloc_free.cc memcmp.cc strlen.cc memset.cc abort.cc \ + printf.cc + +STDINC = yes + +vpath % $(REP_DIR)/src/lib/mini_c + +include $(REP_DIR)/lib/import/import-mini_c.mk diff --git a/demo/lib/mk/scout_widgets.mk b/demo/lib/mk/scout_widgets.mk new file mode 100644 index 000000000..cd54fd6fe --- /dev/null +++ b/demo/lib/mk/scout_widgets.mk @@ -0,0 +1,42 @@ +LIBS = cxx env ipc server blit + +SRC_CC = sky_texture.cc startup.cc \ + elements.cc widgets.cc \ + tick.cc scrollbar.cc \ + refracted_icon.cc + +SRC_CC += platform_genode.cc + +SCOUT_DIR = $(REP_DIR)/src/app/scout + +INC_DIR += $(SCOUT_DIR)/include \ + $(SCOUT_DIR)/include/genode + +vpath % $(SCOUT_DIR)/data +vpath %.cc $(SCOUT_DIR)/common +vpath startup.cc $(SCOUT_DIR)/genode +vpath launcher.cc $(SCOUT_DIR)/genode +vpath platform_genode.cc $(SCOUT_DIR)/genode + + +SRC_TFF = vera16.tff \ + verai16.tff \ + vera18.tff \ + vera20.tff \ + vera24.tff \ + verabi10.tff \ + mono16.tff + +SRC_RGBA = uparrow.rgba \ + downarrow.rgba \ + slider.rgba \ + sizer.rgba \ + titlebar.rgba \ + loadbar.rgba \ + redbar.rgba \ + whitebar.rgba \ + kill_icon.rgba \ + opened_icon.rgba \ + closed_icon.rgba + +SRC_BIN = $(SRC_TFF) $(SRC_MAP) $(SRC_RGBA) diff --git a/demo/src/app/backdrop/README b/demo/src/app/backdrop/README new file mode 100644 index 000000000..3c1d2ab61 --- /dev/null +++ b/demo/src/app/backdrop/README @@ -0,0 +1,19 @@ +This directory contains a simple backdrop program for Nitpicker. + + +Usage +----- + +You have to specify the name of the PNG file to be used as background +image via a declaration in your config file: + +! +! background.png +! + + +Limitations +----------- + +The PNG file is expected to be equal to the screen size. No scaling +or tiling is supported. diff --git a/demo/src/app/backdrop/main.cc b/demo/src/app/backdrop/main.cc new file mode 100644 index 000000000..f981215ff --- /dev/null +++ b/demo/src/app/backdrop/main.cc @@ -0,0 +1,264 @@ +/* + * \brief Backdrop for Nitpicker + * \author Norman Feske + * \date 2009-08-28 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* libpng includes */ +#include + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + + +/*************** + ** Dithering ** + ***************/ + +enum { DITHER_SIZE = 16, DITHER_MASK = DITHER_SIZE - 1 }; + +static const int dither_matrix[DITHER_SIZE][DITHER_SIZE] = { + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } +}; + + +static inline uint16_t rgb565(int r, int g, int b) +{ + enum { + R_MASK = 0xf800, R_LSHIFT = 8, + G_MASK = 0x07e0, G_LSHIFT = 3, + B_MASK = 0x001f, B_RSHIFT = 3 + }; + return ((r << R_LSHIFT) & R_MASK) + | ((g << G_LSHIFT) & G_MASK) + | ((b >> B_RSHIFT) & B_MASK); +} + + +static void convert_line_rgba_to_rgb565(const unsigned char *rgba_src, + uint16_t *dst, int num_pixels, int line) +{ + enum { CHANNEL_MAX = 255 }; + + int const *dm = dither_matrix[line & DITHER_MASK]; + + for (int i = 0; i < num_pixels; i++) { + int v = dm[i & DITHER_MASK] >> 5; + + *dst++ = rgb565(min(v + (int)rgba_src[0], (int)CHANNEL_MAX), + min(v + (int)rgba_src[1], (int)CHANNEL_MAX), + min(v + (int)rgba_src[2], (int)CHANNEL_MAX)); + + /* we ignore the alpha channel */ + + rgba_src += 4; /* next pixel */ + } +} + + +/************************ + ** PNG image decoding ** + ************************/ + +class Png_stream +{ + private: + + char *_addr; + + public: + + /** + * Constructor + */ + Png_stream(char *addr) { _addr = addr; } + + /** + * Read from png stream + */ + void read(char *dst, int len) + { + Genode::memcpy(dst, _addr, len); + _addr += len; + } +}; + + +/** + * PNG read callback + */ +static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t len) +{ + Png_stream *stream = (Png_stream *)png_get_io_ptr(png_ptr); + + stream->read((char *)data, len); +} + + + +static void convert_png_to_rgb565(void *png_data, + uint16_t *dst, int dst_w, int dst_h) +{ + Png_stream *stream = new (env()->heap()) Png_stream((char *)png_data); + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) return; + + png_set_read_fn(png_ptr, stream, user_read_data); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); + return; + } + + png_read_info(png_ptr, info_ptr); + + /* get image data chunk */ + int bit_depth, color_type, interlace_type; + png_uint_32 img_w, img_h; + png_get_IHDR(png_ptr, info_ptr, &img_w, &img_h, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + printf("png is %d x %d, depth=%d\n", (int)img_w, (int)img_h, bit_depth); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_gray_1_2_4_to_8(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + if (bit_depth < 8) png_set_packing(png_ptr); + if (bit_depth == 16) png_set_strip_16(png_ptr); + + /* allocate buffer for decoding a row */ + static png_byte *row_ptr; + static int curr_row_size; + + int needed_row_size = png_get_rowbytes(png_ptr, info_ptr)*8; + + if (curr_row_size < needed_row_size) { + if (row_ptr) env()->heap()->free(row_ptr, curr_row_size); + row_ptr = (png_byte *)env()->heap()->alloc(needed_row_size); + curr_row_size = needed_row_size; + } + + /* fill texture */ + int dst_y = 0; + for (int j = 0; j < min((int)img_h, dst_h); j++, dst_y++) { + png_read_row(png_ptr, row_ptr, NULL); + convert_line_rgba_to_rgb565((unsigned char *)row_ptr, dst + dst_y*dst_w, + min(dst_w, (int)img_w), j); + } +} + + +/**************************** + ** Configuration handling ** + ****************************/ + +/** + * Determine PNG filename of image to be used as background + * + * \param dst destination buffer for storing the filename + * \param dst_len size of destination buffer + * \return 0 on success + */ +static int read_image_filename_from_config(char *dst, Genode::size_t dst_len) +{ + try { + Xml_node image_xml = config()->xml_node().sub_node("image"); + image_xml.value(dst, dst_len); + return 0; + } catch (Xml_node::Nonexistent_sub_node) { + printf("Error: Configuration has no 'image' declaration.\n"); + return -2; + } +} + + +/****************** + ** Main program ** + ******************/ + +int main(int argc, char **argv) +{ + enum { PNG_NAME_MAX = 128 }; + static char png_name[PNG_NAME_MAX]; + + if (read_image_filename_from_config(png_name, sizeof(png_name)) < 0) + return -1; + + printf("using PNG file \"%s\" as background\n", png_name); + + static void *png_data; + try { + static Rom_connection png_rom(png_name); + png_data = env()->rm_session()->attach(png_rom.dataspace()); + } catch (...) { + printf("Error: Could not obtain PNG image from ROM service\n"); + return -2; + } + + static Nitpicker::Connection nitpicker; + static Framebuffer::Session_client framebuffer(nitpicker.framebuffer_session()); + static int fb_width, fb_height; + static Framebuffer::Session::Mode fb_mode; + Nitpicker::View_capability view_cap = nitpicker.create_view(); + static Nitpicker::View_client view(view_cap); + + /* obtain screen size */ + framebuffer.info(&fb_width, &fb_height, &fb_mode); + + if (fb_mode != Framebuffer::Session::RGB565) { + printf("Error: Color mode %d not supported\n", (int)fb_mode); + return -3; + } + + /* make virtual framebuffer locally accessible */ + uint16_t *fb = env()->rm_session()->attach(framebuffer.dataspace()); + + /* fill virtual framebuffer with decoded image data */ + convert_png_to_rgb565(png_data, fb, fb_width, fb_height); + + /* display view behind all others */ + nitpicker.background(view_cap); + view.viewport(0, 0, fb_width, fb_height, 0, 0, false); + view.stack(Nitpicker::View_capability(), false, false); + framebuffer.refresh(0, 0, fb_width, fb_height); + + sleep_forever(); + return 0; +} diff --git a/demo/src/app/backdrop/target.mk b/demo/src/app/backdrop/target.mk new file mode 100644 index 000000000..1d7e3a8cd --- /dev/null +++ b/demo/src/app/backdrop/target.mk @@ -0,0 +1,4 @@ +TARGET = backdrop +SRC_CC = main.cc +LIBS = cxx env libpng_static libz_static mini_c +CC_OPT += -DPNG_USER_CONFIG diff --git a/demo/src/app/launchpad/README b/demo/src/app/launchpad/README new file mode 100644 index 000000000..8fb6b113c --- /dev/null +++ b/demo/src/app/launchpad/README @@ -0,0 +1,34 @@ +Launchpad is a graphical application for interactively starting and +killing programs. + +By default, launchpad displays a preconfigured list of programs and their +respective default memory quotas. The user can tweak the memory quota +for each entry with mouse and then start a program by clicking on its +name. As an alternative to using the default list, you can define the list +manually by supplying a configuration to Launchpad. The following example +configuration tells launchpad to display a list of two launcher entries: + +! +! +! sdl_pathfind +! 10M +! +! +! liquid_fb +! 10M +! +! +! init +! 10M +! +! +! hello +! 1M +! +! +! +! + +To use this configuration for a Launchpad started via init, you can +simply insert the launchpad configuration into the '' node +of the launchpad entry in init's 'config' file. diff --git a/demo/src/app/launchpad/child_entry.h b/demo/src/app/launchpad/child_entry.h new file mode 100644 index 000000000..f86898692 --- /dev/null +++ b/demo/src/app/launchpad/child_entry.h @@ -0,0 +1,146 @@ +/* + * \brief Child entry widget + * \author Norman Feske + * \date 2006-09-13 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CHILD_ENTRY_H_ +#define _CHILD_ENTRY_H_ + +#include +#include + +#include +#include "loadbar.h" + +#define KILL_ICON_RGBA _binary_kill_icon_rgba_start +#define OPENED_ICON_RGBA _binary_opened_icon_rgba_start +#define CLOSED_ICON_RGBA _binary_closed_icon_rgba_start +extern unsigned char KILL_ICON_RGBA[]; +extern unsigned char OPENED_ICON_RGBA[]; +extern unsigned char CLOSED_ICON_RGBA[]; + + +class Kill_event_handler : public Event_handler +{ + private: + + Launchpad *_launchpad; + Launchpad_child *_launchpad_child; + + public: + + Kill_event_handler(Launchpad *launchpad, Launchpad_child *launchpad_child): + _launchpad(launchpad), _launchpad_child(launchpad_child) { } + + /** + * Event handler interface + */ + void handle(Event &ev) + { + static int key_cnt; + + if (ev.type == Event::PRESS) key_cnt++; + if (ev.type == Event::RELEASE) key_cnt--; + + if (ev.type == Event::RELEASE && key_cnt == 0) + _launchpad->exit_child(_launchpad_child); + } +}; + + +template +class Child_entry : public Parent_element, public Genode::List >::Element +{ + private: + + enum { _IW = 16 }; /* icon width */ + enum { _IH = 16 }; /* icon height */ + enum { _PTW = 100 }; /* program text width */ + enum { _PADX = 10 }; /* horizontal padding */ + enum { _NAME_LEN = 64 }; /* max length of child name */ + + Block _block; + Kbyte_loadbar _loadbar; + + char _name[_NAME_LEN]; + + Fade_icon _kill_icon; + Fade_icon _fold_icon; + + Kill_event_handler _kill_event_handler; + + public: + + /** + * Constructor + */ + Child_entry(const char *name, int quota_kb, int max_quota_kb, + Launchpad *launchpad, Launchpad_child *launchpad_child) + : + _block(Block::RIGHT), _loadbar(0, &label_font), + _kill_event_handler(launchpad, launchpad_child) + { + Genode::strncpy(_name, name, sizeof(_name)); + _block.append_plaintext(_name, &plain_style); + + _loadbar.max_value(max_quota_kb); + _loadbar.value(quota_kb); + + _kill_icon.rgba(KILL_ICON_RGBA, 0, 0); + _kill_icon.alpha(100); + _kill_icon.focus_alpha(200); + _kill_icon.event_handler(&_kill_event_handler); + + _fold_icon.rgba(CLOSED_ICON_RGBA, 0, 0); + _fold_icon.alpha(100); + _fold_icon.focus_alpha(200); + + append(&_loadbar); + append(&_block); + append(&_kill_icon); + append(&_fold_icon); + + _min_w = _PTW + 100; + } + + + /** + * Accessors + */ + const char *name() { return _name; } + + + /****************************** + ** Parent element interface ** + ******************************/ + + void format_fixed_width(int w) + { + _block.format_fixed_width(_PTW); + int bh = _block.min_h(); + int iy = max(0, (bh - _loadbar.min_h())/2); + + _fold_icon.geometry(0, iy, _IW, _IH); + _kill_icon.geometry(w - _IW - 8, iy, _IW, _IH); + + _block.geometry(max(10, _PTW - _block.min_w()), + max(0, (bh - _block.min_h())/2), + min((int)_PTW, _block.min_w()), bh); + + int lw = w - 2*_PADX - _PTW - _IW; + _loadbar.format_fixed_width(lw); + _loadbar.geometry(_PADX + _PTW, iy, lw, 16); + _min_h = bh; + _min_w = w; + } +}; + +#endif diff --git a/demo/src/app/launchpad/launch_entry.h b/demo/src/app/launchpad/launch_entry.h new file mode 100644 index 000000000..7206139b7 --- /dev/null +++ b/demo/src/app/launchpad/launch_entry.h @@ -0,0 +1,90 @@ +/* + * \brief Launcher entry widget + * \author Norman Feske + * \date 2006-09-13 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LAUNCH_ENTRY_H_ +#define _LAUNCH_ENTRY_H_ + +#include "loadbar.h" +#include "launcher_config.h" + +template +class Launch_entry : public Parent_element, public Loadbar_listener +{ + private: + + Block _block; + Kbyte_loadbar _loadbar; + Launcher_config _config; + Launcher _launcher; + int _lh; /* launch entry height */ + + enum { _PTW = 100 }; /* program text width */ + enum { _PADX = 10 }; /* program text width */ + enum { _PADR = 16 }; /* right padding */ + + public: + + /** + * Constructor + */ + Launch_entry(const char *prg_name, int initial_quota, int max_quota, + Launchpad *launchpad, + Genode::Dataspace_capability config_ds) + : _block(Block::RIGHT), _loadbar(this, &label_font), _config(config_ds), + _launcher(prg_name, launchpad, 1024 * initial_quota, &_config) + { + _block.append_launchertext(prg_name, &link_style, &_launcher); + + _loadbar.max_value(max_quota); + _loadbar.value(initial_quota); + append(&_loadbar); + append(&_block); + _min_w = _PTW + 100; + } + + + /******************************** + ** Loadbar listener interface ** + ********************************/ + + void loadbar_changed(int mx) + { + int value = _loadbar.value_by_xpos(mx - _loadbar.abs_x()); + _loadbar.value(value); + _loadbar.refresh(); + _launcher.quota(1024 * (unsigned long)value); + } + + + /****************************** + ** Parent element interface ** + ******************************/ + + void format_fixed_width(int w) + { + _block.format_fixed_width(_PTW); + _lh = _block.min_h(); + _block.geometry(max(10, _PTW - _block.min_w()), + max(0, (_lh - _block.min_h())/2), + min((int)_PTW, _block.min_w()), _lh); + + int lw = max(0, w - 2*_PADX - _PTW - _PADR); + int ly = max(0, (_lh - _loadbar.min_h())/2); + _loadbar.format_fixed_width(lw); + _loadbar.geometry(_PADX + _PTW, ly, lw, 16); + _min_h = _lh; + _min_w = w; + } +}; + +#endif diff --git a/demo/src/app/launchpad/launcher.cc b/demo/src/app/launchpad/launcher.cc new file mode 100644 index 000000000..b5950dcab --- /dev/null +++ b/demo/src/app/launchpad/launcher.cc @@ -0,0 +1,28 @@ +/* + * \brief Support for launcher of the Genode programs via the Launchpad + * \author Norman Feske + * \date 2006-08-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include "elements.h" +#include "launcher_config.h" + + +/************************ + ** Launcher interface ** + ************************/ + +void Launcher::launch() +{ + _launchpad->start_child(prg_name(), quota(), + _config ? _config->config_ds() + : Genode::Dataspace_capability()); +} diff --git a/demo/src/app/launchpad/launchpad_window.cc b/demo/src/app/launchpad/launchpad_window.cc new file mode 100644 index 000000000..bbdb19f0f --- /dev/null +++ b/demo/src/app/launchpad/launchpad_window.cc @@ -0,0 +1,173 @@ +/* + * \brief Launchpad window implementation + * \date 2006-08-30 + * \author Norman Feske + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "miscmath.h" +#include "launchpad_window.h" +#include "styles.h" + +/**************************** + ** External graphics data ** + ****************************/ + +#define SIZER_RGBA _binary_sizer_rgba_start +#define TITLEBAR_RGBA _binary_titlebar_rgba_start + +extern unsigned char SIZER_RGBA[]; +extern unsigned char TITLEBAR_RGBA[]; + + +/******************************** + ** Launchpad window interface ** + ********************************/ + +template +Launchpad_window::Launchpad_window(Platform *pf, + Redraw_manager *redraw, + int max_w, int max_h, + unsigned long initial_quota) +: + Launchpad(initial_quota), + Window(pf, redraw, max_w, max_h), + _docview(0), + _spacer(1, _TH), + _info_section("Status", &subsection_font), + _launch_section("Launcher", &subsection_font), + _kiddy_section("Children", &subsection_font), + _status_entry("Quota") +{ + /* resize handle */ + _sizer.rgba(SIZER_RGBA); + _sizer.event_handler(new Sizer_event_handler(this)); + _sizer.alpha(100); + + /* titlebar */ + _titlebar.rgba(TITLEBAR_RGBA); + _titlebar.text("Launchpad"); + _titlebar.event_handler(new Mover_event_handler(this)); + + _min_w = 200; + _min_h = 200; + + _status_entry.max_value(initial_quota / 1024); + + /* adopt widgets as child elements */ + _info_section.append(&_status_entry); + _document.append(&_spacer); + _document.append(&_info_section); + _document.append(&_launch_section); + _document.append(&_kiddy_section); + + append(&_docview); + append(&_titlebar); + append(&_scrollbar); + append(&_sizer); + + _scrollbar.listener(this); + _docview.texture(&_texture); + _docview.content(&_document); +} + + +template +void Launchpad_window::ypos_sb(int ypos, int update_scrollbar) +{ + if (ypos < -_docview.h() + _h) + ypos = -_docview.h() + _h; + + _ypos = ypos <= 0 ? ypos : 0; + + _docview.geometry(_docview.x(), _ypos, _docview.w(), _docview.h()); + + if (update_scrollbar) + _scrollbar.view(_docview.h(), _h, -_ypos); + + refresh(); +} + + +/************************* + ** Launchpad interface ** + *************************/ + +template +void Launchpad_window::format(int w, int h) +{ + /* limit window size to valid values */ + w = (w < _min_w) ? _min_w : w; + h = (h < _min_h) ? _min_h : h; + w = (w > max_w()) ? max_w() : w; + h = (h > max_h()) ? max_h() : h; + + /* determine old scrollbar visibility */ + int old_sb_visibility = (_docview.min_h() > _h); + + /* assign new size to window */ + _w = w; + _h = h; + + /* format document */ + _docview.format_fixed_width(_w); + + /* format titlebar */ + _titlebar.format_fixed_width(_w); + + /* determine new scrollbar visibility */ + int new_sb_visibility = (_docview.min_h() > _h); + + /* reformat docview on change of scrollbar visibility */ + if (old_sb_visibility ^ new_sb_visibility) { + _docview.right_pad(new_sb_visibility ? _scrollbar.min_w() : 0); + _docview.format_fixed_width(_w); + } + + /* position docview */ + _docview.geometry(0, _ypos, _docview.min_w(), max(_docview.min_h(), _h)); + + /* start at top */ + int y = 0; + + /* position titlebar */ + _titlebar.geometry(y, 0, _w, _TH); + y += _TH; + + _scrollbar.geometry(w - _scrollbar.min_w() - _SB_XPAD, y + _SB_YPAD, + _scrollbar.min_w(), h - y - _SB_YPAD*2 - 8); + + + _sizer.geometry(_w - 32, _h - 32, 32, 32); + + pf()->view_geometry(pf()->vx(), pf()->vy(), _w, _h); + redraw()->size(_w, _h); + ypos(_ypos); + refresh(); +} + + +/********************************** + ** Scrollbar listener interface ** + **********************************/ + +template +void Launchpad_window::handle_scroll(int view_pos) +{ + /* + * The handle scroll notification comes from the scrollbar, + * which already adjusted itself to the new view port. + * Therefore, we do not need to re-adjust it another time + * and call ypos() with update_scrollbar set to zero. + */ + ypos_sb(-view_pos, 0); +} + +#include "canvas_rgb565.h" +template class Launchpad_window; diff --git a/demo/src/app/launchpad/launchpad_window.h b/demo/src/app/launchpad/launchpad_window.h new file mode 100644 index 000000000..8d84a9751 --- /dev/null +++ b/demo/src/app/launchpad/launchpad_window.h @@ -0,0 +1,171 @@ +/* + * \brief Launchpad window interface + * \date 2006-08-30 + * \author Norman Feske + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LAUNCHPAD_WINDOW_H_ +#define _LAUNCHPAD_WINDOW_H_ + +#include "elements.h" +#include "widgets.h" +#include "sky_texture.h" +#include "scrollbar.h" +#include "fade_icon.h" +#include "platform.h" +#include "window.h" +#include "titlebar.h" + +#include "launch_entry.h" +#include "status_entry.h" +#include "child_entry.h" +#include "section.h" + +#include +#include + +template +class Launchpad_window : public Scrollbar_listener, + public Launchpad, + public Window +{ + private: + + /** + * Constants + */ + enum { + _TH = 32, /* height of title bar */ + _SB_XPAD = 5, /* hor. pad of scrollbar */ + _SB_YPAD = 10, /* vert. pad of scrollbar */ + }; + + /** + * Widgets + */ + Titlebar _titlebar; + Sky_texture _texture; + Fade_icon _sizer; + Scrollbar _scrollbar; + Genode::List > _child_entry_list; + Docview _docview; + Spacer _spacer; + Document _document; + + Section _info_section; + Section _launch_section; + Section _kiddy_section; + + Status_entry _status_entry; + + public: + + /** + * Constructor + * + * \param initial_quota maximum value of quota displays + */ + Launchpad_window(Platform *pf, + Redraw_manager *redraw, int max_w, int max_h, + unsigned long inital_quota); + + /** + * Define vertical scroll offset of document + * + * \param update_scrollbar if set to one, adjust scrollbar properties + * to the new view position. + */ + void ypos_sb(int ypos, int update_scrollbar = 1); + + /** + * Window interface + */ + void format(int w, int h); + void ypos(int ypos) { ypos_sb(ypos, 1); } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y) + { + ::Parent_element::draw(c, x, y); + + /* border */ + Color col(0, 0, 0); + c->draw_box(0, 0, _w, 1, col); + c->draw_box(0, _h - 1, _w, 1, col); + c->draw_box(0, 1, 1, _h - 2, col); + c->draw_box(_w - 1, 1, 1, _h - 2, col); + }; + + /** + * Scrollbar listener interface + */ + void handle_scroll(int view_pos); + + /** + * Launchpad interface + */ + void quota(unsigned long quota) + { + _status_entry.max_value(initial_quota() / 1024); + _status_entry.value(quota / 1024); + _status_entry.refresh(); + } + + void add_launcher(const char *filename, + unsigned long default_quota, + Genode::Dataspace_capability config_ds = Genode::Dataspace_capability()) + { + Launch_entry *le; + le = new Launch_entry(filename, default_quota / 1024, + initial_quota() / 1024, + this, config_ds); + _launch_section.append(le); + refresh(); + } + + void add_child(const char *unique_name, + unsigned long quota, + Launchpad_child *launchpad_child, + Genode::Allocator *alloc) + { + Child_entry *ce; + ce = new (alloc) Child_entry(unique_name, quota / 1024, + initial_quota() / 1024, + this, launchpad_child); + _child_entry_list.insert(ce); + _kiddy_section.append(ce); + format(_w, _h); + refresh(); + } + + void remove_child(const char *name, Genode::Allocator *alloc) + { + /* lookup child entry by its name */ + Child_entry *ce = _child_entry_list.first(); + for ( ; ce; ce = ce->Genode::List >::Element::next()) + if (Genode::strcmp(ce->name(), name) == 0) + break; + + if (!ce) { + PWRN("child entry lookup failed"); + return; + } + + _child_entry_list.remove(ce); + _kiddy_section.forget(ce); + destroy(alloc, ce); + format(_w, _h); + refresh(); + } +}; + +#endif diff --git a/demo/src/app/launchpad/loadbar.h b/demo/src/app/launchpad/loadbar.h new file mode 100644 index 000000000..3635f09f6 --- /dev/null +++ b/demo/src/app/launchpad/loadbar.h @@ -0,0 +1,254 @@ +/* + * \brief Loadbar widget + * \author Norman Feske + * \date 2006-08-30 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LOADBAR_H_ +#define _LOADBAR_H_ + +#include "widgets.h" +#include "styles.h" +#include "fade_icon.h" + +#include +#include + + +#define LOADBAR_RGBA _binary_loadbar_rgba_start +#define REDBAR_RGBA _binary_redbar_rgba_start +#define WHITEBAR_RGBA _binary_whitebar_rgba_start +extern unsigned char LOADBAR_RGBA[]; +extern unsigned char REDBAR_RGBA[]; +extern unsigned char WHITEBAR_RGBA[]; + +class Loadbar_listener +{ + public: + + virtual ~Loadbar_listener() { } + + virtual void loadbar_changed(int mx) = 0; +}; + + +class Loadbar_event_handler : public Event_handler +{ + private: + + Loadbar_listener *_listener; + + public: + + Loadbar_event_handler(Loadbar_listener *listener): + _listener(listener) { } + + /** + * Event handler interface + */ + void handle(Event &ev) + { + static int key_cnt; + + if (ev.type == Event::PRESS) key_cnt++; + if (ev.type == Event::RELEASE) key_cnt--; + + if (ev.type == Event::PRESS || ev.type == Event::MOTION) + if (_listener && key_cnt > 0) + _listener->loadbar_changed(ev.mx); + } +}; + + +template +class Loadbar : public Parent_element +{ + private: + + enum { + _LW = 16, + _LH = 16, + }; + + bool _active; + + Fade_icon _cover; + Fade_icon _bar; + + Loadbar_event_handler _ev_handler; + + int _value; + int _max_value; + + const char *_txt; + int _txt_w, _txt_h, _txt_len; + Font *_font; + + void _update_bar_geometry(int w) + { + int max_w = w - _LW; + int bar_w = (_value * max_w) / _max_value; + bar_w += _LW; + _bar.geometry(_bar.x(), _bar.y(), bar_w, _LH); + } + + public: + + Loadbar(Loadbar_listener *listener = 0, Font *font = 0): + _active(listener ? true : false), + _ev_handler(listener), + _value(0), _max_value(100), + _txt(""), _txt_w(0), _txt_h(0), _txt_len(0), + _font(font) + { + _min_h = _LH; + _cover.rgba(LOADBAR_RGBA); + _cover.alpha(100); + _cover.focus_alpha(150); + + _bar.rgba(_active ? REDBAR_RGBA : LOADBAR_RGBA); + _bar.alpha(_active ? 150 : 255); + _bar.default_alpha(150); + + if (_active) + event_handler(&_ev_handler); + + append(&_cover); + append(&_bar); + } + + int value_by_xpos(int xpos) + { + xpos -= _LW/2; + int max_w = _w - _LW; + return max(min((_max_value * xpos) / max_w, _max_value), 0); + } + + int value() { return _value; } + + void value(int value) + { + _value = max(min(value, _max_value), 0); + _update_bar_geometry(_w); + } + + int max_value() { return _max_value; } + + void max_value(int max_value) + { + _max_value = max_value; + _update_bar_geometry(_w); + } + + void txt(const char *txt) + { + if (!_font) return; + _txt = txt; + _txt_w = _font->str_w(_txt, strlen(_txt)); + _txt_h = _font->str_h(_txt, strlen(_txt)); + _txt_len = strlen(_txt); + } + + /** + * Element interface + */ + void format_fixed_width(int w) + { + _cover.geometry(0, 0, w, _LH); + _update_bar_geometry(w); + _min_w = w; + } + + void draw(Canvas *c, int x, int y) + { + Parent_element::draw(c, x, y); + + if (!_font) return; + + int txt_x = x + _x + max((_w - _txt_w)/2, 8); + int txt_y = y + _y + max((_h - _txt_h)/2, 0) - 1; + + /* shrink clipping area to text area (limit too long label) */ + int cx1 = c->clip_x1(), cy1 = c->clip_y1(); + int cx2 = c->clip_x2(), cy2 = c->clip_y2(); + int nx1 = max(cx1, _x + x); + int ny1 = max(cy1, _y + y); + int nx2 = min(cx2, nx1 + _w - 8); + int ny2 = min(cy2, ny1 + _h); + c->clip(nx1, ny1, nx2 - nx1 + 1, ny2 - ny1 + 1); + + c->draw_string(txt_x , txt_y+1, _font, Color(0,0,0,150), _txt, strlen(_txt)); + c->draw_string(txt_x , txt_y, _font, Color(255,255,255,230), _txt, strlen(_txt)); + + /* reset clipping */ + c->clip(cx1, cy1, cx2 - cx1 + 1, cy2 - cy1 + 1); + } + + void mfocus(int flag) + { + if (!_active) return; + + _bar.mfocus(flag); + _cover.mfocus(flag); + } +}; + + +template +class Kbyte_loadbar : public Loadbar +{ + private: + + char _label[32]; + + void _print_kbytes(int kbytes, char *dst, int dst_len) + { + if (kbytes >= 10*1024) + Genode::snprintf(dst, dst_len, "%d MByte", kbytes / 1024); + else + Genode::snprintf(dst, dst_len, "%d KByte", kbytes); + } + + void _update_label() + { + char value_buf[16]; + char max_buf[16]; + + _print_kbytes(Loadbar::value(), value_buf, sizeof(value_buf)); + _print_kbytes(Loadbar::max_value(), max_buf, sizeof(max_buf)); + + Genode::snprintf(_label, sizeof(_label), "%s / %s", value_buf, max_buf); + + Loadbar::txt(_label); + } + + public: + + Kbyte_loadbar(Loadbar_listener *listener, Font *font = 0): + Loadbar(listener, font) + { + _label[0] = 0; + _update_label(); + } + + void value(int val) + { + Loadbar::value(val); + _update_label(); + } + + void max_value(int max_value) + { + Loadbar::max_value(max_value); + _update_label(); + } +}; + +#endif diff --git a/demo/src/app/launchpad/main.cc b/demo/src/app/launchpad/main.cc new file mode 100644 index 000000000..a895d7208 --- /dev/null +++ b/demo/src/app/launchpad/main.cc @@ -0,0 +1,250 @@ +/* + * \brief Launchpad main program + * \date 2006-08-30 + * \author Norman Feske + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "config.h" +#include "elements.h" +#include "platform.h" +#include "canvas_rgb565.h" +#include "tick.h" +#include "redraw_manager.h" +#include "user_state.h" +#include "launchpad_window.h" +#include "printf.h" + +#include +#include +#include + + +/** + * Runtime configuration + */ +namespace Config +{ + int iconbar_detail = 1; + int background_detail = 1; + int mouse_cursor = 1; + int browser_attr = 0; +} + +extern int native_startup(int, char **); + + +/** + * Facility to keep the available quota display up-to-date + */ +class Avail_quota_update : public Tick +{ + private: + + Launchpad *_launchpad; + Genode::size_t _avail; + + public: + + /** + * Constructor + */ + Avail_quota_update(Launchpad *launchpad): + _launchpad(launchpad), _avail(0) { + schedule(200); } + + /** + * Tick interface + */ + int on_tick() + { + Genode::size_t new_avail = Genode::env()->ram_session()->avail(); + + /* update launchpad window if needed */ + if (new_avail != _avail) + _launchpad->quota(new_avail); + + _avail = new_avail; + + /* schedule next tick */ + return 1; + } +}; + + +/** + * Process launchpad XML configuration + */ +static void process_config(Launchpad *launchpad) +{ + using namespace Genode; + + Xml_node config_node = config()->xml_node(); + + /* + * Iterate through all entries of the config file and create + * launchpad entries as specified. + */ + int launcher_cnt = 0; + for (unsigned i = 0; i < config_node.num_sub_nodes(); i++) { + Xml_node node = config_node.sub_node(i); + if (node.has_type("launcher")) + + /* catch XML syntax errors within launcher node */ + try { + /* read file name and default quote from launcher node */ + Xml_node filename_node = node.sub_node("filename"); + + size_t filename_len = filename_node.content_size(); + char *filename = (char *)env()->heap()->alloc(filename_len + 1); + if (!filename) { + ::printf("Error: Out of memory while processing configuration\n"); + return; + } + filename_node.value(filename, filename_len + 1); + Xml_node ram_quota_node = node.sub_node("ram_quota"); + Number_of_bytes default_ram_quota = 0; + ram_quota_node.value(&default_ram_quota); + + /* obtain configuration for the child */ + Init::Child_config *config = new (env()->heap()) + Init::Child_config(Genode::env()->ram_session_cap(), node); + + /* add launchpad entry */ + launchpad->add_launcher(filename, default_ram_quota, + config->dataspace()); + launcher_cnt++; + + } catch (...) { + ::printf("Warning: Launcher entry %d is malformed.\n", + launcher_cnt + 1); + } + else { + char buf[32]; + node.type_name(buf, sizeof(buf)); + ::printf("Warning: Ignoring unsupported tag <%s>.\n", buf); + } + } +} + + +static long read_int_attr_from_config(const char *attr, long default_value) +{ + long result = default_value; + try { + Genode::config()->xml_node().attribute(attr).value(&result); + } catch (...) { } + return result; +} + + +/** + * Main program + */ +int main(int argc, char **argv) +{ + using namespace Genode; + + if (native_startup(argc, argv)) return -1; + + /* look for dynamic linker */ + try { + static Genode::Rom_connection rom("ld.lib.so"); + Genode::Process::dynamic_linker(rom.dataspace()); + } catch (...) { } + + long initial_x = read_int_attr_from_config("xpos", 550); + long initial_y = read_int_attr_from_config("ypos", 150); + long initial_w = read_int_attr_from_config("width", 400); + long initial_h = read_int_attr_from_config("height", 400); + + /* init platform */ + static Platform pf(initial_x, initial_y, initial_w, initial_h); + + /* init canvas */ + static Chunky_canvas canvas; + canvas.init(static_cast(pf.buf_adr()), + pf.scr_w()*pf.scr_h()); + canvas.set_size(pf.scr_w(), pf.scr_h()); + canvas.clip(0, 0, pf.scr_w(), pf.scr_h()); + + /* init redraw manager */ + static Redraw_manager redraw(&canvas, &pf, pf.vw(), pf.vh()); + + /* create instance of launchpad window */ + static Launchpad_window + launchpad( + &pf, &redraw, pf.scr_w(), pf.scr_h(), + env()->ram_session()->avail() + ); + + /* request config file from ROM service */ + try { + process_config(&launchpad); + + /* if there exists no configuration, use defaults */ + } catch (...) { + launchpad.add_launcher("testnit", 512*1024); + launchpad.add_launcher("scout", 11*1024*1024); + launchpad.add_launcher("launchpad", 6*1024*1024); + launchpad.add_launcher("nitlog", 1*1024*1024); + launchpad.add_launcher("liquid_fb", 7*1024*1024); + launchpad.add_launcher("nitpicker", 1*1024*1024); + } + + Avail_quota_update avail_quota_update(&launchpad); + + /* create user state manager */ + static User_state user_state(&launchpad, &launchpad, pf.vx(), pf.vy()); + + /* assign launchpad window as root element to redraw manager */ + redraw.root(&launchpad); + + pf.view_geometry(pf.vx(), pf.vy(), pf.vw(), pf.vh()); + launchpad.parent(&user_state); + launchpad.format(pf.vw(), pf.vh()); + launchpad.ypos(0); + + Genode::printf("--- entering main loop ---\n"); + + /* enter main loop */ + Event ev; + unsigned long curr_time, old_time; + curr_time = old_time = pf.timer_ticks(); + do { + pf.get_event(&ev); + + launchpad.gui_lock.lock(); + + if (ev.type != Event::WHEEL) { + ev.mx -= user_state.vx(); + ev.my -= user_state.vy(); + } + + user_state.handle_event(ev); + + if (ev.type == Event::REFRESH) + pf.scr_update(0, 0, pf.scr_w(), pf.scr_h()); + + if (ev.type == Event::TIMER) + Tick::handle(pf.timer_ticks()); + + /* perform periodic redraw */ + curr_time = pf.timer_ticks(); + if (!pf.event_pending() && ((curr_time - old_time > 20) || (curr_time < old_time))) { + old_time = curr_time; + redraw.process(); + } + + launchpad.gui_lock.unlock(); + + } while (ev.type != Event::QUIT); + + return 0; +} diff --git a/demo/src/app/launchpad/section.h b/demo/src/app/launchpad/section.h new file mode 100644 index 000000000..4bfafa4a4 --- /dev/null +++ b/demo/src/app/launchpad/section.h @@ -0,0 +1,73 @@ +/* + * \brief Section widget + * \author Norman Feske + * \date 2006-08-30 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SECTION_H_ +#define _SECTION_H_ + +#include "widgets.h" + + +template +class Section : public Parent_element +{ + private: + + enum { _SH = 8 }; /* shadow height */ + enum { _STH = 20 }; /* shadow height */ + + Horizontal_shadow _bg; + Horizontal_shadow _shadow; + const char *_txt; + int _txt_w, _txt_h; + int _txt_len; + Font *_font; + int _r_add; + + public: + + Section(const char *txt, Font *font) + : _bg(_STH), _shadow(_SH), _txt(txt), _font(font), _r_add(100) + { + _txt_w = font->str_w(_txt, strlen(_txt)); + _txt_h = font->str_h(_txt, strlen(_txt)); + _txt_len = strlen(_txt); + append(&_bg); + append(&_shadow); + } + + /** + * Element interface + */ + void format_fixed_width(int w) + { + _min_h = _format_children(0, w) + _SH/2; + _min_w = w; + + _bg.geometry(_bg.x(), _bg.y(), _bg.w() + _r_add, _bg.h()); + _shadow.geometry(_shadow.x(), _shadow.y(), _shadow.w() + _r_add, _shadow.h()); + } + + void draw(Canvas *c, int x, int y) + { + c->draw_box(x + _x, y + _y + 1, _w + _r_add, _txt_h - 1, Color(240,240,240,130)); + + int _txt_x = x + _x + max((_w - _txt_w)/2, 8); + int _txt_y = y + _y + max((_STH - _SH - _txt_h)/2, 0) - 1; + + Parent_element::draw(c, x, y); + c->draw_string(_txt_x , _txt_y, _font, Color(0,0,0,150), _txt, strlen(_txt)); + c->draw_box(x + _x, y + _y, _w + _r_add, 1, Color(0,0,0,64)); + } +}; + +#endif diff --git a/demo/src/app/launchpad/status_entry.h b/demo/src/app/launchpad/status_entry.h new file mode 100644 index 000000000..d50626208 --- /dev/null +++ b/demo/src/app/launchpad/status_entry.h @@ -0,0 +1,71 @@ +/* + * \brief Status entry widget + * \author Norman Feske + * \date 2006-09-13 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _STATUS_ENTRY_H_ +#define _STATUS_ENTRY_H_ + +#include "loadbar.h" + +template +class Status_entry : public Parent_element +{ + private: + + Block _block; + Kbyte_loadbar _loadbar; + int _lh; /* launch entry height */ + + enum { _PTW = 100 }; /* program text width */ + enum { _PADX = 10 }; /* horizontal padding */ + enum { _PADR = 16 }; /* right padding */ + + public: + + /** + * Constructor + */ + Status_entry(const char *label) + : _block(Block::RIGHT), _loadbar(0, &label_font) + { + _block.append_plaintext(label, &plain_style); + + _loadbar.max_value(20*1024); + _loadbar.value(3*1024); + + append(&_loadbar); + append(&_block); + + _min_w = _PTW + 100; + } + + void format_fixed_width(int w) + { + _block.format_fixed_width(_PTW); + _lh = _block.min_h(); + _block.geometry(max(10, _PTW - _block.min_w()), + max(0, (_lh - _block.min_h())/2), + min((int)_PTW, _block.min_w()), _lh); + + int lw = max(0, w - 2*_PADX - _PTW - _PADR); + int ly = max(0, (_lh - _loadbar.min_h())/2); + _loadbar.format_fixed_width(lw); + _loadbar.geometry(_PADX + _PTW, ly, lw, 16); + _min_h = _lh; + _min_w = w; + } + + void value(int value) { _loadbar.value(value); } + void max_value(int max_value) { _loadbar.max_value(max_value); } +}; + +#endif diff --git a/demo/src/app/launchpad/target.mk b/demo/src/app/launchpad/target.mk new file mode 100644 index 000000000..df8a3b2c2 --- /dev/null +++ b/demo/src/app/launchpad/target.mk @@ -0,0 +1,11 @@ +TARGET = launchpad +LIBS = launchpad scout_widgets +SRC_CC = launchpad_window.cc \ + launcher.cc \ + main.cc + +SCOUT_DIR = $(REP_DIR)/src/app/scout + +INC_DIR = $(PRG_DIR) \ + $(SCOUT_DIR)/include \ + $(SCOUT_DIR)/include/genode diff --git a/demo/src/app/scout/common/about.cc b/demo/src/app/scout/common/about.cc new file mode 100644 index 000000000..6218094bc --- /dev/null +++ b/demo/src/app/scout/common/about.cc @@ -0,0 +1,191 @@ +/* + * \brief Content generator for "About" section + * \date 2008-07-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "elements.h" +#include "styles.h" + +Document *create_about() +{ + Document *doc = new Document(); + doc->title = ""; + + /** + * Table of contents + */ + + Chapter *toc = new Chapter(); + Center *tc = new Center(); + tc->append(new Spacer(1, 20)); + + /* anchor for section "Scout Tutorial Browser" */ + Anchor *anchor0 = new Anchor(); + + Block *b0 = new Block(); + b0->append_linktext("Scout Tutorial Browser", &link_style, anchor0); + tc->append(b0); + + /* anchor for section "Technical background" */ + Anchor *anchor1 = new Anchor(); + + b0 = new Block(); + b0->append_linktext("Technical background", &link_style, anchor1); + tc->append(b0); + + /* anchor for section "Credits" */ + Anchor *anchor2 = new Anchor(); + + b0 = new Block(); + b0->append_linktext("Credits", &link_style, anchor2); + tc->append(b0); + toc->append(tc); + toc->append(new Spacer(1, 20)); + doc->toc = toc; + doc->append(new Spacer(1, 10)); + Block *title = new Block(Block::CENTER); + title->append_plaintext("", &chapter_style); + doc->append(new Center(title)); + doc->append(new Spacer(1, 10)); + Block *authors = new Block(Block::CENTER); + authors->append_plaintext("", §ion_style); + doc->append(new Center(authors)); + doc->append(new Spacer(1, 10)); + Block *date = new Block(Block::CENTER); + date->append_plaintext("2006-02-06", &subsection_style); + doc->append(new Center(date)); + doc->append(new Spacer(1, 10)); + + /** + * Chapter "Scout Tutorial Browser" + */ + + Chapter *chapter = new Chapter(); + chapter->append(anchor0); + chapter->append(new Spacer(1, 20)); + + b0 = new Block(); + b0->append_plaintext("Scout Tutorial Browser", &chapter_style); + chapter->append(b0); + chapter->append(anchor1); + chapter->append(new Spacer(1, 15)); + + b0 = new Block(); + b0->append_plaintext("Technical background", §ion_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("The Scout Tutorial Browser was created as a low-complexity example application", &plain_style); + b0->append_plaintext("for the Nitpicker GUI. It is implemented with less than", &plain_style); + b0->append_plaintext("4,000", &mono_style); + b0->append_plaintext("lines", &plain_style); + b0->append_plaintext("of C++ code. For PNG image support, libpng is additionally required.", &plain_style); + b0->append_plaintext("Scout demonstrates that neat and useful applications can be built ontop of an extremely", &plain_style); + b0->append_plaintext("small underlying code base.", &plain_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("The most interesting features of Scout are:", &plain_style); + chapter->append(b0); + + Item *i0 = new Item(&plain_style, " o", 20); + + Block *b1 = new Block(); + b1->append_plaintext("Antialiased fonts by the use of an intermediate pixel font format,", &plain_style); + i0->append(b1); + chapter->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("Procedurally textured background that is generated in runtime,", &plain_style); + i0->append(b1); + chapter->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("Refraction and alpha blending of the icons of the Nitpicker-version,", &plain_style); + i0->append(b1); + chapter->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("Text elements such as hyperlinks, accentuations, items, verbatim, and sections,", &plain_style); + i0->append(b1); + chapter->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("Smooth acceleration and deceleration of scrolling (e.g., when hitting the bottom of", &plain_style); + b1->append_plaintext("a page),", &plain_style); + i0->append(b1); + chapter->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("Fading icons and links on mouse-over, and", &plain_style); + i0->append(b1); + chapter->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("Document history browsing.", &plain_style); + i0->append(b1); + chapter->append(i0); + + b0 = new Block(); + b0->append_plaintext("All graphics are runtime generated or created via POV-Ray.", &plain_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("Although Scout was originally designed as a Nitpicker client application, it also", &plain_style); + b0->append_plaintext("runs natively on the DOpE window server, on the L4 Console, and as libSDL program", &plain_style); + b0->append_plaintext("on Linux/X11.", &plain_style); + chapter->append(b0); + chapter->append(anchor2); + chapter->append(new Spacer(1, 15)); + + b0 = new Block(); + b0->append_plaintext("Credits", §ion_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("The graphics, design, and code of Scout were created by Norman Feske.", &plain_style); + chapter->append(b0); + + Block *descitem = new Block(32); + descitem->append_plaintext("E-Mail", &bold_style); + + + chapter->append(descitem); + + Verbatim *v0 = new Verbatim(verbatim_bgcol); + v0->append_textline(" norman.feske@genode-labs.com", &mono_style); + chapter->append(v0); + + descitem = new Block(32); + descitem->append_plaintext("Website", &bold_style); + + + chapter->append(descitem); + + v0 = new Verbatim(verbatim_bgcol); + v0->append_textline(" http://os.inf.tu-dresden.de/~nf2", &mono_style); + chapter->append(v0); + + + return chapter; +} diff --git a/demo/src/app/scout/common/browser_window.cc b/demo/src/app/scout/common/browser_window.cc new file mode 100644 index 000000000..ae80c4edb --- /dev/null +++ b/demo/src/app/scout/common/browser_window.cc @@ -0,0 +1,460 @@ +/* + * \brief Browser window implementation + * \date 2005-10-24 + * \author Norman Feske + * + * This class defines the layout and user policy of a browser window. + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "miscmath.h" +#include "browser_window.h" + + +/**************************** + ** External graphics data ** + ****************************/ + +#define IOR_MAP _binary_ior_map_start +#define HOME_RGBA _binary_home_rgba_start +#define COVER_RGBA _binary_cover_rgba_start +#define INDEX_RGBA _binary_index_rgba_start +#define ABOUT_RGBA _binary_about_rgba_start +#define FORWARD_RGBA _binary_forward_rgba_start +#define BACKWARD_RGBA _binary_backward_rgba_start +#define SIZER_RGBA _binary_sizer_rgba_start +#define TITLEBAR_RGBA _binary_titlebar_rgba_start + +extern short IOR_MAP[]; +extern unsigned char HOME_RGBA[]; +extern unsigned char COVER_RGBA[]; +extern unsigned char INDEX_RGBA[]; +extern unsigned char ABOUT_RGBA[]; +extern unsigned char FORWARD_RGBA[]; +extern unsigned char BACKWARD_RGBA[]; +extern unsigned char SIZER_RGBA[]; +extern unsigned char TITLEBAR_RGBA[]; + +enum { + ICON_HOME = 0, + ICON_BACKWARD = 1, + ICON_FORWARD = 2, + ICON_INDEX = 3, + ICON_ABOUT = 4, + NUM_ICONS = 5 +}; + +/* icon graphics data */ +static unsigned char *glow_icon_gfx[] = { + HOME_RGBA, + BACKWARD_RGBA, + FORWARD_RGBA, + INDEX_RGBA, + ABOUT_RGBA, +}; + +/* color definitions for glowing effect of the icons */ +static Color glow_icon_col[] = { + Color(210, 210, 0), + Color( 0, 0, 160), + Color( 0, 0, 160), + Color( 0, 160, 0), + Color(160, 0, 0), +}; + + +/*************** + ** Utilities ** + ***************/ + +/** + * Transform rgba source image to image with native pixel type + * + * If we specify an empty buffer as alpha channel (all values zero), we simply + * assign the source image data to the destination buffer. If there are valid + * values in the destination alpha channel, we paint the source image on top + * of the already present image data. This enables us to combine multiple + * rgba buffers (layers) into one destination buffer. + */ +template +static void extract_rgba(const unsigned char *src, int w, int h, + PT *dst_pixel, unsigned char *dst_alpha) +{ + for (int i = 0; i < w*h; i++, src += 4) { + + int r = src[0]; + int g = src[1]; + int b = src[2]; + int a = src[3]; + + if (dst_alpha[i]) { + PT s(r, g, b); + dst_pixel[i] = PT::mix(dst_pixel[i], s, a); + dst_alpha[i] = max((int)dst_alpha[i], a); + } else { + dst_pixel[i].rgba(r, g, b); + dst_alpha[i] = a; + } + } +} + + +/******************** + ** Event handlers ** + ********************/ + +class Iconbar_event_handler : public Event_handler +{ + private: + + Fader *_fader; + Browser *_browser; + int _icon_id; + + public: + + /** + * Constructor + */ + Iconbar_event_handler(Fader *fader, int icon_id, Browser *browser) + { + _fader = fader; + _browser = browser; + _icon_id = icon_id; + } + + /** + * Event handler interface + */ + void handle(Event &ev) + { + static int key_cnt; + + if (ev.type == Event::PRESS) key_cnt++; + if (ev.type == Event::RELEASE) key_cnt--; + + /* start movement with zero speed */ + if ((ev.type != Event::PRESS) || (key_cnt != 1)) return; + + /* no flashing by default */ + int flash = 0; + + switch (_icon_id) { + + case ICON_HOME: + + flash |= _browser->go_home(); + break; + + case ICON_BACKWARD: + + flash |= _browser->go_backward(); + break; + + case ICON_FORWARD: + + flash |= _browser->go_forward(); + break; + + case ICON_INDEX: + + flash |= _browser->go_toc(); + break; + + case ICON_ABOUT: + + flash |= _browser->go_about(); + break; + } + + /* flash clicked icon */ + if (0 && flash) { + + /* flash fader to the max */ + _fader->step(4); + _fader->curr(190); + } + } +}; + + +template +class Browser_sizer_event_handler : public Sizer_event_handler +{ + private: + + Browser_window *_browser_win; + Anchor *_ca; /* original visible element */ + + /** + * Event handler interface + */ + void start_drag() + { + Sizer_event_handler::start_drag(); + _ca = _browser_win->curr_anchor(); + } + + void do_drag() + { + Sizer_event_handler::do_drag(); + _browser_win->go_to(_ca, 0); + } + + public: + + /** + * Constructor + */ + Browser_sizer_event_handler(Browser_window *browser_win): + Sizer_event_handler(browser_win) + { + _browser_win = browser_win; + } +}; + + +/****************************** + ** Browser window interface ** + ******************************/ + +template +Browser_window::Browser_window(Document *initial_content, + Platform *pf, + Redraw_manager *redraw, + int max_w, int max_h, int attr) +: Browser(_IH + _TH), Window(pf, redraw, max_w, max_h) +{ + /* init attributes */ + _ypos = 0; + _document = initial_content; + _attr = attr; + + /* init docview and history with initial document */ + _docview.texture(&_texture); + _docview.voffset(doc_offset()); + _history.add(initial_content); + + /* init icons */ + memset(_icon_fg, 0, sizeof(_icon_fg)); + memset(_icon_fg_alpha, 0, sizeof(_icon_fg_alpha)); + for (int i = 0; i < _NUM_ICONS; i++) { + + /* convert rgba raw image to PT pixel format and alpha channel */ + extract_rgba(COVER_RGBA, _IW, _IH, + _icon_fg[i][0], _icon_fg_alpha[i][0]); + + /* assign back buffer, foreground and distmap to icon */ + _icon[i].backbuf(_icon_backbuf[0], 1); + _icon[i].distmap(IOR_MAP, _IW*2, _IH*2); + _icon[i].foreground(_icon_fg[i][0], _icon_fg_alpha[i][0]); + _icon[i].event_handler(new Mover_event_handler(this)); + + /* apply foreground graphics to icon */ + extract_rgba(glow_icon_gfx[i], _IW, _IH, + _icon_fg[i][0], _icon_fg_alpha[i][0]); + + /* init glow icon */ + Fade_icon *fadeicon = &_glow_icon[i]; + + fadeicon->glow(glow_icon_gfx[i], glow_icon_col[i]); + fadeicon->default_alpha(0); + fadeicon->focus_alpha(100); + fadeicon->alpha(0); + fadeicon->event_handler(new Iconbar_event_handler(fadeicon, i, this)); + } + + /* + * All icons share the same distmap. Therefore we need to scratch + * only one distmap to affect all icons. + */ + _icon[0].scratch(_SCRATCH); + + /* create panel tile texture */ + /* + * NOTE: The panel height must be the same as the icon height. + */ + for (int j = 0; j < _PANEL_H; j++) + for (int i = 0; i < _PANEL_W; i++) { + _panel_fg [j][i] = _icon_fg [ICON_INDEX][j][i&0x1]; + _panel_fg_alpha [j][i] = _icon_fg_alpha [ICON_INDEX][j][i&0x1] + random()%3; + } + + /* init panel background */ + _panel.backbuf(&_panel_backbuf[0][0]); + _panel.distmap(_panel_distmap[0], _PANEL_W*2, _PANEL_H*2); + _panel.foreground(_panel_fg[0], _panel_fg_alpha[0]); + _panel.scratch(_SCRATCH); + _panel.event_handler(new Mover_event_handler(this)); + + /* resize handle */ + _sizer.rgba(SIZER_RGBA); + _sizer.event_handler(new Browser_sizer_event_handler(this)); + _sizer.alpha(100); + + /* titlebar */ + _titlebar.rgba(TITLEBAR_RGBA); + _titlebar.text(_document->title); + _titlebar.event_handler(new Mover_event_handler(this)); + + _min_w = _NUM_ICONS*_IW; + _min_h = _IH + 250; + + /* adopt widgets as child elements */ + append(&_docview); + for (int i = 0; i <= ICON_INDEX; i++) { + append(&_icon[i]); + append(&_glow_icon[i]); + } + append(&_panel); + append(&_icon[ICON_ABOUT]); + append(&_glow_icon[ICON_ABOUT]); + append(&_shadow); + append(&_scrollbar); + + if (_attr & ATTR_SIZER) append(&_sizer); + if (_attr & ATTR_TITLEBAR) append(&_titlebar); + + _scrollbar.listener(this); + + _content(initial_content); +} + + +template +void Browser_window::ypos_sb(int ypos, int update_scrollbar) +{ + if (ypos < -_docview.h() + _h) + ypos = -_docview.h() + _h; + + _ypos = ypos <= 0 ? ypos : 0; + + _docview.geometry(_docview.x(), _ypos, _docview.w(), _docview.h()); + + if (update_scrollbar) + _scrollbar.view(_docview.h(), _h, -_ypos); + + refresh(); +} + + +/*********************** + ** Browser interface ** + ***********************/ + +template +Element *Browser_window::_content() +{ + return _docview.content(); +} + + +template +void Browser_window::_content(Element *content) +{ + if (!content || (content == _docview.content())) return; + content->fill_cache(redraw()->canvas()); + _docview.content(content); + format(_w, _h); + _ypos = 0; +} + + +template +void Browser_window::format(int w, int h) +{ + /* limit browser window size to valid values */ + w = (w < _min_w) ? _min_w : w; + h = (h < _min_h) ? _min_h : h; + w = (w > max_w()) ? max_w() : w; + h = (h > max_h()) ? max_h() : h; + + /* determine old scrollbar visibility */ + int old_sb_visibility = (_docview.min_h() > _h); + + /* assign new size to browser window */ + _w = w; + _h = h; + + /* format document */ + _docview.format_fixed_width(_w); + + /* format titlebar */ + _titlebar.format_fixed_width(_w); + + /* determine new scrollbar visibility */ + int new_sb_visibility = (_docview.min_h() > _h); + + /* reformat docview on change of scrollbar visibility */ + if (old_sb_visibility ^ new_sb_visibility) { + _docview.right_pad(new_sb_visibility ? _scrollbar.min_w() : 0); + _docview.format_fixed_width(_w); + } + + /* position docview */ + _docview.geometry(0, _ypos, _docview.min_w(), max(_docview.min_h(), _h)); + + /* start at top */ + int y = 0; + + /* position titlebar */ + if (_attr & ATTR_TITLEBAR) { + _titlebar.geometry(y, 0, _w, _TH); + y += _TH; + } + + /* position icons */ + for (int i = 0; i <= ICON_INDEX; i++) { + _icon[i].geometry(i*_IW, y, _IW, _IH); + _glow_icon[i].geometry(i*_IW, y, _IW, _IH); + } + _icon[ICON_ABOUT].geometry(_w - _IW, y, _IW, _IH); + _glow_icon[ICON_ABOUT].geometry(_w - _IW, y, _IW, _IH); + + /* the panel is the space between the left icon set and the right about icon */ + int panel_x = _icon[ICON_INDEX].x() + _IW; + _panel.geometry(panel_x, y, _icon[ICON_ABOUT].x() - panel_x, _IH); + + y += _IH; + + _scrollbar.geometry(w - _scrollbar.min_w() - _SB_XPAD, y + _SB_YPAD, + _scrollbar.min_w(), h - y - _SB_YPAD*2 - + (_attr & ATTR_SIZER ? 8 : 0)); + _shadow.geometry(0, y, _w, 10); + + if (_attr & ATTR_SIZER) + _sizer.geometry(_w - 32, _h - 32, 32, 32); + + pf()->view_geometry(pf()->vx(), pf()->vy(), _w, _h); + redraw()->size(_w, _h); +} + + +template +Anchor *Browser_window::curr_anchor() { return find_by_y(doc_offset()); } + + +/********************************** + ** Scrollbar listener interface ** + **********************************/ + +template +void Browser_window::handle_scroll(int view_pos) +{ + /* + * The handle scroll notification comes from the scrollbar, + * which already adjusted itself to the new view port. + * Therefore, we do not need to re-adjust it another time + * and call ypos() with update_scrollbar set to zero. + */ + ypos_sb(-view_pos, 0); +} + +#include "canvas_rgb565.h" +template class Browser_window; diff --git a/demo/src/app/scout/common/doc.cc b/demo/src/app/scout/common/doc.cc new file mode 100644 index 000000000..1cd26dbd1 --- /dev/null +++ b/demo/src/app/scout/common/doc.cc @@ -0,0 +1,456 @@ +/* + * \brief Browser content + * \date 2010-11-07 + * \author Generated by GOSH + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "elements.h" +#include "styles.h" + +Document *create_document() +{ + Document *doc = new Document(); + doc->title = "Introduction to Genode"; + + /** + * Table of contents + */ + + Chapter *toc = new Chapter(); + Center *tc = new Center(); + tc->append(new Spacer(1, 20)); + + /* anchor for section "The launchpad application starter" */ + Anchor *anchor0 = new Anchor(); + + Block *b0 = new Block(); + b0->append_linktext("The launchpad application starter", &link_style, anchor0); + tc->append(b0); + + /* anchor for section "Recursive system structure" */ + Anchor *anchor1 = new Anchor(); + + b0 = new Block(); + b0->append_linktext("Recursive system structure", &link_style, anchor1); + tc->append(b0); + + /* anchor for section "The flexibility of nested policies" */ + Anchor *anchor2 = new Anchor(); + + b0 = new Block(); + b0->append_linktext("The flexibility of nested policies", &link_style, anchor2); + tc->append(b0); + + /* anchor for section "Where to go from here?" */ + Anchor *anchor3 = new Anchor(); + + b0 = new Block(); + b0->append_linktext("Where to go from here?", &link_style, anchor3); + tc->append(b0); + toc->append(tc); + toc->append(new Spacer(1, 20)); + doc->toc = toc; + doc->append(new Spacer(1, 10)); + Block *title = new Block(Block::CENTER); + title->append_plaintext("Introduction to Genode", &chapter_style); + doc->append(new Center(title)); + + extern char _binary_genode_logo_png_start[]; + Png_image *png = new Png_image(_binary_genode_logo_png_start); + doc->append(new Spacer(1, 10)); + doc->append(new Center(png)); + doc->append(new Spacer(1, 10)); + + b0 = new Block(); + b0->append_plaintext("Genode is a construction kit for building special-purpose operating systems", &plain_style); + b0->append_plaintext("out of a number of components such as device drivers, protocol", &plain_style); + b0->append_plaintext("stacks, and applications. Those components are organized using only a few", &plain_style); + b0->append_plaintext("yet powerful architectual prinicples, and thereby, allow for the", &plain_style); + b0->append_plaintext("composition of a wide range of different systems. The live CD is meant to", &plain_style); + b0->append_plaintext("showcase how far the concept scales as of today.", &plain_style); + doc->append(b0); + + b0 = new Block(); + b0->append_plaintext("The following introduction will provide you with hands-on experience with", &plain_style); + b0->append_plaintext("the basics of Genode:", &plain_style); + doc->append(b0); + + Item *i0 = new Item(&plain_style, " o", 20); + + Block *b1 = new Block(); + b1->append_plaintext("The creation and destruction of single processes as well as arbitrarily", &plain_style); + b1->append_plaintext("complex sub systems", &plain_style); + i0->append(b1); + doc->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("The trusted-path facility of the Nitpicker secure GUI", &plain_style); + i0->append(b1); + doc->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("The assignment of resource quotas to sub systems", &plain_style); + i0->append(b1); + doc->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("The multiple instantiation of services", &plain_style); + i0->append(b1); + doc->append(i0); + + i0 = new Item(&plain_style, " o", 20); + + b1 = new Block(); + b1->append_plaintext("The usage of run-time adaptable policy for routing client requests to", &plain_style); + b1->append_plaintext("different services", &plain_style); + i0->append(b1); + doc->append(i0); + + /** + * Chapter "The launchpad application starter" + */ + + Chapter *chapter = new Chapter(); + chapter->append(anchor0); + chapter->append(new Spacer(1, 20)); + + b0 = new Block(); + b0->append_plaintext("The launchpad application starter", &chapter_style); + chapter->append(b0); + + extern char _binary_launchpad_png_start[]; + png = new Png_image(_binary_launchpad_png_start); + chapter->append(new Spacer(1, 10)); + chapter->append(new Center(png)); + chapter->append(new Spacer(1, 10)); + + b0 = new Block(); + b0->append_plaintext("Figure", &plain_style); + b0->append_plaintext("launchpad", &link_style); + b0->append_plaintext("shows the main window of the launchpad application. It", &plain_style); + b0->append_plaintext("consists of three areas. The upper area contains status information about", &plain_style); + b0->append_plaintext("launchpad itself. The available memory quota is presented by a grey-colored", &plain_style); + b0->append_plaintext("bar. The middle area of the window contains the list of available applications", &plain_style); + b0->append_plaintext("that can be started by clicking on the application's name. Before starting an", &plain_style); + b0->append_plaintext("application, the user can define the amount of memory quota to donate to the", &plain_style); + b0->append_plaintext("new application by adjusting the red bar using the mouse.", &plain_style); + Launcher *l0 = new Launcher("launchpad", 1, 22*1024*1024); + b0->append_launchertext("Start the launchpad by clicking on this link...", &link_style, l0); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("For a first test, you may set the memory quota of the program named scout to", &plain_style); + b0->append_plaintext("10MB and then click its name. Thereupon, another instance of the scout text", &plain_style); + b0->append_plaintext("browser will be started and the lower area of launchpad becomes populated with", &plain_style); + b0->append_plaintext("status information about launchpad's children. Currently, launchpad has scout", &plain_style); + b0->append_plaintext("as its only child. For each child, its name, its memory quota, and a kill", &plain_style); + b0->append_plaintext("button are presented. After having started scout, you will further notice a", &plain_style); + b0->append_plaintext("change of launchpad's own status information as the memory quota spent for", &plain_style); + b0->append_plaintext("scout is not directly available to launchpad anymore.", &plain_style); + chapter->append(b0); + + extern char _binary_setup_png_start[]; + png = new Png_image(_binary_setup_png_start); + chapter->append(new Spacer(1, 10)); + chapter->append(new Center(png)); + chapter->append(new Spacer(1, 10)); + + b0 = new Block(); + b0->append_plaintext("In Figure", &plain_style); + b0->append_plaintext("setup", &link_style); + b0->append_plaintext(", you see an illustration of the current setup (slightly", &plain_style); + b0->append_plaintext("simplified, leaving out the main menu and the other parts of the live CD). At", &plain_style); + b0->append_plaintext("the very bottom, there are the kernel, core, and init. Init has started the", &plain_style); + b0->append_plaintext("framebuffer driver, the timer driver, the nitpicker GUI server, and launchpad", &plain_style); + b0->append_plaintext("as it children. Launchpad, in turn, has started the second instance of scout as", &plain_style); + b0->append_plaintext("its only child. You can get a further idea about the relationship between the", &plain_style); + b0->append_plaintext("applications by pressing the", &plain_style); + b0->append_plaintext("ScrLock", &mono_style); + b0->append_plaintext("key, which gets especially handled by", &plain_style); + b0->append_plaintext("the nitpicker GUI server. We call this key the X-ray key because it makes the", &plain_style); + b0->append_plaintext("identity of each window on screen visible to the user. Each screen region gets", &plain_style); + b0->append_plaintext("labeled by its chain of parents and their grandparents respectively. During the", &plain_style); + b0->append_plaintext("walk through the demo scenario, you may press the X-ray key at any time to make", &plain_style); + b0->append_plaintext("the parent-child relationships visible on screen.", &plain_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("By pressing the kill button (labeled with", &plain_style); + b0->append_plaintext("x", &mono_style); + b0->append_plaintext(") of the scout child in", &plain_style); + b0->append_plaintext("launchpad's window, scout will disappear and launchpad regains its original", &plain_style); + b0->append_plaintext("memory quota. Although killing a process may sound like a simple thing to do,", &plain_style); + b0->append_plaintext("it is worthwhile to mention that scout was using a number of services, for", &plain_style); + b0->append_plaintext("example core's LOG service, the nitpicker GUI service, and the timer service.", &plain_style); + b0->append_plaintext("While using these services, scout made portions of its own memory quota", &plain_style); + b0->append_plaintext("available to them. When scout was killed by launchpad, all those relationships", &plain_style); + b0->append_plaintext("were gracefully reverted such that there is no resource leakage.", &plain_style); + chapter->append(b0); + + Navbar *navbar = new Navbar(); + navbar->prev_link("Home", doc); + navbar->next_link("Recursive system structure", anchor1); + chapter->append(navbar); + + /** + * Chapter "Recursive system structure" + */ + + chapter = new Chapter(); + chapter->append(anchor1); + chapter->append(new Spacer(1, 20)); + + b0 = new Block(); + b0->append_plaintext("Recursive system structure", &chapter_style); + chapter->append(b0); + + extern char _binary_x_ray_small_png_start[]; + png = new Png_image(_binary_x_ray_small_png_start); + chapter->append(new Spacer(1, 10)); + chapter->append(new Center(png)); + chapter->append(new Spacer(1, 10)); + + b0 = new Block(); + b0->append_plaintext("Thanks to the recursive structure of Genode, the mechanisms", &plain_style); + b0->append_plaintext("that function for a single application are also applicable to", &plain_style); + b0->append_plaintext("whole sub systems.", &plain_style); + b0->append_plaintext("As a test, you may configure the launchpad application", &plain_style); + b0->append_plaintext("entry within the launchpad window to 15MB and start", &plain_style); + b0->append_plaintext("another instance of launchpad.", &plain_style); + b0->append_plaintext("A new launchpad window will appear. Apart from the status", &plain_style); + b0->append_plaintext("information at the upper part of its window, it looks", &plain_style); + b0->append_plaintext("completely identical to the first instance.", &plain_style); + b0->append_plaintext("You may notice that the displayed available quota of the", &plain_style); + b0->append_plaintext("second launchpad instance is lower then the 15MB. The", &plain_style); + b0->append_plaintext("difference corresponds to the application's static memory", &plain_style); + b0->append_plaintext("usage including the BSS segment and the double-buffer", &plain_style); + b0->append_plaintext("backing store.", &plain_style); + b0->append_plaintext("With the new instance, you may start further applications,", &plain_style); + b0->append_plaintext("for example by clicking on", &plain_style); + b0->append_plaintext("testnit.", &mono_style); + b0->append_plaintext("To distinguish the different instances of the applications", &plain_style); + b0->append_plaintext("on screen, the X-ray key becomes handy again.", &plain_style); + b0->append_plaintext("Figure", &plain_style); + b0->append_plaintext("x-ray_small", &link_style); + b0->append_plaintext("shows a screenshot of the described setup", &plain_style); + b0->append_plaintext("in X-ray mode.", &plain_style); + b0->append_plaintext("Now, after creating a whole hierarchy of applications,", &plain_style); + b0->append_plaintext("you can try killing the whole tree at once by clicking", &plain_style); + b0->append_plaintext("the kill button of the launchpad entry in the original", &plain_style); + b0->append_plaintext("launchpad window.", &plain_style); + b0->append_plaintext("You will notice that whole sub system gets properly", &plain_style); + b0->append_plaintext("destructed and the original system state is regained.", &plain_style); + chapter->append(b0); + + navbar = new Navbar(); + navbar->prev_link("The launchpad application starter", anchor0); + navbar->next_link("The flexibility of nested policies", anchor2); + chapter->append(navbar); + + /** + * Chapter "The flexibility of nested policies" + */ + + chapter = new Chapter(); + chapter->append(anchor2); + chapter->append(new Spacer(1, 20)); + + b0 = new Block(); + b0->append_plaintext("The flexibility of nested policies", &chapter_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("Beside providing the ability to construct and destruct", &plain_style); + b0->append_plaintext("hierarchically structured sub systems, the recursive", &plain_style); + b0->append_plaintext("system structure allows for an extremely flexible", &plain_style); + b0->append_plaintext("definition and management of system policies that can", &plain_style); + b0->append_plaintext("be implanted into each parent.", &plain_style); + b0->append_plaintext("As an example, launchpad has a simple built-in policy of how", &plain_style); + b0->append_plaintext("children are connected to services.", &plain_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("If a child requests", &plain_style); + b0->append_plaintext("a service, launchpad looks if such a service is provided", &plain_style); + b0->append_plaintext("by any of the other children and, if so, a connection", &plain_style); + b0->append_plaintext("gets established. If the service is not offered by any child,", &plain_style); + b0->append_plaintext("launchpad delegates the request to its parent.", &plain_style); + b0->append_plaintext("For example, a request for the", &plain_style); + b0->append_plaintext("LOG", &mono_style); + b0->append_plaintext("service will always", &plain_style); + b0->append_plaintext("end up at core, which implements the service by the", &plain_style); + b0->append_plaintext("means of terminal (or kernel debug) output.", &plain_style); + b0->append_plaintext("By starting a child that offers the same service interface,", &plain_style); + b0->append_plaintext("however, we can shadow core's", &plain_style); + b0->append_plaintext("LOG", &mono_style); + b0->append_plaintext("service by an alternative", &plain_style); + b0->append_plaintext("implementation.", &plain_style); + b0->append_plaintext("You can try this out by first starting", &plain_style); + b0->append_plaintext("testnit", &mono_style); + b0->append_plaintext("and", &plain_style); + b0->append_plaintext("observing its log output at the terminal window. When", &plain_style); + b0->append_plaintext("started,", &plain_style); + b0->append_plaintext("testnit", &mono_style); + b0->append_plaintext("tells us some status information.", &plain_style); + b0->append_plaintext("By further starting the program called", &plain_style); + b0->append_plaintext("nitlog,", &mono_style); + b0->append_plaintext("we create", &plain_style); + b0->append_plaintext("a new", &plain_style); + b0->append_plaintext("LOG", &mono_style); + b0->append_plaintext("service as a child of launchpad. On screen, this", &plain_style); + b0->append_plaintext("application appears just as a black window that can be", &plain_style); + b0->append_plaintext("dragged to any screen position with the mouse.", &plain_style); + b0->append_plaintext("When now starting a new instance of", &plain_style); + b0->append_plaintext("testnit", &mono_style); + b0->append_plaintext(", launchpad", &plain_style); + b0->append_plaintext("will resolve the request for the", &plain_style); + b0->append_plaintext("LOG", &mono_style); + b0->append_plaintext("service by establishing", &plain_style); + b0->append_plaintext("a connection to", &plain_style); + b0->append_plaintext("nitlog", &mono_style); + b0->append_plaintext("instead of propagating the request", &plain_style); + b0->append_plaintext("to its parent. Consequently, we can now observe the status", &plain_style); + b0->append_plaintext("output of the second", &plain_style); + b0->append_plaintext("testnit", &mono_style); + b0->append_plaintext("instance inside the", &plain_style); + b0->append_plaintext("nitlog", &mono_style); + b0->append_plaintext("window.", &plain_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("The same methodology can be applied to arbitrarily complex", &plain_style); + b0->append_plaintext("services. For example, you can create a new instance of", &plain_style); + b0->append_plaintext("the framebuffer service by starting the", &plain_style); + b0->append_plaintext("liquid_fb", &mono_style); + b0->append_plaintext("application.", &plain_style); + b0->append_plaintext("This application provides the framebuffer service and,", &plain_style); + b0->append_plaintext("in turn, uses the nitpicker GUI server to get displayed on", &plain_style); + b0->append_plaintext("screen. Because any new requests for a framebuffer will now be", &plain_style); + b0->append_plaintext("served by the", &plain_style); + b0->append_plaintext("liquid_fb", &mono_style); + b0->append_plaintext("application, we can start another", &plain_style); + b0->append_plaintext("instance of nitpicker. This instance uses", &plain_style); + b0->append_plaintext("liquid_fb", &mono_style); + b0->append_plaintext("as its", &plain_style); + b0->append_plaintext("graphics back end and, in turn, provides the GUI service.", &plain_style); + b0->append_plaintext("Now, when starting another instance of scout, the new scout", &plain_style); + b0->append_plaintext("window will appear within", &plain_style); + b0->append_plaintext("liquid_fb", &mono_style); + b0->append_plaintext("too (Figure", &plain_style); + b0->append_plaintext("liquid_fb_small", &link_style); + b0->append_plaintext(").", &plain_style); + chapter->append(b0); + + extern char _binary_liquid_fb_small_png_start[]; + png = new Png_image(_binary_liquid_fb_small_png_start); + chapter->append(new Spacer(1, 10)); + chapter->append(new Center(png)); + chapter->append(new Spacer(1, 10)); + + b0 = new Block(); + b0->append_plaintext("The extremely simple example policy implemented in launchpad", &plain_style); + b0->append_plaintext("in combination with the recursive system structure of Genode", &plain_style); + b0->append_plaintext("already provides a wealth of flexibility without the need", &plain_style); + b0->append_plaintext("to recompile or reconfigure any application.", &plain_style); + b0->append_plaintext("The policy implemented and enforced by a parent may", &plain_style); + b0->append_plaintext("also deny services for its children or impose other restrictions.", &plain_style); + b0->append_plaintext("For example, the window labels presented in X-ray mode are", &plain_style); + b0->append_plaintext("successively defined by all parents and grandparents that", &plain_style); + b0->append_plaintext("mediate the request of an application to the GUI service.", &plain_style); + b0->append_plaintext("The scout window as the parent of launchpad imposes its", &plain_style); + b0->append_plaintext("policy of labeling the GUI session with the label", &plain_style); + b0->append_plaintext("\"launchpad\"", &italic_style); + b0->append_plaintext(".", &plain_style); + b0->append_plaintext("Init as the parent of scout again overrides this label", &plain_style); + b0->append_plaintext("with the name of its immediate child from which the GUI request", &plain_style); + b0->append_plaintext("comes from. Hence the label becomes", &plain_style); + b0->append_plaintext("\"scout -> launchpad\"", &italic_style); + b0->append_plaintext(".", &plain_style); + chapter->append(b0); + + navbar = new Navbar(); + navbar->prev_link("Recursive system structure", anchor1); + navbar->next_link("Where to go from here?", anchor3); + chapter->append(navbar); + + /** + * Chapter "Where to go from here?" + */ + + chapter = new Chapter(); + chapter->append(anchor3); + chapter->append(new Spacer(1, 20)); + + b0 = new Block(); + b0->append_plaintext("Where to go from here?", &chapter_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("Although this little demonstration scratches only the surface of", &plain_style); + b0->append_plaintext("Genode, we hope that the power of its underlying design becomes", &plain_style); + b0->append_plaintext("apparent. The most distinctive property of Genode, however, is its", &plain_style); + b0->append_plaintext("extremely low complexity. The functionality of the complete demo", &plain_style); + b0->append_plaintext("scenario is implemented in less than 20,000 lines of source code", &plain_style); + b0->append_plaintext("(LOC), including the GUI and the demo applications. As a point of", &plain_style); + b0->append_plaintext("reference, when relying on libpng for decompressing the images as seen", &plain_style); + b0->append_plaintext("in the text browser, this number doubles. In fact, the complete base", &plain_style); + b0->append_plaintext("OS framework accounts for less source-code complexity than the code", &plain_style); + b0->append_plaintext("needed for decoding the PNG images. To these numbers, the complexity", &plain_style); + b0->append_plaintext("of the used underlying kernel must be added, for example 10-20 KLOC", &plain_style); + b0->append_plaintext("for an L4 microkernel (or far more than 500 KLOC when relying on the", &plain_style); + b0->append_plaintext("Linux kernel). In combination with a microkernel, Genode enables the", &plain_style); + b0->append_plaintext("implementation of security-sensitive applications with a trusted", &plain_style); + b0->append_plaintext("computing base (TCB) of some thousands rather than millions of lines", &plain_style); + b0->append_plaintext("of code. If using a hypervisor as kernel for Genode, this advantage", &plain_style); + b0->append_plaintext("can further be combined with compatibility to existing applications", &plain_style); + b0->append_plaintext("executed on virtual machines.", &plain_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("More details, architectural and technical documents, our road", &plain_style); + b0->append_plaintext("map, and the complete source code are available at", &plain_style); + b0->append_plaintext("http://genode.org", &link_style); + b0->append_plaintext(".", &plain_style); + chapter->append(b0); + + b0 = new Block(); + b0->append_plaintext("The development of the Genode OS Framework is conducted as", &plain_style); + b0->append_plaintext("an open-source community project, coordinated by Genode Labs,", &plain_style); + b0->append_plaintext("a company founded by the original authors of Genode.", &plain_style); + b0->append_plaintext("If you are interested in supporting our project through", &plain_style); + b0->append_plaintext("participation or funding, please consider joining our", &plain_style); + b0->append_plaintext("community (", &plain_style); + b0->append_plaintext("http://genode.org", &link_style); + b0->append_plaintext(") or contact Genode Labs", &plain_style); + b0->append_plaintext("(", &plain_style); + b0->append_plaintext("http://www.genode-labs.com", &link_style); + b0->append_plaintext(").", &plain_style); + chapter->append(b0); + + Verbatim *v0 = new Verbatim(verbatim_bgcol); + v0->append_textline(" info@genode-labs.com", &mono_style); + chapter->append(v0); + + navbar = new Navbar(); + navbar->prev_link("The flexibility of nested policies", anchor2); + chapter->append(navbar); + + navbar = new Navbar(); + navbar->next_link("The launchpad application starter", anchor0); + doc->append(navbar); + + return doc; +} diff --git a/demo/src/app/scout/common/elements.cc b/demo/src/app/scout/common/elements.cc new file mode 100644 index 000000000..7d074ea67 --- /dev/null +++ b/demo/src/app/scout/common/elements.cc @@ -0,0 +1,453 @@ +/* + * \brief Document structure elements + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "miscmath.h" +#include "elements.h" +#include "browser.h" + + +/************* + ** Element ** + *************/ + +Element::~Element() +{ + if (_parent) + _parent->forget(this); +} + + +void Element::redraw_area(int x, int y, int w, int h) +{ + x += _x; + y += _y; + + /* intersect specified area with element geometry */ + int x1 = max(x, _x); + int y1 = max(y, _y); + int x2 = min(x + w - 1, _x + _w - 1); + int y2 = min(y + h - 1, _y + _h - 1); + + if (x1 > x2 || y1 > y2) return; + + /* propagate redraw request to the parent */ + if (_parent) + _parent->redraw_area(x1, y1, x2 - x1 + 1, y2 - y1 + 1); +} + + +Element *Element::find(int x, int y) +{ + if (x >= _x && x < _x + _w + && y >= _y && y < _y + _h + && _flags.findable) + return this; + + return 0; +} + + +Element *Element::find_by_y(int y) +{ + return (y >= _y && y < _y + _h) ? this : 0; +} + + +int Element::abs_x() { return _x + (_parent ? _parent->abs_x() : 0); } +int Element::abs_y() { return _y + (_parent ? _parent->abs_y() : 0); } + + +Element *Element::chapter() +{ + if (_flags.chapter) return this; + return _parent ? _parent->chapter() : 0; +} + + +Browser *Element::browser() +{ + return _parent ? _parent->browser() : 0; +} + + +/******************** + ** Parent element ** + ********************/ + +void Parent_element::append(Element *e) +{ + if (_last) + _last->next = e; + else + _first = e; + + _last = e; + + e->parent(this); +} + + +void Parent_element::remove(Element *e) +{ + if (e == _first) + _first = e->next; + + else { + + /* search specified element in the list */ + Element *ce = _first; + while (ce->next && (ce->next != e)) + ce = ce->next; + + /* element is not member of the list */ + if (!ce->next) return; + + /* e->_next is the element to remove, skip it in list */ + ce->next = ce->next->next; + } + + e->next = 0; + + /* update information about last element */ + for (Element *ce = _first; ce; ce = ce->next) + _last = ce; +} + + +void Parent_element::forget(Element *e) +{ + if (e->parent() == this) + remove(e); + + _parent->forget(e); +} + + +int Parent_element::_format_children(int x, int w) +{ + int y = 0; + + if (w <= 0) return 0; + + for (Element *e = _first; e; e = e->next) { + e->format_fixed_width(w); + e->geometry(x, y, e->min_w(), e->min_h()); + y += e->min_h(); + } + + return y; +} + + +void Parent_element::draw(Canvas *c, int x, int y) +{ + for (Element *e = _first; e; e = e->next) + e->try_draw(c, _x + x, _y + y); +} + + +Element *Parent_element::find(int x, int y) +{ + /* check if position is outside the parent element */ + if (x < _x || x >= _x + _w + || y < _y || y >= _y + _h) + return 0; + + x -= _x; + y -= _y; + + /* check children */ + Element *ret = this; + for (Element *e = _first; e; e = e->next) { + Element *res = e->find(x, y); + if (res) ret = res; + } + + return ret; +} + + +Element *Parent_element::find_by_y(int y) +{ + /* check if position is outside the parent element */ + if (y < _y || y >= _y + _h) + return 0; + + y -= _y; + + /* check children */ + for (Element *e = _first; e; e = e->next) { + Element *res = e->find_by_y(y); + if (res) return res; + } + + return this; +} + + +void Parent_element::geometry(int x, int y, int w, int h) +{ + ::Element::geometry(x, y, w, h); + + if (!_last || !_last->is_bottom()) return; + + _last->geometry(_last->x(), h - _last->h(), _last->w(), _last->h()); +} + + +void Parent_element::fill_cache(Canvas *c) +{ + for (Element *e = _first; e; e = e->next) e->fill_cache(c); +} + + +void Parent_element::flush_cache(Canvas *c) +{ + for (Element *e = _first; e; e = e->next) e->flush_cache(c); +} + + +void Parent_element::curr_link_destination(Element *dst) +{ + for (Element *e = _first; e; e = e->next) e->curr_link_destination(dst); +} + + +/*********** + ** Token ** + ***********/ + +Token::Token(Style *style, const char *str, int len) +{ + _str = str; + _len = len; + _style = style; + _flags.takes_focus = 0; + _col = _style ? _style->color : Color(0, 0, 0); + _outline = Color(0, 0, 0, 0); + + if (!_style) return; + _min_w = _style->font->str_w(str, len) + _style->font->str_w(" ", 1); + _min_h = _style->font->str_h(str, len); +} + + +void Token::draw(Canvas *c, int x, int y) +{ + if (!_style) return; + if (_style->attr & Style::ATTR_BOLD) + _outline.rgba(_col.r, _col.g, _col.b, 32); + + x++; y++; + + if (_outline.a) + for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) + c->draw_string(_x + x +i , _y + y +j, _style->font, _outline, _str, _len); + + c->draw_string(_x + x, _y + y, _style->font, _col, _str, _len); + + if (_flags.link) + c->draw_box(_x + x, _y + y + _h - 1, _w, 1, Color(0,0,255)); +} + + +/*********** + ** Block ** + ***********/ + +void Block::append_text(const char *str, Style *style, + Text_type type, Anchor *a, Launcher *l) +{ + while (*str) { + + /* skip spaces */ + if (*str == ' ') { + str++; + continue; + } + + /* search end of word */ + int i; + for (i = 0; str[i] && (str[i] != ' '); i++); + + /* create and append token for the word */ + if (i) { + + if ((type == LAUNCHER) && l) + append(new Launcher_link_token(style, str, i, l)); + + else if ((type == LINK) && a) + append(new Link_token(style, str, i, a)); + + else + append(new Token(style, str, i)); + } + + /* continue with next word */ + str += i; + } +} + + +void Block::format_fixed_width(int w) +{ + int x = 0, y = 0; + int line_max_h = 0; + int max_w = 0; + + for (Element *e = _first; e; e = e->next) { + + /* wrap at the end of the line */ + if (x + e->min_w() >= w) { + x = _second_indent; + y += line_max_h; + line_max_h = 0; + } + + /* position element */ + if (max_w < x + e->min_w()) + max_w = x + e->min_w(); + + e->geometry(x, y, e->min_w(), e->min_h()); + + /* determine token with the biggest height of the line */ + if (line_max_h < e->min_h()) + line_max_h = e->min_h(); + + x += e->min_w(); + } + + /* + * Now, the text is left-aligned. + * Let's apply another alignment if specified. + */ + + if (_align != LEFT) { + for (Element *line = _first; line; ) { + + Element *e; + int cy = line->y(); /* y position of current line */ + int max_x; /* rightmost position */ + + /* determine free space at the end of the line */ + for (max_x = 0, e = line; e && (e->y() == cy); e = e->next) + max_x = max(max_x, e->x() + e->w() - 1); + + /* indent elements of the line according to the alignment */ + int dx = 0; + if (_align == CENTER) dx = max(0, (max_w - max_x)/2); + if (_align == RIGHT) dx = max(0, max_w - max_x); + for (e = line; e && (e->y() == cy); e = e->next) + e->geometry(e->x() + dx, e->y(), e->w(), e->h()); + + /* find first element of next line */ + for (; line && (line->y() == cy); line = line->next); + } + } + + /* line break at the end of the last line */ + if (line_max_h) y += line_max_h; + + _min_h = y + 5; + _min_w = max_w; +} + + +/************ + ** Center ** + ************/ + +void Center::format_fixed_width(int w) +{ + _min_h = _format_children(0, w); + + /* determine highest min with of children */ + int highest_min_w = 0; + for (Element *e = _first; e; e = e->next) + if (highest_min_w < e->min_w()) + highest_min_w = e->min_w(); + + int dx = (w - highest_min_w)>>1; + _min_w = max(w, highest_min_w); + + /* move children to center */ + for (Element *e = _first; e; e = e->next) + e->geometry(dx, e->y(), e->w(), e->h()); +} + + +/************** + ** Verbatim ** + **************/ + +void Verbatim::draw(Canvas *c, int x, int y) +{ + static const int pad = 5; + + c->draw_box(_x + x + pad, _y + y + pad, _w - 2*pad, _h - 2*pad, bgcol); + + int cx1 = c->clip_x1(), cy1 = c->clip_y1(); + int cx2 = c->clip_x2(), cy2 = c->clip_y2(); + + c->clip(_x + x + pad, _y + y + pad, _w - 2*pad, _h - 2*pad); + Parent_element::draw(c, x, y); + c->clip(cx1, cy1, cx2 - cx1 + 1, cy2 - cy1 + 1); +} + + +void Verbatim::format_fixed_width(int w) +{ + int y = 10; + + for (Element *e = _first; e; e = e->next) { + + /* position element */ + e->geometry(10, y, e->min_w(), e->min_h()); + + y += e->min_h(); + } + + _min_h = y + 10; + _min_w = w; +} + + +/**************** + ** Link_token ** + ****************/ + +void Link_token::handle(Event &e) +{ + if (e.type != Event::PRESS) return; + + /* make browser to follow link */ + Browser *b = browser(); + if (b && _dst) b->go_to(_dst); +} + + +/************************* + ** Launcher_link_token ** + *************************/ + +void Launcher_link_token::handle(Event &e) +{ + if (e.type != Event::PRESS) return; + + step(8); + curr(255); + + /* launch executable */ + Launcher *l = (Launcher *)_dst; + if (l) l->launch(); +} diff --git a/demo/src/app/scout/common/main.cc b/demo/src/app/scout/common/main.cc new file mode 100644 index 000000000..0c7ebd850 --- /dev/null +++ b/demo/src/app/scout/common/main.cc @@ -0,0 +1,158 @@ +/* + * \brief Scout tutorial browser main program + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/** + * Local includes + */ +#include "config.h" +#include "elements.h" +#include "platform.h" +#include "canvas_rgb565.h" +#include "fade_icon.h" +#include "tick.h" +#include "redraw_manager.h" +#include "user_state.h" +#include "browser_window.h" + +extern Document *create_document(); + + +/** + * Runtime configuration + */ +namespace Config +{ + int iconbar_detail = 1; + int background_detail = 1; + int mouse_cursor = 1; + int browser_attr = 0; +} + + +#define POINTER_RGBA _binary_pointer_rgba_start +#define NAV_NEXT_RGBA _binary_nav_next_rgba_start +#define NAV_PREV_RGBA _binary_nav_prev_rgba_start + +extern unsigned char POINTER_RGBA[]; +extern unsigned char NAV_NEXT_RGBA[]; +extern unsigned char NAV_PREV_RGBA[]; + +static unsigned char *navicons_rgba[] = { NAV_NEXT_RGBA, NAV_PREV_RGBA }; +static Generic_icon **navicons[] = { &Navbar::next_icon, &Navbar::prev_icon }; + +extern int native_startup(int, char **); + + +/** + * Main program + */ +int main(int argc, char **argv) +{ + if (native_startup(argc, argv)) return -1; + + /* init platform */ + static Platform pf(256, 80, 530, 620); + + /* initialize icons for navigation bar */ + for (unsigned int i = 0; i < sizeof(navicons)/sizeof(void *); i++) { + Fade_icon *icon = new Fade_icon; + icon->rgba(navicons_rgba[i]); + icon->alpha(100); + *navicons[i] = icon; + } + + static Document *doc = create_document(); + + /* init canvas */ + static Chunky_canvas canvas; + canvas.init(static_cast(pf.buf_adr()), + pf.scr_w()*pf.scr_h()); + canvas.set_size(pf.scr_w(), pf.scr_h()); + canvas.clip(0, 0, pf.scr_w(), pf.scr_h()); + + /* init redraw manager */ + static Redraw_manager redraw(&canvas, &pf, pf.vw(), pf.vh(), true); + + /* create instance of browser window */ + static Browser_window browser + ( + doc, /* initial document */ + &pf, /* platform */ + &redraw, /* redraw manager object */ + pf.scr_w(), pf.scr_h(), /* max size of window */ + Config::browser_attr + ); + + /* initialize mouse cursor */ + int mx = 0, my = 0; + static Icon mcursor; + if (Config::mouse_cursor) { + mcursor.geometry(mx, my, 32, 32); + mcursor.rgba(POINTER_RGBA); + mcursor.alpha(255); + mcursor.findable(0); + browser.append(&mcursor); + } + + /* create user state manager */ + static User_state user_state(&browser, &browser, pf.vx(), pf.vy()); + + /* assign browser as root element to redraw manager */ + redraw.root(&browser); + + browser.ypos(0); + + /* enter main loop */ + Event ev; + unsigned long curr_time, old_time; + curr_time = old_time = pf.timer_ticks(); + do { + pf.get_event(&ev); + + if (ev.type != Event::WHEEL) { + ev.mx -= user_state.vx(); + ev.my -= user_state.vy(); + + /* update mouse cursor */ + if (Config::mouse_cursor && (ev.mx != mx || ev.my != my)) { + int x1 = min(ev.mx, mx); + int y1 = min(ev.my, my); + int x2 = max(ev.mx + mcursor.w() - 1, mx + mcursor.w() - 1); + int y2 = max(ev.my + mcursor.h() - 1, my + mcursor.h() - 1); + + mcursor.geometry(ev.mx, ev.my, mcursor.w(), mcursor.h()); + redraw.request(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + + mx = ev.mx; my = ev.my; + } + } + + user_state.handle_event(ev); + + if (ev.type == Event::REFRESH) + pf.scr_update(0, 0, pf.scr_w(), pf.scr_h()); + + if (ev.type == Event::TIMER) + Tick::handle(pf.timer_ticks()); + + /* perform periodic redraw */ + curr_time = pf.timer_ticks(); + if (!pf.event_pending() && ((curr_time - old_time > 20) || (curr_time < old_time))) { + old_time = curr_time; + redraw.process(); + } + + } while (ev.type != Event::QUIT); + + return 0; +} diff --git a/demo/src/app/scout/common/navbar.cc b/demo/src/app/scout/common/navbar.cc new file mode 100644 index 000000000..91d7b86df --- /dev/null +++ b/demo/src/app/scout/common/navbar.cc @@ -0,0 +1,200 @@ +/* + * \brief Document navigation element + * \date 2005-11-23 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "elements.h" +#include "widgets.h" +#include "canvas_rgb565.h" +#include "styles.h" +#include "browser.h" + + +/** + * Configuration + */ +enum { + ARROW_H = 64, /* height of arrow gfx */ + ARROW_W = 64, /* width of arrow gfx */ +}; + + +Generic_icon *Navbar::next_icon; +Generic_icon *Navbar::prev_icon; + + +class Linkicon_event_handler : public Event_handler +{ + private: + + Anchor *_dst; + Navbar *_navbar; + + public: + + /** + * Constructor + */ + Linkicon_event_handler() { _dst = 0; _navbar = 0; } + + /** + * Assign link destination + */ + void destination(Navbar *navbar, Anchor *dst) + { + _dst = dst; + _navbar = navbar; + } + + /** + * Event handler interface + */ + void handle(Event &ev) + { + if (ev.type != Event::PRESS || !_navbar) return; + + Browser *b = _navbar->browser(); + if (!b || !_dst) return; + + _navbar->curr(0); + b->go_to(_dst); + _navbar->fade_to(100, 2); + } +}; + + +static Linkicon_event_handler next_ev_handler; +static Linkicon_event_handler prev_ev_handler; + + +Navbar::Navbar() +{ + _next_title = _prev_title = 0; + _next_anchor = _prev_anchor = 0; + + _flags.bottom = 1; + + next_ev_handler.destination(0, 0); + prev_ev_handler.destination(0, 0); +} + + +void Navbar::next_link(const char *title, Anchor *dst) +{ + _next_title = new Block(Block::RIGHT); + _next_anchor = dst; + _next_title->append_plaintext(title, &navbar_style); + append(_next_title); + next_ev_handler.destination(0, 0); +} + + +void Navbar::prev_link(const char *title, Anchor *dst) +{ + _prev_title = new Block(Block::LEFT); + _prev_anchor = dst; + _prev_title->append_plaintext(title, &navbar_style); + append(_prev_title); + prev_ev_handler.destination(0, 0); +} + + +void Navbar::format_fixed_width(int w) +{ + const int padx = 10; /* free space in the center */ + + /* format labels */ + int text_w = w/2 - ARROW_W - padx; + if (_next_title) _next_title->format_fixed_width(text_w); + if (_prev_title) _prev_title->format_fixed_width(text_w); + + /* determine right-alignment offset for right label */ + int next_dx = _next_title ? text_w - _next_title->min_w() : 0; + + /* determine bounding box of navbar */ + int h = ARROW_H; + if (_next_title) h = max(h, _next_title->min_h()); + if (_prev_title) h = max(h, _prev_title->min_h()); + h += 16; + + /* assign icons to this navbar instance */ + next_icon->parent(this); + prev_icon->parent(this); + + next_ev_handler.destination(this, _next_anchor); + prev_ev_handler.destination(this, _prev_anchor); + + next_icon->event_handler(&next_ev_handler); + prev_icon->event_handler(&prev_ev_handler); + + /* place icons */ + int ypos = (h - ARROW_H)/2; + next_icon->geometry(w - 64, ypos, ARROW_W, ARROW_H); + prev_icon->geometry(0, ypos, ARROW_W, ARROW_H); + + /* place labels */ + if (_next_title) { + ypos = (h - _next_title->min_h())/2 + 1; + _next_title->geometry(w/2 + padx + next_dx, ypos, text_w, _next_title->min_h()); + } + if (_prev_title) { + ypos = (h - _prev_title->min_h())/2 + 1; + _prev_title->geometry(ARROW_W, ypos, text_w, _prev_title->min_h()); + } + + _min_w = w; + _min_h = h; +} + + +void Navbar::draw(Canvas *c, int x, int y) +{ + int cx1 = c->clip_x1(), cy1 = c->clip_y1(); + int cx2 = c->clip_x2(), cy2 = c->clip_y2(); + + /* shrink clipping area to text area (cut too long words) */ + int nx1 = max(cx1, _x + x + ARROW_W); + int ny1 = max(cy1, _y + y); + int nx2 = min(cx2, nx1 + _w - 2*ARROW_W); + int ny2 = min(cy2, ny1 + _h); + + c->clip(nx1, ny1, nx2 - nx1 + 1, ny2 - ny1 + 1); + Parent_element::draw(c, x, y); + c->clip(cx1, cy1, cx2 - cx1 + 1, cy2 - cy1 + 1); + + if (_prev_title) prev_icon->draw(c, _x + x, _y + y); + if (_next_title) next_icon->draw(c, _x + x, _y + y); +} + + +Element *Navbar::find(int x, int y) +{ + Element *res; + + if (_prev_title && (res = prev_icon->find(x - _x, y - _y))) return res; + if (_next_title && (res = next_icon->find(x - _x, y - _y))) return res; + + return ::Parent_element::find(x, y); +} + + +int Navbar::on_tick() +{ + /* call on_tick function of the fader */ + if (::Fader::on_tick() == 0) return 0; + + prev_icon->alpha(_curr_value); + next_icon->alpha(_curr_value); + navbar_style.color.rgba(0, 0, 0, _curr_value); + + refresh(); + return 1; +} diff --git a/demo/src/app/scout/common/png_image.cc b/demo/src/app/scout/common/png_image.cc new file mode 100644 index 000000000..732c2154e --- /dev/null +++ b/demo/src/app/scout/common/png_image.cc @@ -0,0 +1,148 @@ +/* + * \brief PNG image element + * \date 2005-11-07 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +#include "miscmath.h" +#include "elements.h" +#include "alloc.h" + + +class Png_stream +{ + private: + + char *_addr; + + public: + + /** + * Constructor + */ + Png_stream(char *addr) { _addr = addr; } + + /** + * Read from png stream + */ + void read(char *dst, int len) + { + memcpy(dst, _addr, len); + _addr += len; + } +}; + + +/** + * PNG read callback + */ +static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t len) +{ + Png_stream *stream = (Png_stream *)png_get_io_ptr(png_ptr); + + stream->read((char *)data, len); +} + + +/** + * Dummy to make libl4png happy + */ +extern "C" int l4libpng_fread(void *buf, int size, int nmemb, void *stream) +{ + printf("l4libpng_fread called - function not implemented\n"); + return 0; +} + + +/*********************** + ** Element interface ** + ***********************/ + +void Png_image::fill_cache(Canvas *c) +{ + if (_texture) return; + + + Png_stream *stream = new Png_stream((char *)_png_data); + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) return; + + png_set_read_fn(png_ptr, stream, user_read_data); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); + return; + } + + png_read_info(png_ptr, info_ptr); + + /* get image data chunk */ + int bit_depth, color_type, interlace_type; + png_uint_32 w, h; + png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + _min_w = w; + _min_h = h; + printf("png is %d x %d, depth=%d\n", _min_w, _min_h, bit_depth); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_gray_1_2_4_to_8(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + if (bit_depth < 8) png_set_packing(png_ptr); + if (bit_depth == 16) png_set_strip_16(png_ptr); + + _texture = c->alloc_texture(_min_w, _min_h); + + /* allocate buffer for decoding a row */ + static png_byte *row_ptr; + static int curr_row_size; + + int needed_row_size = png_get_rowbytes(png_ptr, info_ptr)*8; + + if (curr_row_size < needed_row_size) { + if (row_ptr) scout_free(row_ptr); + row_ptr = (png_byte *)scout_malloc(needed_row_size); + curr_row_size = needed_row_size; + } + + /* fill texture */ + for (int j = 0; j < _min_h; j++) { + png_read_row(png_ptr, row_ptr, NULL); + c->set_rgba_texture(_texture, (unsigned char *)row_ptr, _min_w, j); + } +} + + +void Png_image::flush_cache(Canvas *c) +{ + c->free_texture(_texture); + _texture = 0; +} + + +void Png_image::draw(Canvas *c, int x, int y) +{ + /* if texture is not ready, try to initialize it */ + if (!_texture) fill_cache(c); + + /* draw texture */ + if (_texture) + c->draw_texture(_texture, x + _x, y + _y); +} diff --git a/demo/src/app/scout/common/refracted_icon.cc b/demo/src/app/scout/common/refracted_icon.cc new file mode 100644 index 000000000..bf06e6113 --- /dev/null +++ b/demo/src/app/scout/common/refracted_icon.cc @@ -0,0 +1,184 @@ +/* + * \brief Implementation of refracted icons + * \date 2005-10-24 + * \author Norman Feske + * + * A refracted icon is a icon that refracts its background + * using a distortion map. + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "config.h" +#include "miscmath.h" +#include "refracted_icon.h" + + +/*************** + ** Utilities ** + ***************/ + +/** + * Backup original (background) pixel data into back buffer + */ +template +static void filter_src_to_backbuf(PT *src, int src_w, + PT *dst, int dst_w, int dst_h, int width) +{ + for (int j = 0; j < (dst_h>>1); j++, src += src_w, dst += 2*dst_w) { + for (int i = 0; i < width; i++) { + dst[2*i] = src[i]; + dst[2*i + 1] = PT::avr(src[i], src[i + 1]); + dst[2*i + dst_w] = PT::avr(src[i], src[i + src_w]); + dst[2*i + dst_w + 1] = PT::avr(dst[2*i + dst_w], dst[2*i + 1]); + } + } +} + + +/** + * Backup original (background) pixel data into back buffer + */ +template +static void copy_src_to_backbuf(PT *src, int src_w, + PT *dst, int dst_w, int dst_h, int width) +{ + for (int j = 0; j < (dst_h>>1); j++, src += src_w, dst += 2*dst_w) + for (int i = 0; i < width; i++) + dst[2*i] = dst[2*i + 1] = dst[2*i + dst_w] = dst[2*i + dst_w + 1] = src[i]; +} + + +/** + * Copy and distort back-buffer pixels to front buffer + */ +template +void distort(PT src[], DT distmap[], int distmap_w, int distmap_h, + PT fg[], unsigned char alpha[], + PT dst[], int dst_w, int width) +{ + int line_offset = (distmap_w>>1) - width; + width <<= 1; + + for (int j = 0; j < distmap_h; j += 2, dst += dst_w) { + + PT *d = dst; + + for (int i = 0; i < width; i += 2, src += 2, distmap += 2) { + + /* fetch distorted pixel from back buffer */ + PT v = PT::avr(src[distmap[0]], + src[distmap[1] + 1], + src[distmap[distmap_w] + distmap_w], + src[distmap[distmap_w + 1] + distmap_w + 1]); + + /* mix back-buffer pixel with foreground */ + *d++ = PT::mix(v, *fg++, *alpha++); + } + + fg += line_offset; + alpha += line_offset; + src += line_offset*2 + distmap_w; /* skip one line in back buffer */ + distmap += line_offset*2 + distmap_w; /* skip one line in distmap */ + } +} + + +/** + * Copy and distort back-buffer pixels to front buffer + */ +template +void copy(PT src[], int src_w, PT dst[], int dst_w, int w, int h) +{ + for (int j = 0; j < h; j ++, src += src_w, dst += dst_w) + memcpy(dst, src, w*sizeof(PT)); +} + + +/****************************** + ** Refracted icon interface ** + ******************************/ + +template +void Refracted_icon::scratch(int jitter) +{ + PT ref_color = _fg[0]; + for (int j = 0; j < _distmap_h; j++) for (int i = 0; i < _distmap_w; i++) { + + int fg_offset = (j>>1)*(_distmap_w>>1) + (i>>1); + + int dr = _fg[fg_offset].r() - ref_color.r(); + int dg = _fg[fg_offset].g() - ref_color.g(); + int db = _fg[fg_offset].b() - ref_color.b(); + + if (dr < 0) dr = -dr; + if (dg < 0) dg = -dg; + if (db < 0) db = -db; + + static const int limit = 20; + if (dr > limit || dg > limit || db > limit) continue; + + int dx, dy; + + do { + dx = jitter ? ((random()%jitter) - (jitter>>1)) : 0; + dy = jitter ? ((random()%jitter) - (jitter>>1)) : 0; + } while ((dx < -i) || (dx > _distmap_w - 2 - i) + || (dy < -j) || (dy > _distmap_h - 2 - j)); + + _distmap[j*_distmap_w + i] += dy*_distmap_w + dx; + } +} + + +/*********************** + ** Element interface ** + ***********************/ + +template +void Refracted_icon::draw(Canvas *c, int x, int y) +{ + PT *addr = static_cast(c->addr()); + + if (!addr || !_backbuf || !_fg || !_fg_alpha) return; + + /* + * NOTE: There is no support for clipping. + * Use this code with caution! + */ + + addr += c->w()*(y + _y) + x + _x; + + int fg_w = _distmap_w>>1; + + for (int i = 0; i < _w; i += fg_w, addr += fg_w) { + + int curr_w = min(fg_w, _w - i); + + if (Config::iconbar_detail == 0) { + copy(_fg, _distmap_w>>1, addr, c->w(), curr_w, _distmap_h>>1); + continue; + } + + /* backup old canvas pixels */ + if (_filter_backbuf) + filter_src_to_backbuf(addr, c->w(), _backbuf, _distmap_w, + _distmap_h, fg_w); + else + copy_src_to_backbuf(addr, c->w(), _backbuf, _distmap_w, + _distmap_h, fg_w); + + /* draw distorted pixels back to canvas */ + distort(_backbuf, _distmap, _distmap_w, _distmap_h, + _fg, _fg_alpha, addr, c->w(), curr_w); + } +} + + +#include "canvas_rgb565.h" +template class Refracted_icon; diff --git a/demo/src/app/scout/common/scrollbar.cc b/demo/src/app/scout/common/scrollbar.cc new file mode 100644 index 000000000..42196f192 --- /dev/null +++ b/demo/src/app/scout/common/scrollbar.cc @@ -0,0 +1,327 @@ +/* + * \brief Scrollbar implementation + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "scrollbar.h" +#include "tick.h" + +#define SLIDER_RGBA _binary_slider_rgba_start +#define UPARROW_RGBA _binary_uparrow_rgba_start +#define DNARROW_RGBA _binary_downarrow_rgba_start + +extern unsigned char SLIDER_RGBA[]; +extern unsigned char UPARROW_RGBA[]; +extern unsigned char DNARROW_RGBA[]; + + +/******************** + ** Event handlers ** + ********************/ + +template +class Arrow_event_handler : public Event_handler, public Tick +{ + private: + + /** + * Constants + */ + static const int _max_speed = 16*256; + + Scrollbar *_sb; + Fade_icon *_icon; + unsigned char *_rgba; + int _direction; + int _curr_speed; + int _dst_speed; + int _view_pos; + int _accel; + + public: + + /** + * Constructor + */ + Arrow_event_handler(Scrollbar *sb, + Fade_icon *icon, + int direction, + unsigned char *rgba) + { + _sb = sb; + _icon = icon; + _direction = direction; + _accel = 1; + _rgba = rgba; + } + + /** + * Event handler interface + */ + void handle(Event &ev) + { + static int key_cnt; + + if (ev.type == Event::PRESS) key_cnt++; + if (ev.type == Event::RELEASE) key_cnt--; + + /* start movement with zero speed */ + if ((ev.type == Event::PRESS) && (key_cnt == 1)) { + + /* press icon (slight vertical shift, darker shadow) */ + _icon->rgba(_rgba, 1, 3); + _icon->refresh(); + + _curr_speed = _direction*256; + _dst_speed = _direction*_max_speed; + _accel = 16; + _view_pos = _sb->view_pos() << 8; + schedule(10); + } + + if ((ev.type == Event::RELEASE) && (key_cnt == 0)) { + + /* release icon */ + _icon->rgba(_rgba); + _icon->refresh(); + + _accel = 64; + _dst_speed = 0; + } + } + + /** + * Tick interface + */ + int on_tick() + { + /* accelerate */ + if (_curr_speed < _dst_speed) + _curr_speed = min(_curr_speed + _accel, _dst_speed); + + /* decelerate */ + if (_curr_speed > _dst_speed) + _curr_speed = max(_curr_speed - _accel, _dst_speed); + + /* soft stopping on boundaries */ + while ((_curr_speed < 0) && (_view_pos > 0) + && (_curr_speed*_curr_speed > _view_pos*_accel*4)) + _curr_speed = min(0, _curr_speed + _accel*4); + + int max_pos; + while ((_curr_speed > 0) + && ((max_pos = (_sb->real_size() - _sb->view_size())*256 - _view_pos) > 0) + && (_curr_speed*_curr_speed > max_pos*_accel*4)) + _curr_speed = max(0, _curr_speed - _accel*4); + + /* move view position with current speed */ + _view_pos = max(0, _view_pos + _curr_speed); + + /* set new view position */ + int old_view_pos = _sb->view_pos(); + _sb->view(_sb->real_size(), _sb->view_size(), _view_pos>>8); + if (old_view_pos != _sb->view_pos()) + _sb->notify_listener(); + + /* keep ticking as long as we are on speed */ + return (_curr_speed != 0); + } +}; + + +template +class Slider_event_handler : public Event_handler +{ + private: + + Scrollbar *_sb; + Fade_icon *_icon; + unsigned char *_rgba; + + public: + + /** + * Constructor + */ + Slider_event_handler(Scrollbar *sb, + Fade_icon *icon, + unsigned char *rgba) + { + _sb = sb; + _icon = icon; + _rgba = rgba; + } + + /** + * Event handler interface + */ + void handle(Event &ev) + { + static int key_cnt; + static int curr_my, orig_my; + static int orig_slider_pos; + + if (ev.type == Event::PRESS) key_cnt++; + if (ev.type == Event::RELEASE) key_cnt--; + + /* start movement with zero speed */ + if ((ev.type == Event::PRESS) && (key_cnt == 1)) { + + /* press icon (slight vertical shift, darker shadow) */ + _icon->rgba(_rgba, 1, 3); + _icon->refresh(); + + orig_my = curr_my = ev.my; + orig_slider_pos = _sb->slider_pos(); + } + + if ((ev.type == Event::RELEASE) && (key_cnt == 0)) { + + /* release icon */ + _icon->rgba(_rgba); + _icon->refresh(); + } + + if (key_cnt && (ev.my != curr_my)) { + curr_my = ev.my; + _sb->slider_pos(orig_slider_pos + curr_my - orig_my); + _sb->notify_listener(); + } + } +}; + + +/************************* + ** Scrollbar interface ** + *************************/ + +template +Scrollbar::Scrollbar() +{ + /* init scrollbar elements */ + _slider.rgba(SLIDER_RGBA); + _uparrow.rgba(UPARROW_RGBA); + _dnarrow.rgba(DNARROW_RGBA); + + _uparrow.alpha(0); + _dnarrow.alpha(0); + _slider .alpha(0); + + append(&_uparrow); + append(&_dnarrow); + append(&_slider); + + _min_w = sb_elem_w; + _min_h = sb_elem_h*3; + + _real_size = 100; + _view_size = 100; + _view_pos = 0; + _listener = 0; + _visibility = 0; + + /* define event handlers for scrollbar elements */ + _uparrow.event_handler(new Arrow_event_handler(this, &_uparrow, -1, UPARROW_RGBA)); + _dnarrow.event_handler(new Arrow_event_handler(this, &_dnarrow, 1, DNARROW_RGBA)); + _slider.event_handler(new Slider_event_handler(this, &_slider, SLIDER_RGBA)); +} + + +template +int Scrollbar::slider_size() +{ + return max(sb_elem_h, ((_h - sb_elem_h*2)*_view_size)/_real_size); +} + + +template +int Scrollbar::slider_pos() +{ + int real_range = _real_size - _view_size; + int slider_range = _h - sb_elem_h*2 - slider_size(); + int pos = real_range ? (slider_range*_view_pos)/real_range : 0; + + return pos + sb_elem_h; +} + + +template +void Scrollbar::slider_pos(int pos) +{ + int slider_bg_h = _h - sb_elem_h*2; + + _view_pos = ((pos - sb_elem_h)*_real_size)/slider_bg_h; + _view_pos = max(0, min(_view_pos, _real_size - _view_size)); + + _slider.geometry(0, slider_pos(), sb_elem_w, slider_size()); +} + + +template +void Scrollbar::view(int real_size, int view_size, int view_pos) +{ + _real_size = real_size; + _view_size = min(view_size, real_size); + _view_pos = max(0, min(view_pos, _real_size - _view_size)); + + geometry(_x, _y, _w, _h); +} + + +template +void Scrollbar::notify_listener() +{ + if (_listener) + _listener->handle_scroll(_view_pos); +} + + +/*********************** + ** Element interface ** + ***********************/ + +template +void Scrollbar::geometry(int x, int y, int w, int h) +{ + Element::geometry(x, y, w, h); + + int new_visibility = _visible(); + + if (new_visibility) { + _uparrow.geometry(0, 0, sb_elem_w, sb_elem_h); + _dnarrow.geometry(0, h - sb_elem_h, sb_elem_w, sb_elem_h); + _slider. geometry(0, slider_pos(), sb_elem_w, slider_size()); + } + + if (_visibility ^ new_visibility) { + int alpha = new_visibility ? _uparrow.default_alpha() : 0; + int speed = new_visibility ? 3 : 2; + _uparrow.fade_to(alpha, speed); + _dnarrow.fade_to(alpha, speed); + _slider. fade_to(alpha, speed); + } + + _visibility = new_visibility; +} + + +template +Element *Scrollbar::find(int x, int y) +{ + if (_visibility) + return Parent_element::find(x, y); + + return 0; +} + + +#include "canvas_rgb565.h" +template class Scrollbar; diff --git a/demo/src/app/scout/common/sky_texture.cc b/demo/src/app/scout/common/sky_texture.cc new file mode 100644 index 000000000..5cce0ff60 --- /dev/null +++ b/demo/src/app/scout/common/sky_texture.cc @@ -0,0 +1,363 @@ +/* + * \brief Sky texture element for the use as background + * \date 2005-10-24 + * \author Norman Feske + * + * At initialization time, we generate four 4-bit maps based on + * bicubic interpolation of some noise at different frequencies. + * At runtime, we overlay (add their values) the generated map + * and use the result as index of a color table. + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "config.h" +#include "miscmath.h" +#include "sky_texture.h" + + +/*********************** + ** Texture generator ** + ***********************/ + +/** + * Calculate fractional part of texture position for a given coordinate + */ +static inline int calc_u(int x, int w, int texture_w) +{ + return ((texture_w*x<<8)/w) & 0xff; +} + + +/** + * Kubic interpolation + * + * \param u relative position between x1 and x2 (0..255) + */ +static inline int filter(int x0, int x1, int x2, int x3, int u) +{ + static int cached_u = -1; + static int k0, k1, k2, k3; + + /* + * Do not recompute coefficients when called + * with the same subsequencing u values. + */ + if (u != cached_u) { + + int v = 255 - u; + int uuu = (u*u*u)>>16; + int vvv = (v*v*v)>>16; + int uu = (u*u)>>8; + int vv = (v*v)>>8; + + k0 = vvv/6; + k3 = uuu/6; + k1 = k3*3 - uu + (4<<8)/6; + k2 = k0*3 - vv + (4<<8)/6; + + cached_u = u; + } + + return (x0*k0 + x1*k1 + x2*k2 + x3*k3)>>8; +} + + +/** + * Determine texture position by given position in image + */ +static inline int get_idx(int x, int w, int texture_w, int offset) +{ + return (offset + texture_w + (texture_w*x)/w) % texture_w; +} + + +/** + * Generate sky texture based on bicubic interpolation of some noise + */ +static void gen_buf(short tmp[], int noise_w, int noise_h, + short dst[], int dst_w, int dst_h) +{ + /* generate noise */ + for (int i = 0; i < noise_h; i++) for (int j = 0; j < noise_w; j++) + dst[i*dst_w + j] = random()%256 - 128; + + /* interpolate horizontally */ + for (int j = dst_w - 1; j >= 0; j--) { + + int x0_idx = get_idx(j, dst_w, noise_w, -1); + int x1_idx = get_idx(j, dst_w, noise_w, 0); + int x2_idx = get_idx(j, dst_w, noise_w, 1); + int x3_idx = get_idx(j, dst_w, noise_w, 2); + int u = calc_u(j, dst_w, noise_w); + + for (int i = 0; i < noise_h; i++) { + + int x0 = dst[i*dst_w + x0_idx]; + int x1 = dst[i*dst_w + x1_idx]; + int x2 = dst[i*dst_w + x2_idx]; + int x3 = dst[i*dst_w + x3_idx]; + + tmp[i*dst_w + j] = filter(x0, x1, x2, x3, u); + } + } + + /* vertical interpolation */ + for (int i = dst_h - 1; i >= 0; i--) { + + int y0_idx = get_idx(i, dst_h, noise_h, -1)*dst_w; + int y1_idx = get_idx(i, dst_h, noise_h, 0)*dst_w; + int y2_idx = get_idx(i, dst_h, noise_h, 1)*dst_w; + int y3_idx = get_idx(i, dst_h, noise_h, 2)*dst_w; + int u = calc_u(i, dst_h, noise_h); + + for (int j = 0; j < dst_w; j++) { + + int y0 = tmp[y0_idx + j]; + int y1 = tmp[y1_idx + j]; + int y2 = tmp[y2_idx + j]; + int y3 = tmp[y3_idx + j]; + + dst[i*dst_w + j] = filter(y0, y1, y2, y3, u); + } + } +} + + +/** + * Normalize buffer values to specified maximum + */ +static void normalize_buf(short dst[], int len, int amp) +{ + int min = 0x7ffffff, max = 0; + + for (int i = 0; i < len; i++) { + if (dst[i] < min) min = dst[i]; + if (dst[i] > max) max = dst[i]; + } + + if (max == min) return; + + for (int i = 0; i < len; i++) + dst[i] = (amp*(dst[i] - min))/(max - min); +} + + +/** + * Multiply buffer values with 24:8 fixpoint value + */ +static void multiply_buf(short dst[], int len, int factor) +{ + for (int i = 0; i < len; i++) + dst[i] = (dst[i]*factor)>>8; +} + + +/** + * Add each pair of values of two buffers + */ +static void add_bufs(short src1[], short src2[], short dst[], int len) +{ + for (int i = 0; i < len; i++) + dst[i] = src1[i] + src2[i]; +} + + +/** + * We combine (add) multiple low-frequency textures with one high-frequency + * texture to get nice shapes. + */ +static void brew_texture(short tmp[], short tmp2[], short dst[], int w, int h, + int lf_start, int lf_end, int lf_incr, int lf_mul, + int hf_val, int hf_mul) +{ + for (int i = lf_start; i < lf_end; i += lf_incr) { + gen_buf(tmp, i, i, tmp2, w, h); + multiply_buf(tmp2, w*h, (lf_mul - i)*32); + add_bufs(tmp2, dst, dst, w*h); + } + if (hf_val) { + gen_buf(tmp, hf_val, hf_val, tmp2, w, h); + multiply_buf(tmp2, w*h, hf_mul*32); + add_bufs(tmp2, dst, dst, w*h); + } + + /* normalize texture to use four bits */ + normalize_buf(dst, w*h, 15); +} + + +/*************************** + ** Color table generator ** + ***************************/ + +static inline int mix_channel(int value1, int value2, int alpha) +{ + return (value1*(255 - alpha) + value2*alpha)>>8; +} + + +/** + * Create 3D color table + */ +template +static void create_coltab(PT *dst, Color c0, Color c1, Color c2, Color bg) +{ + for (int i = 0; i < 16; i++) + for (int j = 0; j < 16; j++) + for (int k = 0; k < 16; k++) { + + int r = bg.r; + int g = bg.g; + int b = bg.b; + + r = mix_channel(r, c2.r, k*16); + g = mix_channel(g, c2.g, k*16); + b = mix_channel(b, c2.b, k*16); + + r = mix_channel(r, c1.r, j*16); + g = mix_channel(g, c1.g, j*16); + b = mix_channel(b, c1.b, j*16); + + r = mix_channel(r, c0.r, i*8); + g = mix_channel(g, c0.g, i*8); + b = mix_channel(b, c0.b, i*8); + + int v = (((i ^ j ^ k)<<1) & 0xff) + 128 + 64; + + r = (r + v)>>1; + g = (g + v)>>1; + b = (b + v)>>1; + +// r = g = b = min(255, 50 + ((i*j*128 + j*k*128 + k*i*128)>>8)); + + v = 180; + r = (v*r + (255 - v)*255)>>8; + g = (v*g + (255 - v)*255)>>8; + b = (v*b + (255 - v)*255)>>8; + + dst[(k<<8) + (j<<4) + i].rgba(r, g, b); + } +} + + +template +static void compose(PT *dst, int dst_w, int dst_h, int x_start, int x_end, + short src1[], int src1_y, + short src2[], int src2_y, + short src3[], int src3_y, int src_w, int src_h, + PT coltab[]) +{ + for (int k = 0; k <= x_end; k += src_w) { + + int x_offset = max(0, x_start - k); + int x_max = min(x_end - k, src_w - 1); + + for (int j = 0; j < dst_h; j++) { + + short *s1 = src1 + x_offset + ((src1_y + j)%src_h)*src_w; + short *s2 = src2 + x_offset + ((src2_y + j)%src_h)*src_w; + short *s3 = src3 + x_offset + ((src3_y + j)%src_h)*src_w; + PT *d = dst + x_offset + j*dst_w + k; + + for (int i = x_offset; i <= x_max; i++) + *d++ = coltab[*s1++ + *s2++ + *s3++]; + } + } +} + + +template +static void copy(PT *dst, int dst_w, int dst_h, int x_start, int x_end, + PT *src, int src_y, int src_w, int src_h) +{ + for (int k = 0; k <= x_end; k += src_w) { + + int x_offset = max(0, x_start - k); + int x_max = min(x_end - k, src_w - 1); + + for (int j = 0; j < dst_h; j++) { + + PT *s = src + x_offset + ((src_y + j)%src_h)*src_w; + PT *d = dst + x_offset + j*dst_w + k; + + if (x_max - x_offset >= 0) + memcpy(d, s, (x_max - x_offset + 1)*sizeof(PT)); + } + } +} + + +/***************** + ** Constructor ** + *****************/ + +template +Sky_texture::Sky_texture() +{ + /* create nice-looking textures */ + brew_texture(_tmp[0], _buf[0], _bufs[0][0], TW, TH, 3, 7, 1, 30, 30, 10); + brew_texture(_tmp[0], _buf[0], _bufs[1][0], TW, TH, 3, 16, 3, 50, 40, 30); + brew_texture(_tmp[0], _buf[0], _bufs[2][0], TW, TH, 5, 40, 11, 70, 0, 0); + + /* shift texture 1 to bits 4 to 7 */ + multiply_buf(_bufs[1][0], TW*TH, 16*256); + + /* shift texture 2 to bits 8 to 11 */ + multiply_buf(_bufs[2][0], TW*TH, 16*16*256); + + /* create color table */ + create_coltab(_coltab, Color(255, 255, 255), + Color( 0, 0, 0), + Color(255, 255, 255), + Color( 80, 88, 112)); + + /* create fallback texture */ + compose(_fallback[0], TW, TH, 0, TW - 1, + _bufs[0][0], 0, _bufs[1][0], 0, _bufs[2][0], 0, + TW, TH, _coltab); +} + + +/***************************************** + ** Implementation of Element interface ** + *****************************************/ + +template +void Sky_texture::draw(Canvas *c, int px, int py) +{ + PT *addr = static_cast(c->addr()); + + if (!addr) return; + + int cx1 = c->clip_x1(); + int cy1 = c->clip_y1(); + int cx2 = c->clip_x2(); + int cy2 = c->clip_y2(); + + int v = -py; + int y0 = cy1 + v; + int y1 = cy1 + (( (5*v)/16)%TH); + int y2 = cy1 + (((11*v)/16)%TH); + + addr += cy1*c->w(); + + if (Config::background_detail == 0) { + copy(addr, c->w(), cy2 - cy1 + 1, cx1, cx2, + _fallback[0], cy1 - py, TW, TH); + return; + } + + compose(addr, c->w(), cy2 - cy1 + 1, cx1, cx2, + _bufs[0][0], y0, _bufs[1][0], y1, _bufs[2][0], y2, + TW, TH, _coltab); +} + + +#include "canvas_rgb565.h" +template class Sky_texture; diff --git a/demo/src/app/scout/common/test.txt b/demo/src/app/scout/common/test.txt new file mode 100644 index 000000000..d9e491ae6 --- /dev/null +++ b/demo/src/app/scout/common/test.txt @@ -0,0 +1,11 @@ + + + Genode Demonstration + + Norman Feske + +[image setup] + +Introduction +############ + diff --git a/demo/src/app/scout/common/tick.cc b/demo/src/app/scout/common/tick.cc new file mode 100644 index 000000000..0228ada96 --- /dev/null +++ b/demo/src/app/scout/common/tick.cc @@ -0,0 +1,125 @@ +/* + * \brief Timed event scheduler + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "tick.h" +#include "printf.h" + +static Tick *head = 0; /* head of tick list */ +static Tick::time now = 0; /* recent time (updated by handle function) */ + +void Tick::_enqueue() +{ + /* do not enqueue twice */ + if (++_active > 1) { +// printf("enqueue twice? ticks scheduled=%d\n", ticks_scheduled()); + _active--; + return; + } + + /* if ticklist is empty add first element */ + if (!head) { + _next = 0; + head = this; + return; + } + + /* if deadline is smaller than any other deadline, put it on the head */ + if ((int)_deadline - (int)now < (int)head->_deadline - (int)now) { + _next = head; + head = this; + return; + } + + /* find list element with a higher deadline */ + Tick *curr = head; + while (curr->_next && ((int)curr->_next->_deadline - (int)now < (int)_deadline - (int)now)) + curr = curr->_next; + + /* if end of list is reached, append new element */ + if (curr->_next == 0) { + curr->_next = this; + return; + } + + /* insert element in middle of list */ + _next = curr->_next; + curr->_next = this; +} + + +void Tick::_dequeue() +{ + if (!head) return; + + if (head == this) { + head = _next; + return; + } + + /* find predecessor in tick queue */ + Tick *curr; + for (curr = head; curr && (curr->_next != this); curr = curr->_next); + + /* tick is not enqueued */ + if (!curr) return; + + /* skip us in tick queue */ + curr->_next = _next; + + _next = 0; +} + + +void Tick::schedule(time period) +{ + _period = period; + _deadline = now; /* first deadline is overdue */ + _enqueue(); +} + + +int Tick::ticks_scheduled() +{ + int num_ticks = 0; + printf("now=%d\n", (int)now); + for (Tick *curr = head; curr; curr = curr->_next, num_ticks++) + printf("ticks_scheduled:\n %d: curr=%p, deadline=%d\n", + (int)num_ticks, curr, (int)curr->_deadline); + return num_ticks; +} + + +void Tick::handle(time curr_time) +{ + Tick *curr; + now = curr_time; + + while ((curr = head) && ((int)head->_deadline - (int)now < 0)) { + + /* remove tick from head of the list */ + head = curr->_next; + + curr->_next = 0; + curr->_active--; + + /* do not reschedule if tick function returns 0 */ + if (!curr->on_tick()) continue; + + /* schedule next event */ + if (curr->_deadline == 0) + curr->_deadline = now; + + curr->_deadline += curr->_period; + curr->_enqueue(); + } +} diff --git a/demo/src/app/scout/common/widgets.cc b/demo/src/app/scout/common/widgets.cc new file mode 100644 index 000000000..339e5ad33 --- /dev/null +++ b/demo/src/app/scout/common/widgets.cc @@ -0,0 +1,474 @@ +/* + * \brief GUI elements + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "miscmath.h" +#include "widgets.h" + + +/************* + ** Docview ** + *************/ + +void Docview::format_fixed_width(int w) +{ + _min_w = _min_h = 0; + + if (_cont) { + _cont->format_fixed_width(w - 2*_padx - _right_pad); + _min_w = w; + _min_h = _voffset + _cont->min_h(); + } + + if (_bg) + _bg->geometry(0, 0, _min_w, _min_h); +} + + +void Docview::draw(Canvas *c, int x, int y) +{ + if (_bg) _bg->draw(c, _x + x, _y + y); + if (_cont) _cont->draw(c, _x + x, _y + y); +} + + +Element *Docview::find(int x, int y) +{ + if (!Element::find(x, y)) return 0; + Element *res = _cont ? _cont->find(x - _x, y - _y) : 0; + return res ? res : this; +} + + +void Docview::geometry(int x, int y, int w, int h) +{ + ::Element::geometry(x, y, w, h); + + if (_cont) _cont->geometry(_padx, _voffset, _cont->min_w(), h - _voffset); +} + + +/*********************** + ** Horizontal shadow ** + ***********************/ + +template +void Horizontal_shadow::draw(Canvas *c, int x, int y) +{ + PT *addr = static_cast(c->addr()); + + if (!addr) return; + + const int cx1 = c->clip_x1(); + const int cy1 = c->clip_y1(); + const int cx2 = c->clip_x2(); + const int cy2 = c->clip_y2(); + + x += _x; + y += _y; + int w = _w; + int h = _h; + + int curr_a = INTENSITY; + int step = _h ? (curr_a/_h) : 0; + + if (x < cx1) { + w -= cx1 - x; + x = cx1; + } + + if (y < cy1) { + h -= cy1 - y; + curr_a -= (cy1 - y)*step; + y = cy1; + } + + if (w > cx2 - x + 1) + w = cx2 - x + 1; + + if (h > cy2 - y + 1) + h = cy2 - y + 1; + + addr += c->w()*y + x; + + PT shadow_color(0,0,0); + + for (int j = 0; j < h; j++, addr += c->w()) { + + PT *d = addr; + + for (int i = 0; i < w; i++, d++) + *d = PT::mix(*d, shadow_color, curr_a); + + curr_a -= step; + } +} + + +/********** + ** Icon ** + **********/ + +template +Icon::Icon() +{ + memset(_pixel, 0, sizeof(_pixel)); + memset(_alpha, 0, sizeof(_alpha)); + memset(_shadow, 0, sizeof(_shadow)); + _icon_alpha = 255; +} + + +template +void Icon::rgba(unsigned char *src, int vshift, int shadow) +{ + /* convert rgba values to pixel type and alpha channel */ + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++, src += 4) { + _pixel[j][i].rgba(src[0], src[1], src[2]); + _alpha[j][i] = src[3]; + } + + /* handle special case of no shadow */ + if (shadow == 0) return; + + /* generate shadow shape from blurred alpha channel */ + for (int j = 1; j < H - 4; j++) + for (int i = 1; i < W - 2; i++) { + int v = 0; + for (int k = -1; k <= 1; k++) + for (int l = -1; l <=1; l++) + v += _alpha[(j + k + H)%H][(i + l + W)%W]; + + _shadow[j + 3][i] = v>>shadow; + } + + /* shift vertically */ + if (vshift > 0) + for (int j = H - 1; j >= vshift; j--) + for (int i = 0; i < W; i++) { + _pixel[j][i] = _pixel[j - vshift][i]; + _alpha[j][i] = _alpha[j - vshift][i]; + } + + /* apply shadow to pixels */ + PT shcol(0, 0, 0); + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) { + _pixel[j][i] = PT::mix(shcol, _pixel[j][i], _alpha[j][i]); + _alpha[j][i] = min(255, _alpha[j][i] + _shadow[j][i]); + } +} + + +static inline void blur(unsigned char *src, unsigned char *dst, int w, int h) +{ + const int kernel = 3; + int scale = (kernel*2 + 1)*(kernel*2 + 1); + + scale = (scale*210)>>8; + for (int j = kernel; j < h - kernel; j++) + for (int i = kernel; i < w - kernel; i++) { + int v = 0; + for (int k = -kernel; k <= kernel; k++) + for (int l = -kernel; l <= kernel; l++) + v += src[w*(j + k) + (i + l)]; + + dst[w*j + i] = min(v/scale, 255); + } +} + + +template +void Icon::glow(unsigned char *src, Color c) +{ + /* extract shape from alpha channel of rgba source image */ + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++, src += 4) + _alpha[j][i] = src[3] ? 255 : 0; + + for (int i = 0; i < 2; i++) { + blur(_alpha[0], _shadow[0], W, H); + blur(_shadow[0], _alpha[0], W, H); + } + + /* assign pixels and alpha */ + PT s(c.r, c.g, c.b); + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++, src += 4) + _pixel[j][i] = s; +} + + +/* + * An Icon has the following layout: + * + * P1---+--------+----+ + * | cs | hs | cs | top row + * +----P2-------+----+ + * | | | | + * | vs | | vs | mid row + * | | | | + * +----+--------P3---+ + * | cs | hs | cs | low row + * +------------------P4 + * + * cs ... corner slice + * hs ... horizontal slice + * vs ... vertical slice + */ + + +/** + * Copy pixel with alpha + */ +template +static inline void transfer_pixel(PT &src, int src_a, int alpha, PT *dst) +{ + if (src_a) { + int register a = (src_a * alpha)>>8; + if (a) *dst = PT::mix(*dst, src, a); + } +} + + +/** + * Draw corner slice + */ +template +static void draw_cslice(PT *src, unsigned char *src_a, int src_pitch, int alpha, + PT *dst, int dst_pitch, int w, int h) +{ + for (int j = 0; j < h; j++) { + + PT *s = src; + unsigned char *sa = src_a; + PT *d = dst; + + for (int i = 0; i < w; i++, s++, sa++, d++) + transfer_pixel(*s, *sa, alpha, d); + + src += src_pitch, src_a += src_pitch, dst += dst_pitch; + } +} + + +/** + * Draw horizontal slice + */ +template +static void draw_hslice(PT *src, unsigned char *src_a, int src_pitch, int alpha, + PT *dst, int dst_pitch, int w, int h) +{ + for (int j = 0; j < h; j++) { + + PT s = *src; + int sa = *src_a; + PT *d = dst; + + for (int i = 0; i < w; i++, d++) + transfer_pixel(s, sa, alpha, d); + + src += src_pitch, src_a += src_pitch, dst += dst_pitch; + } +} + + +/** + * Draw vertical slice + */ +template +static void draw_vslice(PT *src, unsigned char *src_a, int src_pitch, int alpha, + PT *dst, int dst_pitch, int w, int h) +{ + for (int i = 0; i < w; i++) { + + PT s = *src; + int sa = *src_a; + PT *d = dst; + + for (int j = 0; j < h; j++, d += dst_pitch) + transfer_pixel(s, sa, alpha, d); + + src += 1, src_a += 1, dst += 1; + } +} + + +/** + * Draw center slice + */ +template +static void draw_center(PT *src, unsigned char *src_a, int src_pitch, int alpha, + PT *dst, int dst_pitch, int w, int h) +{ + PT s = *src; + int sa = *src_a; + + for (int j = 0; j < h; j++, dst += dst_pitch) { + + PT *d = dst; + + for (int i = 0; i < w; i++, d++) + transfer_pixel(s, sa, alpha, d); + } +} + + +/** + * Clip rectangle against clipping region + * + * The out parameters are the resulting x/y offsets and the + * visible width and height. + * + * \return 1 if rectangle intersects with clipping region, + * 0 otherwise + */ +static inline int clip(int px1, int py1, int px2, int py2, + int cx1, int cy1, int cx2, int cy2, + int *out_x, int *out_y, int *out_w, int *out_h) +{ + /* determine intersection of rectangle and clipping region */ + int x1 = max(px1, cx1); + int y1 = max(py1, cy1); + int x2 = min(px2, cx2); + int y2 = min(py2, cy2); + + *out_w = x2 - x1 + 1; + *out_h = y2 - y1 + 1; + *out_x = x1 - px1; + *out_y = y1 - py1; + + return (*out_w > 0) && (*out_h > 0); +} + + +template +void Icon::draw(Canvas *c, int x, int y) +{ + PT *addr = static_cast(c->addr()); + + if (!addr || (_icon_alpha == 0)) return; + + const int cx1 = c->clip_x1(); + const int cy1 = c->clip_y1(); + const int cx2 = c->clip_x2(); + const int cy2 = c->clip_y2(); + + /* determine point positions */ + const int x1 = x + _x; + const int y1 = y + _y; + const int x4 = x1 + _w - 1; + const int y4 = y1 + _h - 1; + const int x2 = x1 + W/2; + const int y2 = y1 + H/2; + const int x3 = max(x4 - W/2, x2); + const int y3 = max(y4 - H/2, y2); + + const int tx1 = 0; + const int ty1 = 0; + const int tx4 = W - 1; + const int ty4 = H - 1; + const int tx2 = W/2; + const int ty2 = H/2; + const int tx3 = max(tx4 - W/2, tx2); + const int ty3 = max(ty4 - H/2, ty2); + + PT *src = _pixel[0] + W*ty1; + unsigned char *src_a = _alpha[0] + W*ty1; + int dx, dy, w, h; + + /* + * top row + */ + + if (clip(x1, y1, x2 - 1, y2 - 1, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_cslice(src + tx1 + dy*W + dx, src_a + tx1 + dy*W + dx, W, _icon_alpha, + addr + (y1 + dy)*c->w() + x1 + dx, c->w(), w, h); + + if (clip(x2, y1, x3 - 1, y2 - 1, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_hslice(src + tx2 + dy*W + dx, src_a + tx2 + dy*W + dx, W, _icon_alpha, + addr + (y1 + dy)*c->w() + x2 + dx, c->w(), w, h); + + if (clip(x3, y1, x4, y2 - 1, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_cslice(src + tx3 + dy*W + dx, src_a + tx3 + dy*W + dx, W, _icon_alpha, + addr + (y1 + dy)*c->w() + x3 + dx, c->w(), w, h); + + /* + * mid row + */ + + src = _pixel[0] + W*ty2; + src_a = _alpha[0] + W*ty2; + + if (clip(x1, y2, x2 - 1, y3 - 1, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_vslice(src + tx1 + dx, src_a + tx1 + dx, W, _icon_alpha, + addr + (y2 + dy)*c->w() + x1 + dx, c->w(), w, h); + + if (clip(x2, y2, x3 - 1, y3 - 1, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_center(src + tx2, src_a + tx2, W, _icon_alpha, + addr + (y2 + dy)*c->w() + x2 + dx, c->w(), w, h); + + if (clip(x3, y2, x4, y3 - 1, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_vslice(src + tx3 + dx, src_a + tx3 + dx, W, _icon_alpha, + addr + (y2 + dy)*c->w() + x3 + dx, c->w(), w, h); + + /* + * low row + */ + + src = _pixel[0] + W*ty3; + src_a = _alpha[0] + W*ty3; + + if (clip(x1, y3, x2 - 1, y4, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_cslice(src + tx1 + dy*W + dx, src_a + tx1 + dy*W + dx, W, _icon_alpha, + addr + (y3 + dy)*c->w() + x1 + dx, c->w(), w, h); + + if (clip(x2, y3, x3 - 1, y4, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_hslice(src + tx2 + dy*W + dx, src_a + tx2 + dy*W + dx, W, _icon_alpha, + addr + (y3 + dy)*c->w() + x2 + dx, c->w(), w, h); + + if (clip(x3, y3, x4, y4, cx1, cy1, cx2, cy2, &dx, &dy, &w, &h)) + draw_cslice(src + tx3 + dy*W + dx, src_a + tx3 + dy*W + dx, W, _icon_alpha, + addr + (y3 + dy)*c->w() + x3 + dx, c->w(), w, h); +} + + +template +Element *Icon::find(int x, int y) +{ + if (!Element::find(x, y)) return 0; + + x -= _x; + y -= _y; + + /* check icon boundaries (the height is flexible) */ + if ((x < 0) || (x >= W) || (y < 0) || (y >= _h)) return 0; + + /* upper part of the icon */ + if (y <= H/2) return _alpha[y][x] ? this : 0; + + /* lower part of the icon */ + if (y > _h - H/2) return _alpha[y - _h + H][x] ? this : 0; + + /* middle part of the icon */ + if (_alpha[H/2][x]) return this; + + return 0; +} + +#include "canvas_rgb565.h" +template class Horizontal_shadow; +template class Horizontal_shadow; +template class Icon; +template class Icon; +template class Icon; diff --git a/demo/src/app/scout/data/about.rgba b/demo/src/app/scout/data/about.rgba new file mode 100644 index 0000000000000000000000000000000000000000..223731a8b61e12f6d8359c8f7168d5078b952070 GIT binary patch literal 16384 zcmZP=1*0J_8UmvsFd71*Aut*OqaiRF0;3@?8UmvsK!XtQi^yT{2ufrKh{|L0i^vx6 z4oMgF3`&u3^G^_U_KD?p@CfIybq!{+bq%J0?E_*yId!_Fr^b zTf-8{7{U_DI6@OjokJ5!XNJU=oDGgGdK4I4@Yp{x_mOW{)z8Qmoxib}9L?0spqVpJ{Q)(DO;!31K6G~e{ z6G|_JCX_x3i7$N^99R4xD5mg!KvdoX-|(ylULolZJOYy+xcbIFaPo?}Z|4^Fz}h+J zfrW$bBQsmCd&XAo8x74}9Q92d7!8L=7zD=^GX%#K%Y?+2EC@{~eHfNd_AoS|^g(ER z>3yjGAK>u+1DO9U9enSb*?K)Nwsd=BXzqMc-_+4Z+rXMp&%}W~kw~U3fiVRPfid|! zL9s=>A#o-5L*q*ygeH`N>5%x62f?vL4+3Kf9{5M*KJX38df*kD_P{+L>4A%H+yh6? zD7gPE?EM~?+IZbJwsd=7VCMWt&&1)FuCbkkzKH{aj*%^yuA{9spRh~@8<$|lfT+Cq zpqPSNAisvh72gN>J0z|c?DwFU!Uq9S`49Xea_;+tW^1Y~h2T*rEqPv4!`8VhSDvMCU*7j{ui_kG(_EA9@5P-*fd#c;xII z^Vq>7;*qUu$O9|KfCuJwKKD(nJsucYxPttDU)R|Fp^l-=Wi1112WPYOr??TV>-MblK9u|DljPRXvPDd(2R#Zp&9FZ!!lI@19L%n1nh5g!qCi_ zLC463LC?fqP1o3dx1Nc^Lr~o7nb<$jF|>W4rEm2}L(gKlhK@O}hORlf_5tO)_{1_e zd&ela_{N=d^Gg7=X+ZV*eb1njhn_*HCq0AHEIor#7(7GL@w!t(*Nj0$(}Y1?$23Jv z+vKjgj@biEeai=0238>Yk%q4MDs>$*K6RbJ<$p)dNCrpGNFyh&s0+^CF`)L`16RNJ z2X1}|kKOzeTRnnOnB4=C@cJDjsj6+lpsHoepsHn(q@rnbS4my}fugG314T8x2g(|T zkJPkGR;g*5@~LT?fRqm!49dfHZefP@?&0ShJR%-CdPUuL@``@o>>YF6#V6L&*(a7L zw}Skxrez|js%f+ugQcg*WPfkgbDD&v0$i_K{!Nxhr z(8eYBqOEHPs2%^r-aY&&C_L=l!`1BF!wEVCIVju4CAh%WHKf4C zIXK1AF@V>~F#xY~RJBYP)U-?lRJDvdR5T3lDybVhP*l~u5Ar)G?qw9zA4toqJ(QMH zEtir}W|Wp4T>i)FIRcWf~EUM(sHU7q-0g%C1jPD zC1sUynmJS?l+_IwRJ4p4lr;?Hl{E}!DXSYi0Oxmjn%BB7tEllnMqVA{_p?%Rs_{~? zDy&k&B;6~i8!#xV8^|lG8!Q6(8&t0Lqnp|rf(b!j=(PANH6NflK?21!|^p^~i$x>H_Ri$Pvl%Ro*^ z>!h5L)~FIQt(hIgD~gLtr!nMnhmU1V%$( YGz3ONU^E0qLtr!nMnhmU1n3w703%AYQvd(} literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/backward.rgba b/demo/src/app/scout/data/backward.rgba new file mode 100644 index 0000000000000000000000000000000000000000..03a602281c0d6f44ef34f7e89105943bfb806cdb GIT binary patch literal 16384 zcmZP=1*0J_8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFbG1x**k{8!#|P1!6QPz*wT%| z!rpffxQaebaPv!GaQ9DQa`lThbM%awZD8hPWoF|=AIk^O7WbfJ22cNF29LlLagTtc zVt4<01tf>u77Gdq5I{n|~scM_{tKXK?CbuaNY+-k})}y+bn|c!#9l_YO&aVC5KaSzcM& zT1C@{wvM5*>8^h946eR$!fyVFIUa$@C%l4FAA$Vq6Pj_~CoJ;;I1EBF9+=vAU6GPg zb&yliq_YcYW1WLX1cRMhD3haSl!=Q^>})sxgu5Pr$q&7PQz3C4n(@E~6#pO`lK#NR z((S6GjIy(gf;w#+L?=@%9Re64lJXd=odWr7T|<){JR*-fd&fL-^^1St9*}h3Gbr@| zD9?k^066YL(jS1t^i1rpO3ElXOUtX#$)(h{#?;1>L0#X1!Q9T<$kNe&o{dZJEqnLy zhfZG64_tiX?z{OXKJW-k2KyIO=D{#XTtnCVvZRc%wX~cn^&LiI6ZK4-7>q1j7!1su zc#SOGVohy44_VmzJ+gKVdSK@kdf&k_@`1B=%mYvw0QuEDFzJCuV9EoJpcF9e;v4rs zRmPfa)DZK(wt+Q+s)jLxj*+ddo{7T@Lvxp#Ce|L0%tHga@E-fMJlFqN*-9|4YfL&^WfJXS}+O8H1X(DYu5MMWnWY^+8=@ zyGI6QP7jPN-R_&(cs;PN_kCdP6bP!@pN$``CMl`uGsr1wF(|7WXsK!%Pf*t}yP>IX`AEmm_JN*>!+k?@mj}jH z?hnjty&qUQ_<_T~#wFx|omVbv5-vb#1^~(~{iZ+rm zgWLad%GwNaN?JS$Dmr0GYWn+CGz=fAX`4LI&^5oWWnlF{*VyiXzNzB_LkpJ&Ce|L{ zFaY__$|>N1jdKt0HiE(fgmsPWA4te3T@{y7aF&o(q|Bi-&@CyW#2_J~$RQ=G>@O{^ zwnIiih%0Vo}S^Mcz0Q*c^GBb3*NiztGNlS}}$@Ghg%U%>z7-@a3~e9i8rgx%NKhF8!l1k&EwA=K zR6_QOsD!MYsKmhgUr1DvK}b|mQb<&)MOakony{GkBM~u~2O{D!_eCXSABaiHJrD23w$S*Pq>JMZUG#-G$22@Uf(ulmW4ye3%prEP?$DnkhprZ3Y zLR#s8sJQGEQE^#2QE^$)Ba8+*ghV761Vtp+g+wI%g+!&c3X4iT5Eheu2o3{r*$1K$ zav0$NN*j_g%Ah;~4GYx=pt1rI9_pYn1Bx}lVFL~yMa>5wJtE>Vmqo;7tVP6SXy6<2 zj1(4;Vh|RTVh|FMR1^}G>Jk={z5-4Q;xeH4x(^EjNZwF*ATF&4PY;r^uy9ZT=M`yr zXxflhdmtmP4#5iQ;Bo|%hJ?kWFAIxF+kx^Pd9J06E>IW}4=8*_IEy#k(@CV?u04*C}=>ZWIO5n5(O7F-Rq(?|p z>aviil%3EZ^*=25K=C6iD#ar#Ditd%CcRGtQYJhQ5tjv*jmUWdzw zL}5{>+u(W<6b@pLZ~%n`1cT~LObn6}6qdLwAS7-hAS@0udPvcrJRu}1#VaH#l`JeO zbzE3f>JhZfa34}%KJX6MMZ}~Z2ndT`6A%(}6A%&`QvODEEU4@f z5|LyQ5|K0!5|x@QBr0`BNL1<}I2=T!?hA`bKY(K~PvDMBKWM}x-TRu1)?7aiGuhrEcH-OSo{*dpr|FkpeW3k zAx(qIF+m{-P#fAArXm(LLw3mghV9ogTn(9CQvLOBz~DsK*X9)Kx9b! z8`<$f!jcR^BH|1}!V;o_!V>v{!V)J0g(V&d3QIf?6qdLTr9tcm0zzV7{|}e=M-B%8 zA#nx)A#o-FA#o!CVexqa!s2%YgvCK=9#rn#7Z4VIzz^}i?Qn{Jg|JP@IKdP|TcPP;4>3 zpx9l0LD5J2f}$7s_=U}eyZ=GqARr{l0IKH%gv3Po1w{+^1w~Ks35Z?g=&cQ{JUI07vJ!k*` literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/cover.rgba b/demo/src/app/scout/data/cover.rgba new file mode 100644 index 000000000..230004765 --- /dev/null +++ b/demo/src/app/scout/data/cover.rgba @@ -0,0 +1 @@ +«³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫³Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²ÁÖ«³ÁÖ«³ÁÖ«³ÁÖ¬´ÂÖ¬´ÂÖ¬´ÂÖ¬´ÂÖ¬´ÂÖ¬´ÂÖ«³ÁÖ«³ÁÖ«³ÁÖ«²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת²À׫²ÁÖ«²ÁÖ¬³ÂÖ¬³ÂÖª²Àצ­ºÙŸ¦³Û™Ÿ«Ý“˜£ß• ß‘–¡ß“™¤Þ™Ÿ«ÝŸ¦²Û¥¬ºÙª±À׬´ÃÖ¬³ÂÖ«²ÁÖ«²ÁÖ«²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Áת±À׫²ÁÖ¬³ÂÖ«²Áפª¸Ù’—¢ßz~†äcfkçQRVéCDFè:;<ç678æ344å445å678å;<=çCCEèPRUèbejç{~†ä’—¢ß¤ª¸Ú«²Â׬³ÂÖ«²ÁÖª±À׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫±Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖ¬³ÂÖ¦­»Ù‘—¢ßqtzåOPSè566æ+++à+**Ú222Õ<<<ÑFFFÏMMMÍRQQÌQQQÌMMMÍFFFÏ<<<Ñ222Õ***Ú***à667æOQTèqu{æ’˜£ß§®¼Ø¬³ÂÖ«²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖ¬³ÂÖ¦­»ÙŠŽ˜á]_cé899æ(''Þ***Õ:::ÎPPPÈfffÄ{{{ÀŽŽŽ½¼¨¨¨º­­­º­­­º§§§º¼ŽŽŽ½{{{ÀfffÄPPPÈ:::Î***Õ'''Þ99:æ_ae芎˜á¦­»Ù¬³ÂÖ«²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖª±Àב–¡à_afè555å$$$Ú---ÐDDDÇ^^^Ázzz»˜˜˜·¶¶¶´ÑÑѱäää²ïïï¶ôôô¹÷÷÷»öö÷»ôôô¹ïïï¶äää²ÑÑѱ¶¶¶³˜˜˜·zzz»^^^ÁDDDÇ---Ð$$$Ú455å_bfè‘–¡à©°¿×«²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖ¬³ÂÖ ¦´Ûsv}æ?@Aç###Û)))Ï@@@ÅZZ[½yyy·œœœ²¾¾¾®ÞÞÞªôôô¬þþþ³ÿÿÿ¿ÿÿÿÉÿÿÿÐÿÿÿÔÿÿÿÔÿÿÿÐÿÿÿÉÿÿÿ¿þþþ³ôôô¬ÝÝݪ¾¾¾®œœœ²yyy·ZZ[½@@@Å)))Ï###Û>?@çsv|æ §´Ú¬³ÂÖ«²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖ«²ÁÖ’—¢ßZ\_é,,,á"""Ò444ÇLLM½iii¶‹‹‹°¬¬¬«ÌÌ̦êêê¥ûûû­þþþ½ÿÿÿÍÿÿÿÚÿÿÿäÿÿÿêÿÿÿíÿÿÿíÿÿÿêÿÿÿäÿÿÿÚÿÿÿÍþþþ½ûûû­êêê¥ÌÌ̦¬¬¬«‹‹‹°iii¶LLM½444Ç"""Ò--,áZ\_铘£ß«²ÂÖ«²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖª±ÀׇŒ•âLMNé%%%Û&&&Ì;;;ÁSSS¸qqq°ª­­­¥ÏÏÏ¡ñññ þþþ¬ÿÿÿ½ÿÿÿÍÿÿÿÚÿÿÿäÿÿÿìÿÿÿñÿÿÿóÿÿÿóÿÿÿñÿÿÿìÿÿÿäÿÿÿÚÿÿÿÍÿÿÿ½þþþ¬ñññ ÏÏÏ¡­­­¥ªqqq°SSS¸;;;Á&&&Ì%%%ÛMMO鈖⪱À׫²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖ©°¿×ƒ‡äFFGç$$#×)))È>>>½UUU´qqq¬ŠŠŠ¦©©©¡ÍÍÍððð›þþþ¤ÿÿÿ´ÿÿÿÂÿÿÿÎÿÿÿÙÿÿÿáÿÿÿçÿÿÿëÿÿÿíÿÿÿíÿÿÿëÿÿÿçÿÿÿáÿÿÿÙÿÿÿÎÿÿÿÂÿÿÿ´þþþ¤ððð›ÍÍÍ©©©¡ŠŠŠ¦qqq¬UUU´>>>½)))È###×EEF炆Žãª±¿×«²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖª±À×…äEEFç###Õ***Æ>>>ºTTT±kkk©ƒƒƒ£¢¢¢žÄÄÄšæææ–üüü™ÿÿÿ¦ÿÿÿ²þþÿ¾þþÿÈþþÿÐþþÿ×þþÿÜþþÿßþþÿâþþÿâþþÿßþþÿÜþþÿ×þþÿÐþþÿÈþþÿ¾ÿÿÿ²ÿÿÿ¦üüü™æææ–ÄÄÄš¢¢¢žƒƒƒ£kkk©TTT±>>>º***Æ###ÕDDE悆㪱À׫²ÁÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²ÁÖ«²Á׈Œ–âDEFç"""Õ***Å<<<¹PPP¯ccc§zzz¡˜˜˜œ¶¶¶—ÕÕÕ“ñññþþþ–þþÿ¢þþÿ­þþÿ·þþÿ¿þþÿÆýþÿÌýþÿÐýþÿÓýýÿÔýýÿÔýþÿÓýþÿÐýþÿÌþþÿÆþþÿ¿þþÿ·þþÿ­þþÿ¢þþþ–ñññÕÕÕ“¶¶¶—˜˜˜œzzz¡ccc§PPP¯<<<¹***Å"""ÕEEF燋•⫲Á׫²ÁÖ«²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׬³ÂÖ“˜£ÞKLNé"!!×(((Æ999¹KKK¯\\\§rrr ŒŒŒš§§§–ÂÂÂ’ÜÝÝŽõõöŒþþÿ“ýþÿžýýÿ§ýýÿ¯ýýÿ¶ýýÿ¼ýýÿÀýýÿÄýþÿÆýþÿÇýþÿÇýþÿÆýýÿÄýýÿÀýýÿ¼ýýÿ¶ýýÿ¯ýýÿ§ýþÿžþþÿ“õõöŒÜÝÝŽÂÂÂ’§§§–ŒŒŒšrrr \\\§KKK¯999¹(((Æ""!×KLM铘¤ß¬³ÂÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׫²ÁÖ¡§´Û[]`é$$$Û$$$È666ºFFF¯VVV§iiiŸ€€€š˜˜—•°°°‘ÉÊÊãääŠøùú‰üýÿüýÿ™ýýÿ ýýÿ¦üüÿ«üüÿ°üýÿ³üýÿµüþÿ·üþÿ¸üþÿ¸üþÿ·üýÿµüýÿ³üüÿ°üüÿ«ýýÿ¦ýýÿ üýÿ™üýÿøùú‰ãääŠÉÊʰ°°‘˜˜—•€€€šiiiŸVVV§FFF¯666º$$$È$$$ÛZ\_é ¦´Ú«²ÂÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²ÁÖª±¿×sv}æ,,,á Ì111½AAA±OOO§``` uuušŠŠŠ”¡¡¡ºº»ŒÓÓÔ‰ééë†ùúû…üýÿ‹üüÿ‘ýüÿ–ýýÿ›ýýÿŸýüÿ¢ýýÿ¥ýýÿ§ýþÿ¨ýþÿ©ýþÿ©ýþÿ¨ýýÿ§ýýÿ¥ýüÿ¢ýýÿŸýýÿ›ýüÿ–üüÿ‘üýÿ‹ùúû…ééë†ÓÓÔ‰ºº»Œ¡¡¡ŠŠŠ”uuuš``` OOO§AAA±111½ Ì+++áru|ç©°¿×«²ÁÖ«²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Á׫²Áת±À׬³ÂÖ”Ÿß>>?çÒ,,,Á;;;´III©XXX¡kkkš}}}•’’’¬­®ŒÃÄÆ‰ÕÖØ†åæèƒôõöûüþƒüýÿ‡ýýÿŒýýÿýýÿ“ýýÿ–ýüÿ˜ýýÿšýýÿ›ýþÿ›ýþÿ›ýýÿ›ýýÿšýüÿ˜ýýÿ–ýýÿ“ýýÿýýÿŒüýÿ‡ûüþƒôõöåæèƒÕÖØ†ÃÄÆ‰¬­®Œ’’’}}}•kkkšXXX¡III©;;;´,,,ÁÒ>>?生ଳÂÖª±À׫²Á׫²Á׫²Á׫²Á׫²Á׫²À׫²À׫²À׫²À׫²À׫²ÁÖ¦¬ºÙ^`dè Û%%%Ç666¸CCC¬QQQ£aaaœrrr–ƒƒƒ‘šš›Œ³´µ‰ÃÄÆ…ÑÒÔƒßàá€ìíî~÷øú}üüþýýÿƒýýÿ†ýýÿ‰ýýÿ‹ýüÿüüÿŽüýÿüýÿüýÿüýÿüüÿŽýüÿýýÿ‹ýýÿ‰ýýÿ†ýýÿƒüüþ÷øú}ìíî~ßàá€ÑÒÔƒÃÄÆ…³´µ‰šš›Œƒƒƒ‘rrr–aaaœQQQ£CCC¬666¸%%%Ç Û^`d覬»Ù«²ÁÖ«²À׫²À׫²À׫²À׫²À׫²À׫²À׫²À׫²Àת±À׬³ÂÖˆ–á223åÏ///½===°JJJ¦YYYžiii—xxx’‰‰Š¡¡¢‰´´¶†ÀÁÃÍÎÏ€ÙÚÛ~äåæ|ïðñ{øùúzüüþ{ýýÿ}ýýÿ€üüÿ‚üüÿ„üýÿ…ýýÿ…ýýÿ†ýýÿ†ýýÿ…üýÿ…üüÿ„üüÿ‚ýýÿ€ýýÿ}üüþ{øùúzïðñ{äåæ|ÙÚÛ~ÍÎÏ€ÀÁô´¶†¡¡¢‰‰‰Šxxx’iii—YYYžJJJ¦===°///½Ï333剎—ᬳÂÖª±À׫²À׫²À׫²À׫²À׫²¿×«²¿×«²¿×«²¿×«²ÀÖ§®»Ø^`dèÚ&&&Å777¶DDDªRRR¡```šnnn“|}}ŽŠ¥¥§†²³µƒ¾¾À€ÈÉË~ÓÔÕ|ÝÞßzæçèyïïñwö÷øvûûýwûüýyüýþyýýÿyýþÿyþþÿzþþÿzþþÿzþþÿzýþÿyýýÿyüýþyûüýyûûýwö÷øvïïñwæçèyÝÞßzÓÔÕ|ÈÉË~¾¾À€²³µƒ¥¥§†Š|}}Žnnn“```šRRR¡DDDª777¶&&&ÅÚ^`dè§®»Ø«²ÀÖ«²¿×«²¿×«²¿×«²¿×«²À׫²À׫²Àת±¿×¬³ÁÖ’—¢ß789æÐ///½===°JJJ¥XXXfff–srr‹”•–‡¦§¨„±±³»»½~ÄÅÇ|ÎÎÐzÖרyÞßàwæçèvîîðuðñòuòóôt÷÷øsúúúrûûüsüüýsüýýsüýýsüüýsûûüsúúúr÷÷øsòóôtðñòuîîðuæçèvÞßàwÖרyÎÎÐzÄÅÇ|»»½~±±³¦§¨„”•–‡‹srrfff–XXXJJJ¥===°///½Ð788æ’—¢ß¬³ÁÖª±¿×«²À׫²À׫²À׫²À׫²À׫²À׫²ÀÖ«²À×qt{æ!!!Þ$$$Ç555·BBB«PPP¡^^^™jjj“vvv†††‰˜™š…¦§¨‚¯°±¸¹º}ÁÁÃ{ÉÉËyÐÑÒwØØÚvßßáuááâtääåsèéérìííqïïðqñññqñòòpññòpññòpñòòpñññqïïðqìííqèéérääåsááâtßßáuØØÚvÐÑÒwÉÉËyÁÁÃ{¸¹º}¯°±¦§¨‚˜™š…†††‰vvvjjj“^^^™PPP¡BBB«555·$$$Ç! Þory櫲À׫²ÀÖ«²À׫²À׫²Àת²Àת²Àת²À׫²ÀÖ¢©¶ÚLNPèÕ+++Á:::²GGG§UUUžbbb–nnnzzz‹‰ŠŠ‡››ƒ¦¦¨€®®°~¶¶¸{½¾¿yÅÅÇwÌÌÍvÒÓÔuÔÔÕsרØrÜÜÝrààáqâââpââãpãããpäääoåååoåååoäääoãããpââãpâââpààáqÜÜÝrרØrÔÔÕsÒÓÔuÌÌÍvÅÅÇw½¾¿y¶¶¸{®®°~¦¦¨€››ƒ‰ŠŠ‡zzz‹nnnbbb–UUUžGGG§:::²+++ÁÕMNQ袩¶Ú«²ÀÖª²Àת²Àת²Àת²Àת²Àת²À׫³ÁÖ– ß223æÎ000»>>>®LLL£ZZZ›fff”qqqŽ}}~‰Ž…œŸ‚¥¦§¬­¯|´´¶z»»½xÁÂÃvÈÉÊuÊÊËtÍÍÎrÒÒÒrÕÕÖqÖÖÖp×××pÙÙÙoÛÛÛoÜÜÜoÝÝÝoÝÝÝoÜÜÜoÛÛÛoÙÙÙo×××pÖÖÖpÕÕÖqÒÒÒrÍÍÎrÊÊËtÈÉÊuÁÂÃv»»½x´´¶z¬­¯|¥¦§œŸ‚Ž…}}~‰qqqŽfff”ZZZ›LLL£>>>®000»Î234æ– ß«³ÁÖª²Àת²Àת²Àת²Àת²Àת²ÀÖ«³ÁÖy~…ä###à###È555·BBBªPPP ]]]˜iii’sssŒ€ˆ‘„žŸ€¤¥§~«¬®{²³´y¸¹ºw¿¿ÁuÂÃÄtÄÄÅsÈÉÉrÌÌÍqÌÌÌpÎÎÎoÑÑÑoÓÓÓnÕÕÕnÖÖÖnÖÖÖnÖÖÖnÖÖÖnÕÕÕnÓÓÓnÑÑÑoÎÎÎoÌÌÌpÌÌÍqÈÉÉrÄÄÅsÂÃÄt¿¿Áu¸¹ºw²³´y«¬®{¤¥§~žŸ€‘„€ˆsssŒiii’]]]˜PPP BBBª555·###È##"ày}„䫳ÂÖª²Àת²Àת²Àת²Àת²Àת²ÀÖ©±¿×bdiçÚ(((Ä888´EEE¨TTTž```–kkkuuu‹ƒƒ„†’’“ƒž ¤¥¦}««­z±±³x··¸v¼½¾u¼½½sÀÁÁrÅÅÅqÅÅÆpÆÆÆoÊÊÊoÌÌÌnÎÎÎnÐÐÐmÑÑÑmÑÑÑmÑÑÑmÑÑÑmÐÐÐmÎÎÎnÌÌÌnÊÊÊoÆÆÆoÅÅÆpÅÅÅqÀÁÁr¼½½s¼½¾u··¸v±±³x««­z¤¥¦}ž ’’“ƒƒƒ„†uuu‹kkk```–TTTžEEE¨888´(((ÄÚbej穱¿×ª²ÀÖª²Àת²Àת²Àת²Àת²ÀÖ¤¬¹ÙNPSèÕ+++À;;;±HHH¥VVVœccc•mmmwwx‰……†…””–‚žž ~¤¥¦|ª«¬y°°±wµ¶·v¸¸¹t¹ººs¾¾¿qÀÀÁpÀÀÀpÃÃÃoÆÆÆnÈÈÈnÊÊÊmÌÌÌmÍÍÍmÍÍÍmÍÍÍmÍÍÍmÌÌÌmÊÊÊmÈÈÈnÆÆÆnÃÃÃoÀÀÀpÀÀÁp¾¾¿q¹ººs¸¸¹tµ¶·v°°±wª«¬y¤¥¦|žž ~””–‚……†…wwx‰mmmccc•VVVœHHH¥;;;±+++ÀÕNPS褬¹Ùª²ÀÖª²Àת²Àת²Àת²Àת²ÁÖž¤±Û?@BèÑ...½===¯KKK¤YYY›ddd“nnnŽyzz‰†‡‡„••–žŸ ~¤¤¦{©ª«y¯¯±w´´¶uµµ¶s¸¸¹r¼¼½q¼¼¼p¾¾¾oÁÁÁnÃÃÃnÆÆÆmÇÇÇmÉÉÉlÊÊÊlÊÊÊmÊÊÊmÊÊÊlÉÉÉlÇÇÇmÆÆÆmÃÃÃnÁÁÁn¾¾¾o¼¼¼p¼¼½q¸¸¹rµµ¶s´´¶u¯¯±w©ª«y¤¤¦{žŸ ~••–†‡‡„yzz‰nnnŽddd“YYY›KKK¤===¯...½Ñ?@B螥±Ûª²ÀÖª²Àת²Àת²Àת²À׫³ÁÖ—©Ý667æÏ000¼>>>­MMM¢ZZZšfff“pppzz{ˆˆˆ‰„–—˜€žŸ }£¤¦{©ª«x®¯°v³³´u³³´s··¸rºººq¹¹¹p¼¼¼o¿¿¿nÁÁÁmÄÄÄmÅÅÅlÇÇÇmÇÇÇlÈÈÈlÈÈÈlÇÇÇlÇÇÇmÅÅÅlÄÄÄmÁÁÁm¿¿¿n¼¼¼o¹¹¹pºººq··¸r³³´s³³´u®¯°v©ª«x£¤¦{žŸ }–—˜€ˆˆ‰„zz{ˆpppfff“ZZZšMMM¢>>>­000¼Ï668ç—©Ý«³ÁÖª²Àת²Àת²Àת²À׫³ÁÖ‘—¢ß011æÍ222º???¬NNN¡[[[™ggg’pppŒ{{|‡ˆ‰Šƒ——™€žŸ }£¤¦z©©«x®¯°v±±²t²²³s¶¶·r¸¸¹p¸¸¸o»»»o¾¾¾nÀÀÀmÂÂÂmÄÄÄmÅÅÅlÆÆÆlÆÆÆkÆÆÆkÆÆÆlÅÅÅlÄÄÄmÂÂÂmÀÀÀm¾¾¾n»»»o¸¸¸o¸¸¹p¶¶·r²²³s±±²t®¯°v©©«x£¤¦zžŸ }——™€ˆ‰Šƒ{{|‡pppŒggg’[[[™NNN¡???¬222ºÍ011æ‘—¡ß«³ÁÖª²Àת²Àת±Àת±À׫³ÂÖŽ”žß---åÌ222º@@@¬NNN¡\\\™ggg’qqqŒ}}}‡‰‰Šƒ——™€žŸ }£¤¥z©©«x®®°v°±±t²²²s¶¶¶q···p···oºººo½½½nÀÀÀmÂÂÂmÃÃÃmÄÄÄlÅÅÅkÆÆÆlÆÆÆlÅÅÅkÄÄÄlÃÃÃmÂÂÂmÀÀÀm½½½nºººo···o···p¶¶¶q²²²s°±±t®®°v©©«x£¤¥zžŸ }——™€‰‰Šƒ}}}‡qqqŒggg’\\\™NNN¡@@@¬222ºÌ...唟߫³ÂÖª±Àת±Àת°Àת±À׫²ÂÖŽ“ß--.åÌ222º@@@¬NNN¡\\\™ggg’qqqŒ}}}‡‰‰Šƒ——™€žŸ }£¤¥z©©«x®®°v¯°±t±²²s¶¶¶q···p···oºººo½½½nÀÀÀmÂÂÂmÃÃÃmÄÄÄlÅÅÅkÆÆÆlÆÆÆlÅÅÅkÄÄÄlÃÃÃmÂÂÂmÀÀÀm½½½nºººo···o···p¶¶¶q±²²s¯°±t®®°v©©«x£¤¥zžŸ }——™€‰‰Šƒ}}}‡qqqŒggg’\\\™NNN¡@@@¬222ºÌ-..厓žà«²ÂÖª±Àת°Àת±Àת±À׫²ÁÖ‘–¡Þ001åÍ122º???¬NNN¡[[[™ggg’pppŒ{{|‡ˆ‰Šƒ–—™€žŸ }£¤¥z©©«x®®°v°±²t²²³s¶¶·r¸¹¹p¸¸¸o»»»o¾¾¾nÀÀÀmÂÂÂmÄÄÄmÅÅÅlÆÆÆlÆÆÆkÆÆÆkÆÆÆlÅÅÅlÄÄÄmÂÂÂmÀÀÀm¾¾¾n»»»o¸¸¸o¸¹¹p¶¶·r²²³s°±²t®®°v©©«x£¤¥zžŸ }–—™€ˆ‰Šƒ{{|‡pppŒggg’[[[™NNN¡???¬122ºÍ011å’—¢Þ«²ÁÖª±Àת±Àת±Àת±À׫²ÁÖ—©Ý567æÏ000¼>>>­MMM¢ZZZšfff“ooozzzˆˆˆ‰„––˜€žž }£¤¥{©ª«x®¯°v²²³u²³³s··¸rºº»q¹¹¹p¼¼¼o¿¿¿nÁÁÁmÃÃÃmÅÅÅlÆÆÆmÇÇÇlÈÈÈlÈÈÈlÇÇÇlÆÆÆmÅÅÅlÃÃÃmÁÁÁm¿¿¿n¼¼¼o¹¹¹pºº»q··¸r²³³s²²³u®¯°v©ª«x£¤¥{žž }––˜€ˆˆ‰„zzzˆooofff“ZZZšMMM¢>>>­000¼Ï678旨ݫ²ÁÖª±Àת±Àת±Àת±Àת±ÁÖ£°Û??AèÑ...½===¯KKK¤YYY›ddd“nnnŽyyy‰††‡„”•–žž ~£¤¥{©ª«y¯¯±w³´µu´´µs¸¸¹r¼¼½q¼¼½p½½½oÁÁÁnÃÃÃnÅÅÅmÇÇÇmÈÈÈlÉÉÉlÊÊÊmÊÊÊmÉÉÉlÈÈÈlÇÇÇmÅÅÅmÃÃÃnÁÁÁn½½½o¼¼½p¼¼½q¸¸¹r´´µs³´µu¯¯±w©ª«y£¤¥{žž ~”•–††‡„yyy‰nnnŽddd“YYY›KKK¤===¯...½Ñ?@B裰۪±ÁÖª±Àת±Àת±Àת±Àת±ÀÖ¤«¹ÙNORèÕ+++À;;;±HHH¥VVVœbbb•mmmwww‰…………“”•‚ž ~¤¤¦|ªª¬y¯°±wµµ¶v¶·¸t¹ººs¾¾¿qÁÁÂpÀÀÀpÃÃÃoÆÆÆnÈÈÈnÊÊÊmËËËmÌÌÌmÍÍÍmÍÍÍmÌÌÌmËËËmÊÊÊmÈÈÈnÆÆÆnÃÃÃoÀÀÀpÁÁÂp¾¾¿q¹ººs¶·¸tµµ¶v¯°±wªª¬y¤¤¦|ž ~“”•‚…………www‰mmmbbb•VVVœHHH¥;;;±+++ÀÕMOR褫¹Ùª±ÀÖª±Àת±Àת±Àת±Àת±ÀÖ©°¿×achçÚ(((Ä888´EEE¨TTTž```–kkkuuu‹‚ƒƒ†‘’“ƒžŸ¤¤¦}ª«¬z°±²x¶·¸vºº»u»¼¼sÀÀÁrÅÅÅqÆÆÇpÆÆÆoÉÉÉoËËËnÎÎÎnÏÏÏmÐÐÐmÑÑÑmÑÑÑmÐÐÐmÏÏÏmÎÎÎnËËËnÉÉÉoÆÆÆoÆÆÇpÅÅÅqÀÀÁr»¼¼sºº»u¶·¸v°±²xª«¬z¤¤¦}žŸ‘’“ƒ‚ƒƒ†uuu‹kkk```–TTTžEEE¨888´(((ÄÚ`chç©°¿×ª±ÀÖª±Àת±Àת±Àת±Àת±ÀÖ«²ÂÖy}…ä"""à###È455·BBBªPPP ]]]˜hhh’rssŒ€€€ˆ„Ÿ€¤¤¦~««­{±²³y¸¸ºw½¾¿uÀÀÁtÃÃÄsÈÈÈrÌÌÍqÌÍÍpÍÍÍoÐÐÐoÒÒÒnÔÔÔnÕÕÕnÖÖÖnÖÖÖnÕÕÕnÔÔÔnÒÒÒnÐÐÐoÍÍÍoÌÍÍpÌÌÍqÈÈÈrÃÃÄsÀÀÁt½¾¿u¸¸ºw±²³y««­{¤¤¦~Ÿ€„€€€ˆrssŒhhh’]]]˜PPP BBBª455·###È"""àx|ƒä«²ÁÖª±ÀÖª±Àת±Àת±Àת±Àת±À׫²ÁÖ• ß112æÎ000»>>>®LLL£YYY›eee”pppŽ|}}‰ŒŒ…›œž‚¤¥¦«¬®|³³µzºº¼xÀÁÂvÅÆÇuÇÈÈtËÌÌrÐÑÑrÕÕÕq×××pÖÖÖpØØØoÙÙÙoÛÛÛoÛÛÛoÛÛÛoÛÛÛoÙÙÙoØØØoÖÖÖp×××pÕÕÕqÐÑÑrËÌÌrÇÈÈtÅÆÇuÀÁÂvºº¼x³³µz«¬®|¤¥¦›œž‚ŒŒ…|}}‰pppŽeee”YYY›LLL£>>>®000»Î112æ• ß«²ÁÖª±Àת±Àת±Àת±Àת±Àת±Àת±ÀÖ¡§µÚKLOéÕ+++Á:::²GGG§UUUžaaa–mmmyyy‹ˆˆ‰‡™š›ƒ¤¥§€¬­®~´µ¶{¼½¾yÃÄÅwÊÊÌvÏÏÐuÑÒÒsÖÖ×rÚÛÛrßßàqáââpâââpâââpâââpãããoãããoâââpâââpâââpáââpßßàqÚÛÛrÖÖ×rÑÒÒsÏÏÐuÊÊÌvÃÄÅw¼½¾y´µ¶{¬­®~¤¥§€™š›ƒˆˆ‰‡yyy‹mmmaaa–UUUžGGG§:::²+++ÁÕLMP袨¶Úª±ÀÖª±Àת±Àת±Àת±Àת±Àת±Àת±ÀÖª±À×nqxæ!! Þ$$$Ç555·AAA«PPP¡]]]™iii“uuu„„…‰–—˜…¤¥¦‚­®¯¶·¸}¿¿Á{ÇÇÉyÎÏÐwÕÖ×vÚÛÜuÝÝÞtáââsææçréêêqííîqïïðqððñqïïïpïïïpððñqïïðqííîqéêêqææçráââsÝÝÞtÚÛÜuÕÖ×vÎÏÐwÇÇÉy¿¿Á{¶·¸}­®¯¤¥¦‚–—˜…„„…‰uuuiii“]]]™PPP¡AAA«555·$$$Ç Þnqw檱Àת±ÀÖª±Àת±Àת±Àת±Àת±Àת±Àש°¿×«²ÁÖ• ß667çÐ../½<<<°III¥XXXeee–qqq‹’’“‡¤¤¦„®¯°¸¹º~ÂÂÄ|ËËÍzÓÔÕyÛÜÝwâãävèéêuëìítïïðsóóôsöö÷røùùrúúûrúûûrúûûrúúûrøùùröö÷róóôsïïðsëìítèéêuâãävÛÜÝwÓÔÕyËËÍzÂÂÄ|¸¹º~®¯°¤¤¦„’’“‡‹qqqeee–XXXIII¥<<<°...½Ð567æ• ß«²ÁÖª±¿×ª±Àת±Àת±Àת±Àת±Àת±Àת±Àת±ÀÖ¦­»Ø]_cèÚ&&&Å666¶CCCªQQQ¡___šlll“zzzŽŒŒŠ¡¢£†¯¯±ƒºº¼€ÅÅÇ~ÏÐÑ|ÙÙÛzââäyêêìwñòóv÷øùvúúüvûûývýýþvýýþvþþÿwþþÿwþþÿwþþÿwýýþvýýþvûûývúúüv÷øùvñòóvêêìwââäyÙÙÛzÏÐÑ|ÅÅÇ~ºº¼€¯¯±ƒ¡¢£†ŒŒŠzzzŽlll“___šQQQ¡CCCª666¶&&&ÅÚ\^b覬»Øª±ÀÖª±Àת±Àת±Àת±Àת±Àת±Àת±Àת±Àש°À׫²ÁÖˆ—á233åÏ///½<<<°III¦XXXžggg—uuu’……†œž‰¯°±†»¼¾ƒÈÈÊ€ÓÔÕ~Þßá|ééë{òóôyúúüyüüþzýýÿ|ýýÿ~ýýÿýýÿ€ýýÿ€ýýÿ€ýýÿ€ýýÿ€ýýÿ€ýýÿýýÿ~ýýÿ|üüþzúúüyòóôyééë{Þßá|ÓÔÕ~ÈÈÊ€»¼¾ƒ¯°±†œž‰……†uuu’ggg—XXXžIII¦<<<°///½Ï223凌•᫲ÁÖ©°Àת±Àת±Àת±Àת±Àת±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±ÀÖ¥«¹Ù]_cèÛ%%%Ç555¸AAA¬PPP£___œooo–‘””•Œ¬­¯‰¼½¿…ÊËÌƒØØÚ€äåæ~ðñò}ùúü|üýþ~ýýÿýýÿ„ýýÿ†üüÿˆüüÿ‰üüÿŠüüÿ‹üüÿ‹üüÿŠüüÿ‰üüÿˆýýÿ†ýýÿ„ýýÿüýþ~ùúü|ðñò}äåæ~ØØÚ€ÊË̃¼½¿…¬­¯‰””•Œ‘ooo–___œPPP£AAA¬555¸%%%Ç Û^`d襫¹Ùª±ÀÖª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×©°¿×«²ÀÖ”žß==>çÒ+++Á99:´GGG©VVV¡gggšxxx•‹‹‹¥¥¦Œ»¼¾‰ÌÍΆÛÜÞƒêëì÷øù€üýþ‚üýÿ†ýýÿŠýýÿýýÿýýÿ’ýýÿ“ýýÿ”ýýÿ•ýýÿ•ýýÿ”ýýÿ“ýýÿ’ýýÿýýÿýýÿŠüýÿ†üýþ‚÷øù€êëìÛÜÞƒÌÍΆ»¼¾‰¥¥¦Œ‹‹‹xxx•gggšVVV¡GGG©99:´+++ÁÒ==>甞૲ÀÖ©°¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿Ö©¯½×ru{æ+++á Ì000½===±LLL§]]] oooš‚‚‚”———¯¯°ŒÇÈɉÜÝ߆ïïñ„úûý„üýÿ‰üýÿŽýýÿ“ýýÿ–ýýÿšýýÿœýýÿžýýÿ ýýÿ ýýÿ ýýÿ ýýÿžýýÿœýýÿšýýÿ–ýýÿ“üýÿŽüýÿ‰úûý„ïïñ„ÜÝ߆ÇÈɉ¯¯°Œ———‚‚‚”oooš]]] LLL§===±000½ Ì***áqtz稯½×ª±¿Öª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×©°¾×ª±¿ÖŸ¥²ÛY[^é###Û$$$È444ºAAA¯QQQ§dddŸxxxš•¢¢£‘ºººÒÓÓŠêëìˆúúü‰üýÿüýÿ–üýÿœüüÿ¡üüÿ¥üüÿ¨üüÿªýüÿ¬ýüÿ¬ýüÿ¬ýüÿ¬üüÿªüüÿ¨üüÿ¥üüÿ¡üýÿœüýÿ–üýÿúúü‰êëìˆÒÓÓŠººº¢¢£‘•xxxšdddŸQQQ§AAA¯444º$$$È###ÛYZ^韥²Ûª±¿Ö©°¾×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×©°¾×«²ÀÖ“˜£ÞJKLè!!!×&&&Æ666¹DDE¯VVV§kkk š˜˜˜–°°°’ÈÈÈŽààà‹÷÷÷Šþþÿýýÿšýýÿ¢ýýÿ¨ýýÿ®ýýÿ²ýýÿ¶üýÿ¸üýÿ¹üýÿ¹üýÿ¸ýýÿ¶ýýÿ²ýýÿ®ýýÿ¨ýýÿ¢ýýÿšþþÿ÷÷÷Šààà‹ÈÈÈŽ°°°’˜˜˜–škkk VVV§DDE¯666¹&&&Æ!!!×JKMè’˜¢Þ«²ÀÖ©°¾×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿Öª±¿×†‹“âDDEç"!!Õ(((Å888¹GGG¯ZZZ§qqq¡ŠŠŠœ¤¤¤—½½½“×××ðððŽþþþ‘þþÿ›þþÿ¤þþÿ¬þþÿ³ýþÿ¸ýýÿ½ýýÿÀýýÿÁýýÿÁýýÿÀýýÿ½ýþÿ¸þþÿ³þþÿ¬þþÿ¤þþÿ›þþþ‘ðððŽ×××½½½“¤¤¤—ŠŠŠœqqq¡ZZZ§GGG¯888¹(((Å!!!ÕBCD燋”⪱ÀÖª±¿Öª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×©°¾×ª±¿Ö©°¾×…ãBCDç"""Õ''(Æ888ºIII±]]]©uuu£ž­­­šÉÉÉ–ååå“úúú“ÿÿÿœþþÿ¦þþÿ°þþÿ·þþÿ¾þþÿÂþþÿÆþþÿÈþþÿÈþþÿÆþþÿÂþþÿ¾þþÿ·þþÿ°þþÿ¦ÿÿÿœúúú“ååå“ÉÉÉ–­­­šžuuu£]]]©III±888º''(Æ"""ÕBBCç~‚Šä©°½×ª±¿Ö©°¾×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª°¾×ª±¿Ö¨¯½×…ãDDEç"""×&&&È777½HHH´]^^¬www¦”””¡²²²ÐÐЙííí—ýýý›ÿÿÿ¦ÿÿÿ±ÿÿÿºÿÿÿÁÿÿÿÇÿÿÿÊÿÿÿÌÿÿÿÌÿÿÿÊÿÿÿÇÿÿÿÁÿÿÿºÿÿÿ±ÿÿÿ¦ýýý›ííí—ÐÐЙ²²²”””¡www¦]^^¬HHH´777½&&&È"""×DDEç…ã©°½×ª±¿Öª°¾×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×ª±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©°¾×ª±¿Ö©°¾×ˆŒ•áKKMé###Û"""Ì333ÁEEE¸Z[[°tttª‘‘‘¥°°°¡ÏÏÏžëëë›üüü ÿÿÿ«ÿÿÿ¶ÿÿÿ¿ÿÿÿÅÿÿÿÊÿÿÿÌÿÿÿÌÿÿÿÊÿÿÿÅÿÿÿ¿ÿÿÿ¶ÿÿÿ«üüü ëëë›ÏÏÏž°°°¡‘‘‘¥tttªZ[[°EEE¸333Á"""Ì$$$ÛKLN醋”â©°¾×ª±¿Ö©°¾×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©°¾×©±¿Öª±¿×‘–¡ßY[^é,,,áÒ,,,Ç>>>½SSS¶kkk°‡‡‡«¤¤¤§Â£ßßß ôôô¢ýýý©ÿÿÿ²ÿÿÿ¹ÿÿÿ¿ÿÿÿÁÿÿÿÁÿÿÿ¿ÿÿÿ¹ÿÿÿ²ýýý©ôôô¢ßßߠ£¤¤¤§‡‡‡«kkk°SSS¶>>>½,,,ÇÒ,,+áY[^é’˜£ßª±¿Ö©±¿Ö©°¾×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨°¾×©±¿Öª²ÀÖŸ¦²Úru|æ>?@ç"""Û###Ï333ÅFFF½\\\·ttt²ŽŽŽ®¨¨¨ªÂÂÂ§ØØØ¥ééé¦óóó¨øøø«úúú­úúú­øøø«óóó¨ééé¦ØØØ¥Â§¨¨¨ªŽŽŽ®ttt²\\\·FFF½333Å###Ï""!Û=>?çru{柦²Úª²ÀÖ©±¿Ö¨°¾×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×¨±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×¨°¾×©±¿Ö¨°¾×”Ÿà^`dè555å"""Ú'''Ð666ÇHHHÁ[[[»ooo·ƒƒƒ´•••±¦¦¦¯´´´­¾¾¾¬ÃÃìÃÃì¾¾¾¬´´´­¦¦¦¯•••±ƒƒƒ´ooo·[[[»HHHÁ666Ç'''Ð#""Ú555å_ae莓žà¨°¾×©±¿Ö¨°¾×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×¨°¾×©±¿Öª²ÀÖ¤«¹Ù‰Ž˜á_aeè::;æ)))Þ*))Õ555ÎEEEÈVVVÄddeÀpqq½{{{¼‚‚‚ºƒƒƒºƒƒƒº‚‚‚º{{{¼pqq½ddeÀUUVÄEEEÈ555Î)))Õ(((Þ:;;æ^`d艎—᤬¹Ùª²ÀÖ©±¿Ö¨°¾×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×¨°¾×©±¿Öª²ÀÖ¥­»Ø‘—¡ßru|æQRUè;;<æ444à666Ú@??ÕLLLÑVVVÏ___ÍdddÌdddÌ___ÍVVVÏLLKÑ@@?Õ777Ú444à;<<æQSUèptzæ‘–¡ß¥­ºØª²ÀÖ©±¿Ö¨°¾×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×¨±¾×©±¿Öª²ÀÖ©±¿×¡¨µÚ– ß}‰äilqè]^bèVWYèVVXçXYZæZZ[åXYYåXXYæVWXçVWYè]_bèjmrç}‚‰ä‘—¡ß¢©¶Ú©±¿×ª²ÀÖ©±¿Ö¨±¾×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©±¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¾×©°¿Ö©±¿Öª²ÀÖª²ÀÖ¨°¾×¤«¸Ùž¥±Ûš ¬Ý–œ§ß”™¤ß“™£ß•›¦ßš¡­ÝŸ¥²Û¤«¸Ù¨°¾×ª²ÀÖª²ÀÖ©±¿Ö©°¿Ö¨°¾×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿Ö©°¿Ö©°¿Ö©°¿Ö©±ÀÖª±ÀÖª±ÀÖª±ÀÖª±ÀÖ©±ÀÖ©°¿Ö©°¿Ö©°¿Ö©°¿Ö©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©¯¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿×©°¿× \ No newline at end of file diff --git a/demo/src/app/scout/data/downarrow.rgba b/demo/src/app/scout/data/downarrow.rgba new file mode 100644 index 0000000000000000000000000000000000000000..1f6d42a7afe54648d625739c961be358e1097a91 GIT binary patch literal 4096 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 Z7!85Z5Eu=C(GVC7fzc2c4S_)r0ss?*`7;0j literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/index.rgba b/demo/src/app/scout/data/index.rgba new file mode 100644 index 0000000000000000000000000000000000000000..e5b863c7be8c8eabbbf9577c594e9a38f1dab02b GIT binary patch literal 16384 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 X1V%$(Gz3ONU^E0qLtr!nNDBb~o*(Yf literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/ior.map b/demo/src/app/scout/data/ior.map new file mode 100644 index 0000000000000000000000000000000000000000..4a4e0d9169cb30126aecef36ea3c832264704c95 GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/mono16.tff b/demo/src/app/scout/data/mono16.tff new file mode 100644 index 0000000000000000000000000000000000000000..811e1b622b8eccc59a552dbd2c1327d8045352c3 GIT binary patch literal 45832 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 literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/opened_icon.rgba b/demo/src/app/scout/data/opened_icon.rgba new file mode 100644 index 0000000000000000000000000000000000000000..26a0f90ca83636d6b44d2770ebe46229068c1e34 GIT binary patch literal 1024 zcmZP=;eefkD}|kd>j9bA8x(wGngP}e(htJy99-TcxFH3k2OSgX4y<}%X5n$iagg~i gb@*r;X5h6K-8?iiNYV>43u*>RIK$*dX-EhF05q^YXaE2J literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/pointer.rgba b/demo/src/app/scout/data/pointer.rgba new file mode 100644 index 0000000000000000000000000000000000000000..f030a72687ef0f1486b29304cd8016899e42e8ba GIT binary patch literal 4096 zcmXrD@yau@boJCXb>MgObZ5{rab%z;Ff@1eF|l%AVQl5zZEWf0WoqLoXlm`j0K)We zhmN5Qzk!)krY4M`cL{Ys4!H!_vCEupGA49#5_j4j<5j4WJ(jV;|a zncMkna`R8z6dYT$F)FoYQ$kMb=CtD8ExFax7c@-RkTGZdHTC6tp0X_2@sOcs=Mk#7 z#n{T7!Pv@O!o=ERy1AXtW;?ggjlr=+o6<}AHWk#*-dNGTbW=_Ds?7}(Hf-shvwun3 z^qqxet&0t^D<`uR)X!#!Nv)>H9j4Zv3^uNz3}&`o5$5*3pfmsugS6tFO|4V6Z=A5` z*v6^L&u*N&^wh?lc?UN&Pu{YnqJ8Pg+^T66iMeg2p$TOiG3oUTev!FkyTiiXm%+l` zSK89SZ?>&V$YvMcxQ&6)1sfYCY}mB+(7WxMPJG?5{^-XoOLsorG zza^!xYjs3&Wu0F{wzaEY0(V$K8H2q?1j+8OatvUwu=8fHb_z@cQ$v-M@Q*>JG#)8^uo41|3bM?@ze;>A=`*URB*1H>9 zr|sBSP(OQ9e0KB3pqRo<9)ZbQ96Ta6SvdyGFthcFG_r8zC&hk{J8fM<8EjocHEdl& zmN~k-YcR3)WH7by0$~d?8?V)%GRev@09-EFxrJ?X^o-i%8>HlqRL z25{Q*3(wx{8I;=U;u9<7=AX#m=oyV{B*|)ZRTD6h@mZ z9Rpg-?R=y_@n~x61v8Rl+R)sY!O+~9$H2^~-N4Lwi=ny87DEe{J|hbk0g(UP{1auI zePY`kJR(7L)+TG`ppBq51gH)(wef5*wsMmMg`<(BE6H|)H0YZ-G3c2%FzA~)y6T%c zuGcqp+@x>n7@%+J#9(0V3@RrW?A^m9Y+Qm{tQ-S2TiE+;GPCvGXlm^V3abr9mabkV zR_+Ys+7EIA$bLN&2LU}3`w4m`_OtX%>?QO}>_JitR!#v77WRG&mJa?>=5{_UrZ!%i zO{_gO8C$x6?9MQ6jFd70Bh5!Kf!aiuGf6addg;)R0%)b1$u=w_0LE-g(PR@z{85qQ|YJv*> zXJT^xFC=v6zk|ci|0N~=|M&I%|KHQ||9^4u|NoATfBy6HU;od@=mb@cLj7l82>Q>- zdE&p0&g=hPUjP4RW&H=6QCRr@e|q}=|2{td|7&Tz1gl|Sut(PXpMgQ-KO^I!{{jLh z{;R1y_-}6h<-fD@@Bdz2|NeV;`~|Z?Vjww?8jxBB26dPj{}~u!{xdOc{?Eg6@V}(w zx&O+_H~(vDKKQSz`vi=&v>t-RKyn~8OiWuqdSLp0F)&Q}&&aspKRf%*|Gc~h{tF8q z`!6nj>c6Dq888O1L1G{|kQzqD4c{0TmcaCXWnftHpMhcZe`e-Q|Jm8M|L5Y`{hyb2 z?|(kN{a_4YgTz2`AT`foEZ)PDz=!OXnuKO5Vc|D2o~{&RD0`p?6&1&l##kQhjgnRyw=?Kc=0 zCc)f)k%1xlDI?>OpRBA){&RD${4XG|{=cyB=KrFiTfi8^28nTVuLP-i#=x)yqz9(| zG6RF^ZAQjPui4p`{1+Bp_FqP3!+%A^ZU0qNc7id84H6R;UItQgi;;2Cc?Jeyn0^p_ zn}Nae5ev(bZ{p%h{%dNk|8H!(`@e<7K`;ifH8t0RhM76{uZT$f ne;JvT|7B%Y{T33M^pTS@`Y8j0DzaKa>=z6S!Y|SMiLM6#+}}R~ literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/sizer.rgba b/demo/src/app/scout/data/sizer.rgba new file mode 100644 index 0000000000000000000000000000000000000000..13b78a2ddc5b922108d991e403ec7d647c986644 GIT binary patch literal 4096 zcmZP=1*0J_GDCog-ri3z0CRJ5U624hFw|~VIXOA2w6rv*y?gf> zD<~)g(8F$ac6N|8tn%{mmVtqRc9D^hO48EOnyjp>t{@3oVyM5_6ciLJf`Wo0!6Lyu4LFK!9CXSeUYqkdU*Aib{Z)nVB002ZtxE>}O+R0~yUG zCnskMvO6?1R7F@=*il7A#ox}(&dtEUK$Vr1)e)qK+87q*a&mIkAiKlE!t85tQ#va+%|QQK~qzd>ou-{0RZC@4r(SXkHzWVeltjhlsqg_5bMskEY^ zBFO(P)U+RFx2&wJ4ajb2d^;&CD+gFxTf2koHZ(Mp5f&D<6%i5fXJus#p_ctHyFp7R#u+o=H|*Ey9ERU9F>%m{7p?w-8ncoys2S7QhbB`9TE}(O6OpITUlAT zo0*v@8yOkN2nq_?gY34kv2iyrFi@jL`h)r#RJK_7`T5y}hK7Rdb_RtxD84N%Efw|k z^<`mp+uGW?Sz1~u8yFZ+oc>_tnu3CY6)4O>{)VMox28FplD9l0et*56aCmGH!otFS z`6}ggV`-7Cua$=J0KuHRY*t(l)wEzcAJ@*DM7;=oYtWE4xIL(c7xP; z7#bR?lUn}3!W@*xU};TQSQwnvpkb~AN^77n2l?C5($W(Y=Af_u zkB^UCaBwiVOofFx$loA)K>|@G!Twwg$OJ#njYPN>Nc!g_V_+ zsPYeHw~UO8jkmYAePCc9sBCcn*==cQ=?;o-Jv}{8ItPWhAF{uNgoHrx?FUVNDxfqV zEG%p-BqRh%e?f%GA7s1H{2gFvX$i_>;Isy^2NdR@cmVku8Wwi)^78&r_kisd5)!fn zi5VFgd9kyz`{J`7mcK!H3}kmuP!OnWafJHY!_?GN85G}wf`U$&| zWDitK)x^X^T1ZI9Movx+6gQys2WtP|tp8yC2HEZF>uU?LTUb~a6uzMR?FtKX0RaI} z9s`M?ElL5I0YLADxxw$GR{ej|JUS1xY@6653)j;V_MMXuKm6g>Q zWDz>f%*Pft&pmzUQOWRJPIxrd2~38*}m z=HuhDm6eqR#WzSy)xf|&3M3{YBLfN_P(7q#XlN)UARu5RARqure}U+BL;1`sEG({` zo}StX2?^Tq@$tbiF)^M=Nl9we)z#85F)A1K!waCcGpt!g=Uzk`zLV`+ocz8f~c(@-wKR?#=Z)(>h|U%ni3{rdHw`}glhK7RZ-`R2`= zMVBsJiobdDX5hnz4 zxVV@F3l>x?Uc9(=>C&ZDD_5?pShj3g^}>Y<>y|88QnPgF(%R+Amsc-cy0iu=UY?kk z7{$-euSAR;*wipGg1pS2DladeeemGH`2YX^7ytYBul(=dzvUnr9|oyCc<^AHyu5rS z0|SFH)GTb);1dIdUxc{0xPO0tzviV&msGD_y{dZU$`!mAqz0rGqz9yzfq@~Mfq?;J z7CyVM$${cDiH(iTPD@Km!OP1_(#OY#C=5~y(gV`Vz`&3UO(WRsz$XT>KarJ{)mBAC zMZwY0QPSDjS%Mf0QVY@p(#ycWkjB8kz=h8u9CDzvodhyNRaF&izl)2Dgo}%dBmpb| zvLB>}m6g?wfq@~BO#gE-FfgPb+Yj|SC>-!%bo&#?v>)XEWGdJn4=pQj1T#)yxc#8G z1cfOn{s-v+xgDDRKxW~z2!{kn57_@8vm6{8Kw%C_e;^tkmH?>*>4Dmx1T80VSc6j- z6#uCpGeBm6%p}(TAhjSpAiWF>4CLC6lK((vl4(CE|AWF3r$=!}fa;P2P#yyL1!TXI zlaqv#lanL?j2`~v+7EJb5(^6pxcsrVx0iHubOhxsf*7P0{P~md=g*&P5dQu9ch2wMzq6qH%D%q75mTp5Eu1}jcG>jl(<^4ro?S9))~wRGbLW=M zo;@2xgZVRO&MccVXHMC)Y10aQeSM?Y+1b_bnN5NmsQw1!2T+;>r6mv>ls;e>nGaF} S;)C>%V8y7?(GVDUApij1B!&+F literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/test.png b/demo/src/app/scout/data/test.png new file mode 100644 index 0000000000000000000000000000000000000000..a00098feaafaa92d10f687ec27bdc080bc6880c9 GIT binary patch literal 45070 zcmeAS@N?(olHy`uVBq!ia0y~yV02($VEDkn#=yW(sJl0tfq{V~-O<;Pfnj4m_n$;o z1_lPUByV>YhW{YAVDIwD3=9mM1s;*b3=9k&VC;4>+m3;ufx*+oF{I+wo4@4^GRJEl zbl0z5wOf7p-MKgC2p>`sP;hKe;53-5&wF?O{>jlk zJuQXRdy&iJDJvW%E|NGR2xXPuubVmO1 zr-iJsdEo}rPcNOiXVSz}%he~m<^`?VbNPA1G;jA)3_7PLu_pVd1QzX z_6V*&E&XwaP2%>;u`dfOm?mjdx+?8rbu4)I{_g+9&FXyrqVCTR>JoJ|G;#3CoXB?L z%DpYMSD&u7TYADPYU0!qyXpQZmv8R$u@d%G4W9hcrAkTC-*1V~$_%x+GtWN%q`7L! z!-#21L4KPVZgA{~re&>N|9Z6t+$L%P6!pg!~SMT~`SHJo8!*xO|2YUs@7#Nw@eE#y!U^809ZXjX6o6NwW*d{RPN3y{& z0e*)bLxW>JnvpIFLQAJ;b{(3~Bymha!08XSV#^%9_QOw0EDs+%^`L9lGpl0~!6}h` zX+bkr-tfsONXvTWQesn;lvkCfFxA6%`qm?xJ_NBPL>^GI+@n~(LQ{roeP4)MmD9d} z_R}v{DYaM#I?Y(=B$=!bJd-J-kvoCgE!Xgn>urN$2FW}cOLzTW&(RQ-;8Lr}vtm&m zH^Z|19ExoQNgRq76goK+?1>zZAY&Mh$t-hA=q<;jaDw=X-ktnZB9 zS-v#mn@_B3-=6+-&P%f4bVv+e_y(XOE@y7`leO z5NnfV3h`K*&d^|zd*K56*3ceBxq?}29c$mdeEqd7*J0JZg|{|3HY>R`2rQCF7VVO- zyc1X6eEfaps{EDr)t*1CsL``Cv9zkpcys2)ypLni+Y)yvv!v$xNtP`Wh9Hc)~;7z67~X9}avtck<%N$2UvNKKk8Xy=jka5ZeP)J&9z+C;X=7*G%F< zLEtP4}J3`KZ5Sb}-G`06j*7hS%|^#0mi8zmh4nMBa0K9ag8uw#f?&SXVerY!W>nVeG_ll%;!NK}M;DOw`SbmkY1Y z3{H=GVSn%cn=enkC@k)Aiqy-}Wd5Ke)*`TuCv4kR^DXzo;$!!peOuCQdcpA2ibah4 zGxz=274akGfOGzBF4xypR~Ls}S-yyYRb=+%rHgh>`=>aW^9)yj*%n9MD^429VVKf1YZcF@>CB@47FGT_$FRbwF{n$#^X?)_;(i}Z2P!I^uKOTPh;*G*;Z|BpxdCPaZ zmA+Z|Q#hr;9>77j?k&I3ps!WcHoY%a1^K#GngA?%Ex=-wr_Wpc68htDCKc}>tkQ*0P!g;mnSu5 zTomfA$-DMcwWZ%-x}B87KFfbUmNjrJXPwogJVz}fMQqWbK%URCl7X3}SC#(EIlzz- z=@zoEfi+aQWY?UltIgK@sz|PWymfisoL^s8DleaRc8SqP?xM%1KTY$JY(sHKNkS@q<&zZcIp?@Lrfthzys_-KPTs*VW}T_rCw_nIsawP} zq4Y=O*|-_Y*^kXg%8`m+vu>MQzx=f0nKQlO977g5a4vOGI3fLE+nW7Zf0=vV_TKJ$ zXZuS&-GBbwwHjWQ{U2g=QW^GjtG?hiTbC1E8Sm_Sm{&<~@07q*yusQH>!;j2wt0<+ zz|NMZhn9VFh~a2sIO<^HkP^D6>uxes@VyF@C(&#vM?bkKwUD3PkUzAqQW^4#@6Fs$Jm96fQhYe?|tQF1~H!a?vskJlb zVDhwgix)ELE#t9sN;~lK_e>LqcYE)-+uh1On}6kOLQlw##6^A5W(OzS4riYqcOtaj zUov#@v7W{`(*=VB3>q~f4!qkoHTQk*6?eVYcd!4JU*0#TN^kz@^mFe|YMgGJX2ftm zHArwpM(o{B6TXwOO2nhKw2=lZ|VB!c=oIinm!6FR>?M+Nqa_hdY zZByTB+rKKTlrjyT{%PXfMGw;w@;B*mEM0m);B3Co^r&qP@^4HIR0bz7ocUQFWF=lv5SoJe@!#mDCm4FB3YY* z_p(H?0uy5+M_c8Z@2(41vCTOD+*e%Z;50dc%-z~ii1^&K`>-< zguS|Jpu@yaj%S}NPdvRcozMBM{|O0Z*RU&};+Yn^C%f6Imya*+pTo|< z=#|~-&89fzg)sl&pr0LPx_S%l8Yy!$oq0R+6;F|Z$*MyN>pYmu^>(~qn8oJNxJq@C zKxp@hB$g;g76mRwuN4bzU)ede2pCLkT)?5wrS+%7p+Vqm`HJ(2O3DEX7Vw_hv?i-r zDOkmGRmAqnyYpGKn9j1t*?)L{vZAQ!W%(|h=gO%J`_6(g+wPpBso%p}SLv|_ycTPi zyS|_OY|^E;XA^@SL>qB@{deQ*MZXzmtu=aBHcE1zdEd<=eCwFVcJHcZ+6N*USIAy3 z;aK;HN1^3PW!Kh(j-Dq@eG1=|W-uf$v^*-6T9pC2uo~w5{XerNu?SZdWzt)exzxN-v-S+jpMqZA;f8AoU zstEpUv-P0UecRe6UpAKC`dZXcdF`tYpK$d)u9)bxdj-9WDM#&JdqGYqZwGZ)Fy! z^gHylthMir<);}++CRAqnNKC@r!wqoRpMGy9D00Je~DRn9fR_l2Y+jiiI+cI@~~-- z>#YU1N-_oF6gZ;JACX$Co6Gci{=}Qh(_bwvt=YiZQWp5=r$UBS!vfyY#XVxZY=SI~ ztqRAUg~;?Y2$=Q<2#N$p^~`H?mel3h9IWvqQjXJSfs4YttJBy2>v=Mz29!z@Dmm^( ztEV#Tb5-J6l%D(jmj8CsEqlXPKS+{)6R|47^Z+-9mLN%95!${d97F*mN7?| z^~Cef9gK@x1U6>-UT-%zw(jrxxBngWW_!o(*c~Gldy1ik`-r0Dp6P3Myz!ULmM+gQ zUT~Am`bWycVlKNqtZIc11-jWE3jNNX&3-~Zeue$gzSLKXeUD1iO|!bEeL+v)99DF^|Tg*3ap!k55)gGBUfdI9Y|2 z@A04FQh46Gdf#-<^i!Wj;}bNQKQMJW7gdj&@y|?-6g)W>O35n6`hwYa1S(Oi8S*`kZstdxh8w& zo@4ABTvO8wn9dXwW*T_6LnqJ#zbcX+>(|HRam-iJ=3tNzz}s|k z`?oxOd5~W_H9FRr#%9~y4!K=o{o;f3oekTz2UT$FJ8Gbl%B3&hYSv!;wvOl7<$pSA z|D2*0Y~9bGn#H#1=-(}TEI4BK^i4X-Y(1P! z4YRCf=Y@-??ti}2_WtSTpI&Z%rw6f?L(wJb^3G?Eb-(JC8zr<^ZI}2FE1(;Y`y#hh zp+T}JOd@LALsJJA4nw!57ZVOhEN1mhkZZa2`Ra82E9t8v&rH9uiBYK`()x(RY@eha zLya!0oO2qhy3Rx`xVJA(KcHXhw#J2J#aGuaR?_BROmb0Jv2^uQJxlfUZ+o}Bckfy= z#XD>6@+piJ^O^+Jv~P#qyMFCPnZ6T;K5LzL%sn@bRqeCwB@V7NzxU$71AF6R4gxGK zhI)6CyVqYfNc@?s`)E%0nWQ-BmY-W488*9Rh`3&G=+H1ac-}goEqn#><;mhN5o*EUu+R^FAPv&S%M z!_RwZKlYwpq9Swk<5pLx2ThIwvmN^RbSG=p*xgdlS-|`1w&LdZezO<)oAz!!vCdFT zB4DFh#z}pXUK7onh$;6^I0nRQ%sc8{eDltokAIKL>-)RcybEG`Q0Wnzd->W`+12*W z28D*lUK}#Jb6X*Q^0#uny+QXgSlPaOzZPTdcVmz4OXbWRE$Nf5?eq|s#x?1~+mFG5 z633I$1U?zKc<8QA_1VjL;ts#OSEil z4Q&d)4U#YMNa|2S3h zf9A<0TV_q)c2>k;MgIBgvRj$mKAe>$y}oHP68>iU~{nICS>S)X~a z)sAgb;{w&4m4;s1d;R{|Qq@!jJx2kj3fI?N)vuLGZ%b})eIa@@$+UKQr`nDguUp=f$NW*% zYg%ycm1Rcru??1XO{>}6*G!G|YPjI%Y~tGZRXkjwQC=wh?fRD%3&a8fQd$GOB4dm1 zew!*CwrVCvYUGwF`qzY(tlai;*0*KfsvYNEpZ0NTulR4RCkNsbZWzv>DdcKyIN!zTu)WOM+W9@N0&V#J;1ml#SY<1Eu5OsCzT4TVVAiLW7 zUG}S-UC(599T#6Zg>i=m=dY%?Q~$7A9wR<%x$fo5A=5`YOIR*qt_It8JVM> zdpcQ{t>s!!xVV?}7US-<3Rn8m*_~deYd`t%c{A($tNbhfhNiHd+~mU=_+jnNq_gu| z4lSIv{bOC+l_0hUN-jdG=VzTWo&RlTt!_=^>d%#uTOaJGcm22E=Yky*mVMV_dl=-M zxN`YvpZDTS5BCUcxy*L{q7GX_tK*3?^WSnQe9*sa$DPse%X^~el=Dwl9)F%0xjXai zd=I~qpUdu??v6dh5Ytj(R(>xuHgs?3{Nso9*x$s&F>F|%7Ev@;v_O2O*p3hmsfLXW z1@~%oJ~zx*r#N4Y+3daei}s0mCwkwy@<^7lcPnhWuy<=#T~)n>#x$?#h6*b*bf%xa z=uw-JeSOwCYyGC@4`insF^Erib?Q~AY_II9`}`sM@3)>Q*OM}8O(=A@vR~uDHQ^rq|dWE?T&n?S2E#9$jjD7Lmof&g>yagRa0m+vVQ7iIv_B8!|KI+%1Y|$%|g)_OM2PR$vQRY@Zl}Vn^d2CociiP)Abus zizn2_f4apug(dOgK5>SYYp)X+cq0U-eEpp%;^gpcYUY+w9>W%ap!p|nzIb!--@SkF zJv@DT7M@`suB0xworTJge?s_P77L-)QEUP4af(Yzhqlk&!ao9ULj*MjbEI{dWcs^PI0 ztmRzunm^of(R?AAY581Z=@y%PwQt)Qw|F;q$5eLzzj(k$*Jz_clUVew`&;}qc{f}s zjX83DZs{&R*@T+iWgAS+MzBBAy80`?UR$77PVvCXl&q8&S1vquR&TGH@0q^rryXaL z!kWmzB`|sw5BOg}JJv;aP`tB>S>OvRK zKe)^2)NpB8k*4v_nj>sNHY<3T_0^wmx|XMRW!s$To23p47@T!*;eMU;JWFi)uDE5N zxBTK1zsP=JJy-nMS=~~z&!na>E(|r+aR@knOzmfkotc%zuj7rv%>75xl9;c3&dSYw zdv9a%@xQmX=l?HI5Ln^D;ppqjd#CRATlp97-~Uh55vzVDllNCu`Lt}qziZd7?Z30D zG}~kS%Vio|J7U(Y%3ing8Mm#i?e_hjPHEq-ez|mdosF%n?XveK!m}lB)TVsDzUug@ z_!n94cCFV6*(#{0!g$~|`yp4U``ez~wJghLlvkcV?d;n_D|Aj5)@>KEJ^rW2QvbN5 zK~~mvoY$q$Vu1Z zi>iNB|CXO8HQ(xhhOwv{Z~I|W^ZPZQ)${iKe0JVVX`)$+z^Uh-U!5uCI2alp8~X89 z_WIhNN5$i7CL4;yznG&J8u^Z4&D|}T!L_cgtQptVeLN886{sPSb$es-@&CKtY&!j} z^4rby{Tr*lzl&db`DM$tILWy(ca2SrOV_O2_VN~}zW5_o-~Oz-^17(!4E>HxH~xCh znN(`iboUnWcZ@@yCkw zJD<(koxlI@H+?g6^Y}IarLIL1r4oWp3pBV`m>6&DsVx30oxdk=_Q{lKx(p|+?>3f4 zEHXAWKF`_2FwtYl<%b13zD!|x_waC|!-93omp|XOZQHifmtTJQaplUD$W>QMmrr?? zo@Bh-ZMj=~de725dHH)ay6hU3s`7qHyVVqbuGOtk$LXAE=Iv$TRs26(goI~^tXlH> z+?T&rQ$2l|7&w|9+}Mo0zM;pmkam+vLwT6nmP_j>KG%k%#|kIvuw)q7#UicZ6{B|Bf% z9+q%6nmMNq6odb}7>kNr0DNmj>^V|J+YFquyrtGxt_CLPi z;n#Uv1lZRrW(F%*g|CnMJ8#>zZL7b%zyJS*@p+riXAd4cczD(_=9MK`pDuJd+uK>! zeVcmj*}3o6TVHSEboron&Avi#>WNKXKSxfJUOx5o)3ix5pRnxTzf3Xu^COA>mUDgl zpKRKhIsL&Wg8=2ZZP~u_*^V5ukI=I1>|gZC-puteZu8szTbD;eK#Sl;^40nnq8A@ zOI}Pqg#A3yNu|7C|`0!}GadA}0=-~6|Hi{TXk9=5~asp{Q#-*KI` zJ+bHKv)TMFuTRo436BikdGGP(Eldoq9525wT9ErHv#i0O{Narnfh+%`n3cV+vb`;g zud&fi>HV@aXqJM+d5**>Cyj%RtE>6H{PV8Q|F3Vn@{Gv+Iu)Un`#+zv-v4D{f6bHm zVe4WlZ55BCK3{%RV)Mg?4-fO(|M~F$^W^z|p45N){#}0gpEDt+BTg|qQJH`K`LEx< zzsuUy{NOwmVJ6gBvMWYUKVOV_g+j^Bn8tPM*5$4aU%!u$;nU>Flb3(~StG{KVCa^7 z^2Ub~Pfldtj43cR`CY>2fJD7{#=(WKK-owloCV3!ix*l)t0IAz2crd zd-nC5+uPoLZV_PLzfpnX(L(3;I&)LgQn&qWdF!o(xZ3sDHGVwaF=c+!v+y4|QV+A) zopxu}_$fT;{l}^h!4jkAdaTG^lBtD}K{cs!-(`3I|1XX#6t?d=d*s(wc>xB7_19nD z-&gzF-R@(jf6d=h+Ux%`oPNr6>myGx&&P_IH!qjZ|99>C{{MgV-{0H&yESH=hSL5u zOL-g^E_8WF+0Q+hQuOY{#l`36+tvP3U4LDA#i~_Vsx2K1t}i)Hy#Idv%a<>AuHC#@ z>8dq#)|^?htejU`g)UsAvGYaFmmfE-+<4uyan3XOFHLeP4h{F7e%Rc!ev7`}{l?1< zpNj0R-M;_(a)?@LhMu%}-kptge}C;>%Pz!nu<+ZPn|qi0&ApYn#Wix@u7$s%d^RvA z@~N=ua%KKczx6^Ud;X8qXVrBJw;rrii1=jD`q^O7?wdYkSudVk(69df{om;;FRsL` z3P>)Qq|tS$jaNGF$B*Oo|IVA=DL5?NbTEPWa#ql%&wU9!j?Nf z+k8ILyfGr@+7!a{c;tUw?o8bLY-U)vb@Qn?9$*@6Fs< zb8qQ)PP+W~(-lD>(|#Kd`)`{Q#RkcG0(QTZml)*=btrulwvME{C-O z_jYP|^5q8yKmY3MD|_A7-(O~FNaVG;1F8H6x_0f-mI`0L&psl$x^%&=D$}V?UDPH@ zr1!jB?C``m{hW+##fJxvXZol~b-GyD%K2aTwR-h$c4;Xoz3Ammof9~JF%D*J6Y*YA8IGke`+50x}V28mQIL5`KH*4<)axMA{l ziDbip{hKecRSL}dmCrga_R^P04m&I+-`H`i$Iwc8u5H1e2OsO-zJFV9GS}xbqw+)`FOg=u{{>wuCzsc$8&&`}VCYzW4e354&C0p^RQ~lq=<@SGH{xALV;$m#g1m$&^XQ`{nDu{`mO#yoC&(p$y-4?@3Qs8kGJoxcu@< zd0AQ9?aP;om7O%~4Gk|Y{~afh?4=`UZER)vE6Qy8+)umqZsofusPlF_?{3Mwe0-scsM7MwFXbgAEiDWTHpItI zJDak2>eZ>zGJftYhHi!jS#>998y)Cg$9rq$(XiYNB5&?Fe>-Tl`B?c9Fh=jNcz9v{PWe3d#k>#m6es9ZSw!Ygm>~&L@lM~_R2|j_q}!D zIBD0vK2Csz|AuS#{iq+hH_wM(VFvwiexB|AOIyWV!;Cl-qe6pAm)bq#S6mV>ZQqvf3}@M$W~c`&(%tSm z*^)muKwax(YO!(p@ALn*mDj)DuI8q8FGS=hhmXf3mE0Q}9xm&z`?5Ix?!MaJf9v+| zpJ$kMEy}dFKWLV9*_#`i?SJ1qzwgfxVgEnbCQ`D`w-@i;dsS9;wf&Byqrc9Fhqzoz z>R}0Rzpl_CP;7NK)Yy1(`=paMBy@r!w=uYIJZuYHZTD*JiU?+gU(O4&#iwvI9gmLO zsTUHyT`+OY65-CoRT`BHrnzZ3Icwe@Xk-RA16mG0G~2yJ@~tgr`{B2*udUs^dEL5o z$!Te61!ZN|Dgugs#?sNOMmQLwa;7I5_H+|351_9NJSkvu?5$SUq?s~v zYj&2$zc)YkZEmgB^s|!h1bYm#va_TAALOt5Q2*}6#>elova`b*t$t2>`Nw4P^uE46 zxqnyJ*L}S&SNUY(e>($%f*${DLD!R)Z``Omy((0CUqtln9kBu|+xhGa8v17_2&glh zSiIOd|9}y1hfXv@mD9p$px|`nC}eK0RNc!TQY zlP52pym(#s<&lQlPuCS$#wMNphVoPNHH|BKuYHyuIi z)%nj0EM&glDL#MS+{`R*%jJJRzNvT?S;+8#T6p^}ul^X&`fT&dH!eHOYE%U~qOVV2 zbu@Su82RE;#$ywoTML%`6vE^ky;=b!s0@BYSp?LnPaBnl36 zx+wKpm%aJ%iQVo)WBr>qZ|<}imPM?eyzZ;2Q0KDmMwEDPc>RO^K;cAy`ac#3~S!L z*`pm1Tx|Mc*#zHHGt!O*vjzIPv5}_9mV4S^o|)C>-8i zwokxD-+unCjjH*9$qUkcX&wLF|Nr-a69?pf)|%zqD0sbg`@LWDy))}uxz}Yiii(P! zRaRF1?e6C0_u}=XlV2>3&1gNeke5U8j7BntvP#z?!|i#UzcY3&=g8dIyzTHSuh@3g ztmd})`g48wzstY>f3ve$eV@7X+_z7=#VYpRJFqSHw%xal{k3miOPl5FX#f8C<52;F zV-wClpIrU@UG1BL?DBsueLiphKfy+B{?pyhzqjAowfp*i&+6Z={D0N_3wv7^@bzALkv9z+P+OcZYDnkjLS(nlndh|6u{r-Cu)MGCz zE6ej*8YEovZ^4=qGf&PusVsc4Ggcz`S!wm_P~&HF)Kx9>?dRX>h~&Ox_xi76`tkF2 zl~1lroFC-X`yyFGgsc9?VflYi7BcUmdX^oNU=9?K4Gj(b%FZuWqcr)XMDyR|%^NT4 z^{>7p|D~LnX#rO%hoXv?62l66gFB}4{Coa5@>R92_|Bv-$6fl$R*y;NpFVeEbDw9i z?f(t)bqxgdyi4rw&;R}RpH;Sf`#u=OwI|XZf8GaEh2DGw>)R&(7ffWEcs4aTIXHQDdXllb%%sE5 z3oEk>4=L&Jwp`mXZRziyJGhQ6%R0UwT+t!J#_HdxfA{|0Klg0Yu?-F_0=qyRmztxZ z;WdZ8S>5i~=c^%-wP=c3zpVAQNr_p(YUh2=%YOc~mp5+y_Q-aHWFAjGPkx>=>v?DK z#>%y{o=+3dbCXWF73`$3bmz*AE3a4cf2qrU@#TW>PM^GK`{mxgef#?T?)Urtmmlww ztq%_l-psY*{Q}q5$-%+FfBo%#KKcK%|NoEv|23bUoV;68S?wrrmzOu!d`(p8P3Emz zea*R=QdDky7LQu5oVZV{&8$Pf$!C&GV?@p~7acR>4IR&4PMVa;?$BbG8L{=%;&l!S zgt_)ldF8eEpS0E=-8QjHZ10pWzT|1o$~OP4rEUErr_5@$m+!0l`+U?UTUCB~@_O&y zz5d)y4ayTecB^>mt&?TY=+V#1$=Q>0cUNgSOS9wuCr_Sa+SX5COzsRxs!2(DvNgr3 z^4Q}~Epz$Or-qerFW6FlVVjSr&e1!j;gx1l1`n2RwH2IOQG0)5ZM^rg$;&pbnjT;G zGq?PH?f3tJt`~PpyzRHwbK)>HFh!-oD_1_2TYfn-cE|cz=hB5O45JkpvJxV%GHng?JN4ql1MOv9BBvdu$uihB zIuzgS5|!8Is(TS7&%a-2(M_=hp{9y{>$mS*w6AXU>F2A@Eel%w( zo&EXw_}=lbFg-kV>XcgTGQHh@4)*o+Wmi{K#nQa&rCF!wVQb9i6f=DkQ3Ve0lQa-@Ns^FFpRgC8kSNzs_D}4IxnMXezG7jiZKGnlJPwG$qWA?uK^t7U99PgPv zRPFtjdwW~$6J`0IC$5|4+(>xxdFG^D+?l#J@7=q%y#C+k`Tw7ShK%pdoGBR`wB^2h z*1K0xpHKGnU9ErgnbrDAb;7sLAFr>MX!`SbZGG5uk4cy94GllJnMHrd)n|_^W8m4o z^GZ!aw=iJ9oP-1l+!=OQ~>L)*fPqN*bEgDll|?^R9g(7T|p{Ao|(+G=k8 zc;>WgJqrX1uW)BDF)zq^FgsKEd&eS;lb6%|>}y^eS@=G^i1h(8L(AcZ$NXzw1^>Tw z=gywgeJkEGT^Ex`e(?F{s@Jby@2;w@j`vv_RQ#*TQh)iP#~1fXB){VRWo3BK&*6Z^ zQlE3DITTqIIkPy5TzMSqEf^uo@~1=SpH-y8iQWj4KQ%>hef@JKt6$yNmwaDBTB_Rf z-05Q}QMY#7mA3!$kiS0l_qVt494t&VSnL4vs-;_xjOSqZS_~rHfnUeE3vYa z>Fv{B-8pHRC_|@$g;dzuUoD3pXENCRDCGO_*fpG8GTFi9qIidk8B2E7wLq`SGb?JQ zWJ)>O%|Fk}!y{u+^5Vk#Pd{rGEnBwi^3$T9Ycq{|3=c?5_E5RBFYoRy+0xQd*T>z) zH_yCzaplFjn>XGVF25}KVTX+=i?HW)zk@tSSe+Jg7z8!Cb1*h>z7YOsudl)weTHSPqzvkHw>-@Lcz1WX{h!VA z|Gx3o-~Z>+;XgA9TMX8%=~)A6R6LK4+GY2msPferW|f}`?Rf|0XtVS#pWeCX$=o?I zwZ?P(^eVZUyF;gmG8{g8);x1(eN~G1m+#ND=U6VT|E}TWQSkfkT2Wr!aLc{-PWdPq zPCB2~%U)YmRrTgTBlCWFSy@{FuGTEjNV(k_zqiwF+OO!<-^a*MBrGi4Rv)m?tF9{V z*Oe1bPON^JQ{^M#vR~2=)t4q&FdGT}39Pg;}`&X}i{mH-n*X8@K-@Mrq*giirXLIkBzA1P5_%lF_nyuTn z=gY{-+KTkLx&NsBw6mrtt0pVH#Aa7_;Iu*uSw}~qni&ag&yUz)b%f;m{Sh#uf^T)EXw)4OKcGebfiunGS z@xx}LkJmF^3OrFh9A(79(e%J)`Q-G)7e5|;{2UOkMsNSO|DVSb zuLK`QiTDY{+bD5sAo&SEl z-Y?MUvg_GL@y10OHz%w6|N96UF@3erxjpX0^Usz0blabNXmx%#?^eOvX=8kT=%Kxp6!M9mdu@-7!GJGz3HQ8Y%4w6)=*CNzNMMzHtU&Qea0HgW@&U?3Js4f zJs(?s_v`nmQ>VWD*4n0f%QX2w59j?KU#HFJJ?m4(p*UmSJiEWQ@BjO@fA6nXtNTBx zDVJM3`B=3|J6p>+aT_*4TJ!r8gNa2y<;+yXV=gdNGxiOd-B6_S}l*sF{}gRYRAlC8H?q z*$0pO_lqw+d}zMHjB|qC)~#FLf@bCB=il1$GC6L&`I*d=Wsj436epiN^X~5M@8bUU zf4`Wsv9a+*`v$RaSjzM@+<%?V^{eTvUH|%x?Ui`CqQrd+`7lT>Ho9J%xo!b1+(6gZ}0^%#A<}ZA9bLmpOWlrxdDR8ve3nVjf zD3(1r@oxPbyV@@wU2U(3@6_RDP--c7{_O1krHd9m6ie{Y<7&`xI>%mPmb+5nh{W^F zH(xw?@Id~U-<%)5R&w%Bw-@h>d3RcW|DQhT{5>B}`}+D$S8nmpyf5X{!N<@4zW&$C z<@^8L%3lB1Xu9^d%X6QbwsduM-8!we`%Pc=x_iIWeb1hqp6qkwUxwd`CvPl#c;B?Y zY}`Aci?4B#_v#IYSx-d%pA{!jIDhLTT@Hr{F$%4-4un=qTD-p2{GwOl_z?-_#a%`w zCR=__4UhkQ5H$HA-s|Rm{(H?@rLINKSFK*R3zYJHB{@4Et_tX$IcpyCvhVGNsRacE z-#4GP`#m*wciG=Q{}eWc4;3|AeSPPaU%h5_SIM)_YeU2NKWnCV`P!Bj@33$e*ph3Y z%)+g#++2TS@9(Am&z^TKT6t;{!-B(a*TwA2y&XPR&j0hy8T-pTmp<1y^Q6Y;PxDKc zq=Yw5PR9Fw`EIYCzF93kSVLshy=}R-)z|O+cI$j-SlG5{TFk8iO)+}YEsLItffBu-V>ghwK7bU&^_32|Ia7>9e3Yd zsHszT4g7TZ^5y2{=H}ggbFIGqnrWQQ=jZFod-UHbm($dn>yKC33oqg-pt#v&vO4?hE;#*(n9h%^K?$AtS{ew3T>pSQfraJhv%dlIw z37%kao|bSzkjJvg;TXpXHYXkiCguqZjm?Z691aI06dMx+IM|%7Bxo!)GC0Lhz3Ra2 zUA%kNUS}v|WsFdK)g{p6qb4jZFK>VE{{8=M_4WVPfreE!Z{DnYGR0`x>8G2-isiYR z6f9);wx55_9U2y9W@>7>Ha9>2{-ry2YFu4iU5yo8=De?39<*|1?eA~4f35`k*FHWo z)A)GBizJ^T9!uVRdiHGH@1}#NN;wpno_$LGY-jd)`AR*xrq=ZBQ`9;b6nT0so_Tjy z|L^_BkJG=;xqZ0q6N!29zLkb*C8>+s(%Ff^W_1gYd@7|fI73x22zf>$-yfa2?s+a5Ik3UjuVgDdCmJLJ3(NjSpw zfca~^1f!_I5?7{TrAA9e=Sj@+hdE}r3Ww}o-8jMMgN6u`Q7c38iB`iyO%09*Bp5Ud zT-mrdmYm|_SaXN*+^efqUqvq4I&&~C4LO-&^f5%sRJ8xNx^Sn9$y~qV63LMP&!q~h zZ00qbf6o28X5WU}Z`VyxIoUhGYF*jB*Q+(Wz8&clKL7XM_x=A*U0m!QKPlDn{C5S3 zH7{~9U+zcv6+$p~dj! zzE`XM%dJ^^ciqzcXLkRs%&zrrl-u9+zJcvfjheBgp}~hIH%?COPZ#2xZ27cockOKR z{JQ(T=6AnLFD)(2W?;B|Rx<-X4k6VtEPTh4B4{CnQv|33SMNuCn_Z24*`I}h}@ zBsi%EC`%N1NbhVo8@2Fbbc>+D8vWx8E??P=oCMrBI=NgMede_m8}TwWcyO>a9q?Fw za1wjXT=VAjpVbtq79833`}6$&d!C=mUEDYgG%-;1Y-aktZN<;e#j8#BoP7Jx+iz}C z(zoBQSe?D|?!$sM#)c^6-OrVuvpL=q(aTBqY-j8-l$4jROT4~L{NHaU$A#UodOuw< zPCi|=;_a(n3lzHk1>KUDkA3RSxWF~5J4$)M+mJscMW@$XFy6B4(6+3+rm$)D0-bBP zuU*SyVNezBmrnh;)$>koV(Gzz4QF3}WfQxcAGdnrJ}=Hg5}P_RExuhkpZMaz<}4TS z;&@A*O)d$CRGsEKHwZF13o&Zk>U_0y$AoWxjpv&&ANlc>L(x%S!sVAqb^GI2uHjJA zX1=%Lw&t0jQvqh{EksXkDD<5w^JJM{!=!mkD;I@!i%qwCA9Ow8-|c9Fa&>j}^EUtg ze2%aG@v!~>uMZCo>wnzYk!AU7+wAald){DQEhkmev<=m+YZ{70Uck0x;8Z+z3zG}z6=I0pA?2)xBdUEvMy?f8Iva+OBuU>6^ z<()YHTdV2f4P5)5oRN^Jd*^OYSG7J$d4YqWn!t?W&Vo~p2@=XD794W9-x{o2XgP7J zb(B@x;fDd1i=xR^*=Unc|&b4+=ihY@;Ca)|xH}kYMUu{+8F^MPX=bh}z z#FliKDfrEwc>mb_;B2XjkMar^W*nTT>^|=YC_~&dzgO{C@bm{$A-?N*F*`qP(l;^N zw%32soWzJF^Yb->B)%rbg;h+N#^v}qL-a4GgqkCBccz!B*wm*kGUJDvj^|5w>UOU5@?@Oz%A@AO>Ab{pg$oj^*4|ax8ME-potXV` zy;2McV%@#+wpCwl255xbyl`QI*cOxC&z%v*$4q|o9y*v_$8d3B+~t=^ESsDgHtPxc z3G;Yfa6kJh+3Sn&)!UE#E|w_%IBTNo9mIITYPVJ3@2;;89e(fX3;2EO)vaA@=}XUB z9{#lQV~W;MBg>oKnun`y|NNoyB<*~|;!VsQm)E;3(fxkc$|d(;_JTa+_jh)FzF+@o z^8A{@q@+VrRNfR-9*T|4w!3+6-!J31_4{M5ewiS5ckhF|W!uG?UZwYJv@kcX%_%E; z#S(L<=p?hjrCU|AmoHkx9>QNRQCXTHXY0I4n_ND8OmbMy@?il>PieZ1fg7u5QHAru ztNT{1x_UF^laJa&gR~i^Q;b$7AMe{M!PV;2+uQr<=zf|E>x|aIl3H|*|#(S7Lo-{g1c^Gm$ zzs$BX-KFyF*6aJef1dyUPySlx8(P|{uYdaz^UwTY!QHZ+PL@_J=lqrRwV47Y@qd0Q ztXP{ap{zLBv$wB*{@tHp^Nw5VPB&rLsNk`6?%em~6*i?c|Kc?_mpg70^9_|YIAFfM zGMn{z%|55uUvEtpWlZMbX7cb$FI5n_sqwt%^2(h%8EbdP=uNk+{Pbk?!-5^_7A{Pb zozreP=lrsVC%EmKKVJUrb5~J9NKeqOLt>d}@C_C_Yu@wU)~78>a&i!GDX>r}u$udz zz{*iXH6Z(f!iQ>G%l_N1o!&KY&v9&Fc~zqQGRDl(+}zxFZqTQjE@5eQtxZ;fqAO-C zm^70){H#{M&Lg}(w|@LQ?bK=)j*Xz^;gfXx-#7282yvc!;<{tszNc+dwasg8zWuf} zlfPZ;Nkw&1_j7K?I_bZg<0cCw_oPJn_4V-VE70_xQ|HMHn!+-jJNNr1#tFqu@thwF z_sT{veW{3a_`R8n-SI$^gQ4O*2bE2YMq>UhiH+m-Dx5*|Mh7MM>8C zI@4ACV=rU(H#at)7v(q}@?)o~g`dHZEx`}ghHt30_b(7KIHthCWHHz8L&aVJ4i*Ig zj;7;}8I?FDoPHXpHFb*XteM594JDs1H}pF5Oi^}z?^z#Jb+zRRll@W!H9qcPs+-xI zP?pCcp!T4zq}gnF)y1z{)R;fNH#}DG=f}t8pj=>MU~Vq{^xI)y-?`h%%hvsD((n@H zU<{wS^GN4D*G2K^zaO0VBk)A~#EqRC3=MrfJa+o0e~QE`D)MB$@b#bYwuDtp$ zYWsn^E7q&K_gMOxOZNI|N_$-H&k;@HU=VanXqa$0K6Go?$>-@pmoKY-{ra-R%FxKD zDC6s^tJkad-t)@NOL!^HaEL|i*SEtLr^~aso|8_D?ooIv^pHhSfVuXP&0ga>FB{MP z4hobPZBPP*#LqthT&)W>EfQins9>SOexi%1xY*Fd(o*mEGWDcSXDUq0tqn!z%JlO0 z^IXm-ky?65GofqJVU9_btW8r^J~K#Wm^st<0mrpw<$&-{w{B%N-&)ZpZ2#+Gzn!^} zQPHgX`&O=eeC^!1dG_Kg2iX}!6muk_etu(|u>V?jvx3$}{zu=gHY?8X@!{D&Y1^5v zMVcp7c^&F4?gZbwP{8cOaX9H=`t$jZ?@ed9Uvul8&;s-G5%(XxSYtJ324gZ`eCpFz zK1P-k`b5=K=Fgldc|L9P=Im>0c82OMw)3CGt;N%RN@dx@3&r+rPu{R7tcew3shrNS z+=c1#Ezj3_OO$?y&Q;475&J8*f;GxDkY)L4*0}ZJHFo+t?#c!NpbmTa_gnh~SPU8NEQxw=!tnKVUcQh%%fY02 zg@z~#WhvSF!u{v@_9?4>F*~8kdmte8?lgzeuTvQgd|`RP`Y@a+p)9!hW>f#|bi)ge z7n;Otd1)SOQ8*@XSfYB*!hq_WtStHX?1GY#FD}l`&by!S&-pI#>wfU=`7ICcnODr0 zo6Nz}$>O$E*nL-6#;4ymGZeNxaK5Hgll5eJ0*{37ff)?U43S|9v(Gwd&239DV*TjC zbTp~Z#M-?5*|Q{#i9SwhlUXCf8iKkcm=!oK@i06U;b3AuBH&6kJa+y3IdZEVbIoTKS1Qg#B$kUcGtqrZM+H5k;PjClxJTu3z|L^FIC5S06Yc!xSc;bhMe+^5P3mPjCO>7cV4w{Cy6b zc_XtVDD%X#PZLg`oNz8haiR}MO^O!p%pAT?o0yDP12hy`87Fh}2=EwlF)w&kG{fpA z*OKMuS9~vF^sUItdv_+--}dX(9J6d65j~DYtM}G^U4ADof41zOd%jH*ZcfnLb6xSo zeaSlcQd|3;n@>Mo*?0N*=de?%+zu@p6?VoXer1lb`BM`7ovG=()x0Zw72a3>e5svb z<{&S8NMfl;M0B<9lBG}C>UQtjx9_iY`Ma8e#05bTR)3V1JT`t6?rM8>kIDf7rwP}e z2{Iiksfct4=W4nWFI)Gk+DlSnij&^__7E-J;^JZhOHQ}w-!1@q5`A6GhS$>E$-J4X*@4c7dioWSFj+HCzf^x&J|DJ0XZ+d_F^th^*%QtUc zrGLCkLc&R)pYQOClXsU*7B}<}Vk!P(>L|t$w71E&e520?=d|97t9XM>IRuKFc=~yQ zU%G;v|DiW`WX_yBcPK`O?PSWS3tvP|@vQ9&diz7_&zJLDtO^p(SePce$ulNYCFn0n zZ|DBD%T>V1z}VRM2dJ+;<+{+_y!_JO;NQOmohHTIyU2E@*NeIyw@jLrG9)wGOaI?{^L+Z68S*vUj_XtEa~Q&>=pEiGz@OVA znA~Hv_ue%R@6*0Dmz|xR_kxxU#LRzvo$tWn!RhRG$Y$|pKm+#?MXe&c_L-ggtJc-^iMC)5Yb3g`yjSAghR=z zG1Kn!oOLVH9vK_l-GALoJ9>Y5JD+UTpEox*@BOpu*RN}{@8;!e7X+`Icb;SR_TV2T zlK16+a&oNcd}{sK|kh=7uk4y13q(Vg_swMPDo`E^Gr~XSoCMt z%Y(HO&d=c5E^%F9^2sA*Z*P6|uFDghep-0lx^?>*uPkicW)-K{{^9O&)~NTB3+2B^ zoAg_rkw|V4*sjnquYA}4{HZaU7(`S~c5@as{GB^jyl&gY2bBl5XS~o|a?_FXz|>E- z8}lX=1;&)#kSVU(>kH~{{CW8M*ROAU{QUA7Yzl2RKh|})o|kHH;t-j5U`bu2*E5?} z%NciBuAa4kOPAq%6=%a`uaf(U|DP`5X$UmF^5gt5vp?UCD!5Ex6gKcNV(Z#hn^Yws z6x__PVn^A76DLj_+_!Jv-anrTc9c!N{Wh0rUD4;Gi~dGFjZx?@EbLhHqjI6ciBVO16h0q3pY?89L;T97 zm8u8By$)|?I=^c^pUGd=g!Qbqo_tb#d;0|^+wR!9dgl9U@7=o>{^@7Utov~%(-YIa zO!*aA%_;FrplMH4F4u+R6Mr6^N}AcEQZ=VB(jg;VNP2m0Ip+e2HrEf$m5DQsOQcn5 z96R>g;bi)c(@Z;K^lBd+=?wpo8n=G`!T0>7w^{ApEV-imA#d{1j3y36(1|@7O)8aL z=ko1-M4pZHdvj{h>e-P8($nuX-JGM#YQtO-wnBG8c!ZZkvQLSH)4o%u^6Gwm_)w6q zBPQ*7XJ?v_@q`(YtT7&G%UBN1nYTWtflsvg+_o8tiW%BF-Z1asU=@C(H8D1a-#%}z-cqGqst41VZ5_mlr?Jc357WGGR+w>~ zeg@Mk^MDUwA!{CppF4Zj*T>g)?z8inrskYKOBNM)G}t&!VCpOU#T9%stfBI%5;M1$ zz@=7)6Pf*=vsw-8c}x|QSSBz96f;ROJh@cpaa_$&R&ixq^U9Sg|Lxpq8T`?Izx@^_ zMHL2ihrJaJ4|yi8yu3>@hT-8+7RSFbUy6g};~fP~1wQEZ{c%xXj}B|yQlV)(d%c?0 zFPV0n`TU&%&jn}057o_^^p1mJ#^vYP$C@5o5Kl9jdFIHGBbm(48Wb$88M(}-^E*v& zl_+x4t}37Lb7?Tg+3%0m?S817GOa^bmre2Ui&M_$xAd~MseLHCrNHn+z~P$5&55ZV zXMKwtWmtrTg_j%6>`4?CRJXLgdPggf*Qj+Yu?hyS(|s-=P0Wb&^Ug9`8%!?`!Q$VLZWb<;C;XCi(xqn;GS=A6T?7J)9$P zWs`;O#k`#gU2%?+_UsHfwIbcz+}!-&j`@Fmves>oU%`37BoTF1IuN@tU>g&g4@IT4wHM zd2#bwso_uI&oa&VhhNQO-}+?c$(DshVmx2jB`!D}Ro|PLwfd*-msqW(x<KU=pjsW*;HG1jAbKCi!%BgWTUooAMQ3O!b+2@%Dy`W^a$& znlJRy`ph!PC7cSyOXgS!TeZ!IrerPU_kEt2-^; zr`p9w+?;tRYxnc@thfGLnQEh|&GX!T#n$}y&yKwNJk?vgq1Eyq!vmRz?5u13qh;?XabMgmR_EEiV=3NF^& z;=;4wQNLA9kA2N-t0@i5itJ90FDakCzC%PHch|zLt(7SWN@DADofKr7bS%a7x9eDTe*Lg0yX^wx#qL9EF+@fF8=WTxW?P6`yHF=-X-JdTrP2aS5E?Wk_ z(>;C;hMz~b)ES;qeSK$1*Cgh@@6Ij$JGE*4zB4RZ@51+7c_AP>>)5wB+tPRQ?w3*g zef(xp+2=Z|#r#vm4)O~)?es}I#dV;-;?9xpw}n?Q^l;}IvGFaLc~C->uV3){O^bco zc)a$1amnIot!=!0|4H!``^|^yj;&2jyXz``Z_8FS&+OBjsagzQ^|#%Q=j7nJzIW56 zO`ZA1^UtepzIjIFdh;Up%*5+6<~rtGWs_9clHe>J@rFs|nu*rqKN3s5-sQe~a9eG< z4#U(vsat;kJm7C0ebYuXVMqRS*)Khh8zfs!Tyj75_PoyDv_DnnrY!4WyxL?pXMHCl z!>J>Wo2Pg;m08U_=b>^bZ;n0Fhqf%PI)|?Xd<+vxHq1Pb{`t}M+ZU3&$G)jgFbu~y|w9=7GuiIPqDjNo=jp@2%RDl zaq6KySKS?n*j#7d%^NgZ4#*2cJ`^VPq7N&A(Ujy*Z@ zWy{K4g$5EmFBn38vp?VsYd-wexbHlJ3)cfTjv1nHW%|*)7gy;<-EaQ6w=U;_V+Yp- zV@H9RiW%!orL|`qI&gNugfrSZYaVdAHDu}<90M&TJNy2-`wz8e&zALP?n^n$oy5CC zpU3gvn>UIaG4}WB4Ckrk&D3n4cyd$CNxz=DlS~2Uo=#f8$8LJW?EX0}d+Ev3*=L<> z*?MSoOIe15OxRn2H;LON5_=efMNDT-mo7WL^#6hyGwWp5gyiJMrxFYzq8Y51m#=54 z5#8$cfq^l>OR-^VQR7mRJwXO0d)bY7*4B4F;E?&lwf&$$=fO?)X1z(A+}bR{#(0e3 zwdaL-3A`4zj;0Aq_Wr4>&vkZgURrSE%f}i!b7qE)@3%$!cSUxoaZIZ{E1Gg|=ak8l zZYi97UVO4R_v>A~=|&8Xdkk;Cn3}jrkFD;S$Ht>)w$9qYcHu`^#aV#^Vr8n@cG1%q zS4>FmO8Vy7#97B9;I#7Q&Bre*YG&Aoiicb&pWU#;fPtJ&E3Zev-;g(B4ne{jpl2 z@}0-i`-`hqJe{ZeEmLPL>!;ca`FE|k9gF297kBAA7k+-{$?h1vScYexIo&b?zr2ch z5YA%qz~R`8wPN8*Ufy)+@@AUIzi5V7abVo(x?MBZeU)ZR{&eQW3+<4=p6myofBpe& z3|;qPRszezXL16ve0Bnl7V^||GtK{|B*t;;;DuM#Tbk#4{(fv9t+1j#TvFuy@@}Vt z%MCufm_F4*D8zJ~36sLFy$L;z<>lW?~;cm%u2_Fk_#Ljls{-W#7f> zMv6Ulk@T30WQUV zjE*rp)ohVuUewR7jz{`J=dmLk9@9ygB>+AhvIvcbsZmJg#BSTV;QC;=B zLZg4vo?ZJBwP340w{tRF&#Cecj;eXt&7V}fQ{u(_i9yTOZ`-T;hrwr` z-QAF9-#1Ek9c^C2Q5E)IS{s9czv3DL zju|yId(w{AiWxMh2o>y>5#afuD6}rgbfUrPRcjI$_w&d2ak?}zD2ml9WGJ*G{W#0+ z*m6LwgTrB=z=>cDmEVR9x6WxF2#<`P(sC~Tae&5^8qkWvIsXeimkK?0c3xh8QsT7X zu?eqQHU7PdvCzG6Yy0;5>MMR)2b#^E878uG4tvE$&cz|;mR)Y+j98Vh;%nyCL+2yb zZ9n|;-WbyoJBZ@3^UQ=Nt5;m(gAr>nEG{W~!~ zJ3RdQuhr}K{aVa$^uy!b%^Mkbv-6qC7YMod&SqvX)OHqjQb^+Qx&H0Pf%bKOJDC`# zUuK*ts`w%!J5ky^rfuD=O_KS$9u-}=d-dQd<-}*D)}b6p)xkTK*az(Huwh|5b9(D` z9*GW1)>#tFf#K}2{{&3d-&|*9qRaXA*^(5t+2;BGJgTdISN)Uf>*296H#Pmn#c*hf z)`}@&I}BLFSF?)EJAO$ZCMvpH^q;{@pR}%^DsXF?Tb7BT!fD|&2bCgAhlAJKEEE_U zPHs?b^BjPjNZ7tFsiDac+F5tRNujH>Gj1LVyg@ zku41zo4B(qWQ8jB%4MurpPYStt+-WqY*_H+eY@5zTX<7^CC^EgjWt?x({AR71)fb` zd7nFl+5C3ALdwo%%O*3j@dWraw(VisTEi_Xo!@$FYPjUA{jw~jg}d)EPCxzh-v0Xk z!Ci~Qyq8Qlbuwko{!K=k;$-D}G>);nOxV=T6McXBt1nH_5`T4Tw&l0$uG)K)yXoY~ zhvLhYtMkVnaJwH`1e&Ybt*!m))jCH8iNqZKU91n-1J-<1)IM>gRqeye!#Ca>`*};v zE?Z51>&my`DSEG$=`UUI-%u_Udd%&dC)GO|6;|HkKBGH zBlW!PpzNyV8z%x!s4p+&P@35ot}tO5i$~@G=P+JB5mpY?ziU=*WQ?*7Z(TBN9^aJ; zSB7vi_B(zTp9^Q$2+lrz^pW9|^G~_<`D(Pe3G)2>{>>nTgCT|M;Top3JPj|RB37)E zt=d-4;&L*#=|qjdnsw{m&6zp#rL=^E#R~n?PmPLi=2WfOv?)#b*oj`lNCShU_!kHI zK797h(QHwi|^01tNzAY_j%pA zb*10$Rll#Fs&bMyK&s~1>jMX#@;kqN#^3Pd69XH=M1~HQBWrkaxmp{Z{ZGE;v0mx9 z;_Q{v>(_8Jo_?9Ybg<#;0uJ#?J+6*|?Pss)ust?w zJftvT;=*1-fr(X}4n?~XkA{{v-;U=D%kA~I-Z<;|MiO(5$EGCfBg8+ z+;6_!r&!lOp)RH576WVZ;2SS~Et>!Q(#6CQzqh(q)LjEFy(}sEVk7R)B`5TiP1+K0!5~re(E4np!YyPgz*yJ34g^IlAdYH+|=66e@RzY zS67_ur|cT&rB&e?H90gVcOQ~jdkG_XU~_!=PJDT zTBV(xoqZd$Kr+WFc7}ze*zA{!LpoWRre+%)^9vPDQ8jgZH*K25!lPMvRev3d@1Nhh z_b30fvuSGWZGN_8n(mGInG8l=PXl7_rY%1n+%J%HY=U98eu3hTk810$-nnXKFnh-x zJBFB0@jPj^taox(kAJ$=ur4EhE5Fal=YpXklg_89xADpT`u5}HWq)Ja&EgD9jSdfr z=l{NAA6s}-^nG&A!k-6@&zIjXlgx9^!}`gi#}hjFKK-l{N_`;Cslv(=c~|C|B1eyt z-gM`WH)PJLqzMbmo-s|mebL5?|2{VE6JELSpyQ0Wa~*f)@a@o(zJB(L_)PoP@)bWA zC1%A>KXf2F=QM*vaMh4>qvgNm7Ui4ZRu`7#Mi<_U+$JX0xvfzrK0r&X{r!FGedgQ#wkp^7@cVD$`MPhL|3BaLdfjgmh6hKpYRmQ+=vW;L zzs&mbrQ?aC$F7u!vor`!l31{yK~Y`6BYA5=(>yo@79kWH?L2-bHP<*x(Nd#!yGw(m5}J&k_-a! z9E=9Dw=8{-Zd&R4VDq-rPPW-Tsd|@IpZ8$9W3`ue^2!(s-mR-TbtcDXO`Rbl@#E}) ze}C`$ojdd8^Yf_4NXdVj?SF6npa1*Y+xct^i7P*7N3ktfo4BtdKeS*l>#1vH-p5%wAKQ;(Pe(Zrrh- zoysG?keXHH7uFWG>guoH&+3eae6R zHuU!J*nv)pNb=;3O({CbJb|Mr9!SqN&)>(-aH8@s zzoZKDMNx+Qw^=97&TXy9Il~dg!IIqO*}UMkxo&7+qpeVS9|K)Bppg`N9}ks?R?YrMcYmsF^j!l+>nr*{P@?i zXU{^@&&{zkGB-D0xPHRZnyjp>8h;zR@;eU;SYq}3d5=XfaC7J{5zBa&{%~S+Rc32P71GSeG9vP-QMiJo1UfS?;Cl z>Z`8<{@mRX{I+X)Ve?$Z=bAr^jkctuWGv&k!*EL=fsKK~`SGf?8~!cxZ&nYv@aK)s z-Jnep#TG)HC2M1Mf9p#ykeD-Pj?Gjbwd7xur+FQ9S5tF~JDn1goVR`Zt%n6+3=B!f zS`K8$>0bC%R~WR0b6#|G)&4K1r=DEm#?J2l&uQA{m#U^E6G00oS841_DE*qMw|~DV zqd`ld-~*rfgFNmG8)D;6-Py|K#&G5X^FN6@-J3Zi6+vJ z9&#T{*RZQtC%F@vE#pHnWjG%kGNjSmvz&Q&70S6^6kqXO{Rnh z0WSU$&dcHP=`jl`|Fj)53&@dUUZHHNVLCnT()Op{S$S(18a}@)DSCBg=5+U}>UzQX z^XyJ$*EA_`fHnqitA4lh`TDA=s&(Ql2OU@LUAesepypDY>8F=I{#a3xmzSp`*6ph% z)M@D&XvE+k=)~85{PFjyy;B<<6yALPxoVEza?o<#O}`luX1qI~ape4)=1yCF$1*41 z$p<;MWU5UxbLU)qAVNxUhO)#njwThS3CwIpSGn&PFtn}|2$oQ2WtybPv56_jkI_G@ z&B3p+VNHW~VuK{}0v~;Twgm<(2Y2a3lz!bCaCg_X3-0g!fX;R6nshSdQKw6imR!HT znowtk*3>S;W1D&mdko8}Z00Sv{(9}FpEZg7$AdLJCrK-|Y*7+^cFOt=fA1oVo5JdT zHs7C2_OE+-VWG4B$xU;fzjrW*xOnr;6~IS`EFH`^*2mw=%fBzU)wyDJd}!>pq)eG;@m1=}q&jngyI% z1RN)+w4Z;@9TFOP6|@=P#k+U=7Oh^LU0hrHmuvdzr^!8vk7sZ=+gb7&@2zjRd2hLc z=Zbxff5lrf6+$!`oEkNGQW7RGH|RJ`NajgdHq{_RB0;vsh_&6vagrv-6sAR1U$*QI z$>o=6EWnD}sXU;r0QCPjs^y2;hYv!DtrS+lh@WT^t zx8MJF^823Ww)5AoT9u{CYS&|!Vl?y2JlpDdm7pGV-G>JU!;c?7?w%59x7gK+9!Vs?ssy0eMG_;v?b%? zQStr14{_`N`Tga~mQ0aW70a5^(zE6!W@UHYe*6(-G9yT|N!*Y7%Gn1$6)gA}0=1?} z+}Zm2=>9V)K^KePop4n;&D6l5cxP+r>-v>hIeSA}}ujWkd4Bn0g**_Yr zuInF$pX#~xx2ETzkCLFQeeEy)WOe_4YO?+PpZ=EZzWWYzI%XfJjM2Y+`}TSU1|$DU z9V-Kaf(O(0|9N_SUgfiy@oAfHaz`BzQe?S$^=fJKww#@Rmr3XUn7r-Vx6^`ZF0$vl z8w|O2?hN$c*~@tH;B`e|Tizlw)}B?y!G_EJT{YRq#8D~nYQtj_+fxi3hk5rEXuDXM zIhGz5eZYBj#dn9@|GJd4|Ni~EdtUv&R{1yg_Wtf%xpL)2?zg)pshr$g`uf`U`(?Lt z<5#U-9nH@$j`yU$551?v{IgIgNqAMlL=#IQV$%)~#o^ zdrVrqDM9RY=Dp|Nw(2(uGBk^)XH#?3!n(!!WJmOe=Dx|{i0V`&2w%P%vSNtvtH}}{iCn1@7&5)E0^#4{^N1~|AL&H zoRod99Sp=cjtRMWC`crqk_gtAD(Y1{<|{w<{#((sN+Rg-aRp;rmYr94G=B1E7#TDAElU$HYGGhe?^vg>Lt9EQU^|l& z@9G2LS6P}4FAhCyRZnv&hYO{cjP?M_Dh6VuzFUe4m zS>YnH7Ed|5S~om2^lEJR-O~A>{Z8r53j>6oYL_v{O*Rs4zh zRj~2oCPvV1@7%B7-u~CIkP)3>+|1au)qVzl9fNC>e1l}2t6C{zjmN}PD|5rbiXS&N zx-UOJ@2;or=V>OUrd#*EuYLb}vvl5$hr)IH?QQ1I*PrTj7Q7AB?$?XO_buxF{7B3` z!pEpEU4@ld&Oi9X<4o=yF}`xkH@gY7G&F2%PS6QBHp3^=O#k%95Iys$9?Q?4QJMVL z_+(1G(d@Y)8dJSvFJD`>OZ5Al?+f*FZ`a**SM~ff>sQx?u!m~wMOk7+ot`I-9#fb- zqopQG?9)vVjiR`xx_vV~U6JEdP&!^`7 z|E}uqt4pg`vpqzf^I+5Mw;b%v^QHxyxclzwE~yy3m0lv9{sJ3X1P*mMiMj?}y17Qi zduh?*jS*P|WoD&Sd3vR4lTV&CVte+wOCT#V^X1OB+iuI<*pPU5uikX+jIaEk6ns>K zg!A`&Jmx>Y^4ZLKNlD3UPr&B&}6l{@$K5d+ZDi%PnS4_1za4 zD00*^`&!Qar_-YM?MXV?^_oMmFX*3Ri@@wTb8J2yxBvHXHfS&Y`Q(i^c>AZu;rV3E%kjbe1kiV?QWl>CVG2apugE=HK7d{`?;%V!6BHaNL(oISd>O z7q+CGZI`;f=BLekzF-zB>D+>S85?B(ta+AaHgnG07w_xSp8WV2zx3I&bDwI9EM)pt zuiN!%+3$C|->(l_6R~lsfYPm{XJ6FqJ9zM*^Zs8~*YEpx#`ydnBeCwIKepYO6mn6B zFaEYr=N*xf-PWAP^e$|2Tl3)A-!Pst2ZT5nn+%;e61`T=Ig#Slwrp9Povq!!5)--i z+nE#n{m(zG|M$H9-_>>N)~&AGd?8Ej)_>LwP3Q9LTLk9V)&9ErzW)F3c&nl(C*-o_ z1(-y*Dx;&KmA9Ligv2~9vwkJO)M&Us%b)9Q!S9Uu+y|N0*-J1uOq+Vx{B7+g)L(nM#dU9YZLRzM;#XHDwkOOy zv59Gcz0A9~t+}`Dxh6e1-qK)blGoVa;rT<;OYz`MnVYT*4NqhAgl&t{o`c$c$JEsM zKl`Qj7)EVOI{J&h<^gm3txc(?f5+%e?>AShOen0|C;#V}`Tm~=Pn_uBTr^2x$8DvF zZ|82^TF!1ekFUv4kt49l@UVn(j`1dDi`{;{;R_YCxlTmII`s$$I7wXdDQ9Vzbo0%R zmm4=O@8jdQpLZgq>rXB(FYj7@n-2%f&GPP4ST7B^ylfiNp-Z26!wnt`MiZXaCeN(? zcvQUp-`4fDZ}sO@y;|x1=;MxsUlUtec6D`~HY+W;vwzdVhkq6OmmgF0JfZ4&Mv+5- zi;1H}VNrjb!-@}=?37y^4oJ8-GPG+nIp1|(Yq>&Wm*hDCCk4)??N$;o;le!Y&Y${} zqcc_N+17&*I%d@`E-VzEJ$v?TX(_3mnOD7f^)$Dh`z4;t^ZVP|-)GJD|2%6iEh}q# z_4GlqQ*CuS8 zZ{lR|tzyo@TemJOJLMH0ZZ>O;Xa6uO(KNPl_s)_}VuMDYkt5%{b@N zq3rrFnBOr+xZ+ z^XDI(56-uy_cA^x(J`xf_TgbWD3d&9wIDWR~FXYx*m7cW9$B3&zoL6kNu&>a7cp1>40LF0w)b1XNjOYpd9X=_Ux9$WQ) zD)%IVV+S4<{Mfx~*SfbaU+!!)oF*$TZ@+f-(Vjp{x%qGB&yc8`8l?KJtUB8@kSpWu zjjZNtOVaKtY;>CNJFMYb#D>l3zw1qHZ3_jaP1Mn5U}DhNG-p*-)~+^=CU!-ZUpEr0 z->!e5FLKcT`Y#W?xqba}ZGJv^d3X1IQ&Wo{o0P{d%Nw21I+*b-21-o zyZ6+oQ+aDHGRYok5pdwRxNzaZX3#lF|KHvJ_xAq3@-Ub--Dw=j+aFUIB#`p7aTPPM5v!&lc_xuwY?Sab#H47!Yi`!l6Ut z+wbdDbI;8>8Sy3~E|90#K(7DzuV25ut&86N?p1<8#9ICMu50WI4&_%={J3y^|G%&2 z`FMHfKKlLaqs1z-yqy0|+1X#K4UY*qIWQhzv2#?A`oZ<$d4FIGgQ&@el^Ye0%~+@Q zN=h~3auQQ=k5H5B)T#TXmX?anR_(NS^zlo9-Fm-+4w_3BFJ8R-``g>!f4q50;U%nh!m{F3nJ*;eX zqk!Y*_p^B8rB%f46|NIFU8XVrysQ6wyI=2b=A?bg{bRN7@uoKn8qbRg<1OwyCL| z!SjF3*VWa1y~W6LS$p91UukJ+=j^`SNRI#g>-zqG*R9Ln)l6LNz@wyi?%S(ZZ==K4 zR3wBZKh|a3e!Jc9T4MW-Z(IsGbG8{?-^u2&;Z5?K{h~cQAp%~DIG7Yy&7Tm|!XR-> zg7xmZZw;M1z zyKK=MyZOeOGJ~JTU%l40Epz*61J2S9oJ?#j8yL+(zc{q(vX~xVyi`-rn zMFn$bYAJLzt>0y}W8pHj9rJ>wd8!yKo?;})pfLS(s{eeuz3(3!Y~FwR`R9|bUcD;Y ze*5iqzZ2_J1Gy)9EXlsRtJM0?!vavxw)<$(F`oLJF==^4MLFR%cGYJd7W|s)SI+C? z(X!}Q^U=j#Z?-yI_3aWk6de3Ge~xAGuaDk)e>8?plVxZ$Y~ysC`t@rxqhgccHMWnl zFDG_ASbqNM>N;Zs1GbMVS3dq*{k^XK!-IqKmpyyd_CtNmrcFjQ_iMlZ{rUI%{r^*W zrOoc>bS=tEQd&L#eXDp}#f$9t-?!pzemV2i_3=9&>44E6&=qMkPaF%F3{xIXy?wGBQ zH*fy^DXWo{fx)8q*_q7s*I&nMPCIL6P;&m#gUvdN_y7C4{{Q~t^7Vh(|CC>PI>{+K z=Hkbvvt~UAGcYmUnj0wMAi|ljW`46|MU2s8(IzPy2|bO!CnQ#{Hk#zGoY&rY3s@0*XB{amk9_0s!}NLk~f{O~34vsL%Yn zqN1YKMQqQzTP?%aZeeIxxXNHw%f=8b!&TGndd)B0X>*mYeX$Tj;#-?9!cWi5{2d}A z;M}wE&w&FU*^@WkOr7}q_~}o~3Jgz-XYLHB{i}BJw>LkxE`I#DU1F)5+`Q9Ii{i@fmX_Ck?2iBQ3$#_9bN>18)bPdq-#^#B zj=ukE_58nY((g;j%E~_5|3dE0Kt*4*6e z4!Q#%wY0Rf`|IgArkV!BwtM&PP2cx@@B6>GT2qa-cm=AQD3pI4(#x!c&5X4bW>%(#C3k;_*m4$gypU0nB9{rc6V&DzNEu_7&ETIkyuL8~(U z9lU?;>{;HH!wKIVA~ySM z#egubrdi26i7gDyj8$tEaK5U2^nO#U>GMZhgmNV>{a{|C@gqB{NqY6_=Iqo+Pc@;N zhP8V(K9j#w{o%pE`>&mu^J7%89J#DBAhv$;x2&ef#e;$?wvMW_V^i zbB4M|)~B$Ll4h$Qrb59)Hin3asZ-|6v$HLDaG;-!U*<<(QCeDqe$<2SznMYDJpW+k zxB2i@H+tKhw`P^48eiW1{1n3LYUYWIS`Psz}8QE{QZezZ1Ic3eoq?`l(yz3NJ`)|;QV9{b> zb-EHF={3`%(*;@@%^)lV4QI>CCUEBLH>X1 z8yg!Z|1z7TQel>p@psYv_wNoxTx}`+u~IPZ0Ymy09x;xrG{qMWE>CNmXu*8!rx=%Y z#MT2r%M8lh=0xUv;=gv~dU)Q-6(6=NxxDjah?e-(cls)mPu`fQ>>g)R_~?lIT)*X; zSFKv*J4vOzG{-QRN5%8gw(Z;F_okhlHJicV+NHEj46B~a^f}h9uI3hZ_UWc%z3I!{ zCds%(*2PJuM5*5B?0jfH&-Qlutl%oklbcu-7z|cvtXy^M*lNLJlfQhxb@9e4k%p3pXkodl$Zt%r55tOwt2bvPsm2G$9EnJ%o?E5GarN`{ z>5O6S6D86Nx5;%gD;~V@fivjXjN=bi_@-`Ne6iwALxhQxEa>8eWxsy?S{G!Q*&+Zs zR@0c@_REFvlt{Br>bGwxnQ!0bc8{NdBe{j8vEg!L!~AQLIGb2=C-Cz~uqU6;xV$22 zf6q-v9-Rx(5>2}b*GUQ{eB|e<+rNA5!iA5trk@t{Q87~f^CRh;{SI{ro>_mIk2$Yi zdbVQky%VpluFik?=FJ|ZnLd5)i!T~i-Rg;(W-ldM&&*(yw|#q@z?DsZYUV9jbfPnO zdEcLCt*Hi66Qlh$&0*x&SyFW?KJ@BKK9(rPhD4_hrPC`vUS9scqUia#{J!sBZPRDg z^6uKTE3E$8=J|i+<$s=V|5qIx9L(?3@#1yG+I|72_O>>@zaQG||LlHW^W6IX;_ch7 zKY3o-p!(n=ebeW}D~VYuv(F!r0}o$mY`t z<^KggK0Lhpt7^J8*ShHIHTy4XUS(hqW$BsYck;u}%>6=D*?T0* zI}fcn82tOwr#&z8>ZVl$-?%exp>NSY$4Bc{efoOkYG{ljhug=Bs)%WsVh29(&%3q1 z{(pXYdOG`|hXvhN?N>O9gKs1G4Z4jaBs|>Q-`Dr+@uZEOwYzKVruVoV|92p8RK9XlUp}(DViO{QrNR@86Vmc9yhDV96_1gSF4zefjd`=5e{|H>>ae zIA;Fu+1Kmw=0AUO91F^}*z@#O=<0nN_H8Y7Kk=9|u_u(>M*cyB$K~i(-R<)Kw;Om0 zT#D-vu}k*pSST3vcS6ETenpl~Q7JbxyB1cc8H@O;^sqb&(GuPL?N;{tPgQ#rYiII_ z=kED@puhglHDd8i_s>G6=)fDcc)Ohvh9wC1fqsLY~MLT1*tzV!2 z9&{_uat*INyLRn5{IcZMFLqYP0y8tSyckf!FGg>Aa!t)2&^>vJp3n5rva+=N;HT_Uhkus%D~X%AY*IWJO5kN+_OE5r4}qtiac+90n&F}a1^ZWLC9m_EZ zUzH?K%@=<%B`AH<%`YczZvN{Ztk;@td1@28La0b(SxHHJP1Y-wsj106+Q%fmfB6#g zap(Ekck=J|{eHK9?Yeb&tiH1!EDyQzr{*ST77;v_avD_hZ9jJGn2(+4{?nm*7p{Hl ze0@#K&!5*kQhJ?36IV=ZOmCE1z`A8$v1#?r{Wd>3%H&!Ewu($sP;lZ9>T+BWK23AU zrGnaP>wDK|cr2S@>2lHD%zC%=%(H%s3;|w|zP`R+m-)`l+uqG)|L@1+|5Yz8EHwX` zDk^%fZ{50G{K-A4P9bmo1voy;n!S$W;~6Hs6N|t5Y}Kw)4tPe8B7gb z54?KyY8Lpmo425G_WBnuUhJ48pv2U`X#38Uot>Tk52#tYyZ-m~`rp5W)%|2bMUGyO zlWlqs85v)HdRyq~eLGeyOq_lEVS#cB&xe^lCzi69FicDNBzDlh-Q3)N!-CL8L6*k) z{tks14jDgqzAl(pmuPaaJt*aKr2>bg@Xa?jKknRl_*a$Zq$97_@BbHPC)G0{O77Le zcKLtl`)+v`7ymdmYt{o(1_r)%j!7yRi*0&rD-UEoTgPX&H?wxfwtX{2n~ru~N=vvo z^UzXerC0F=wa*@=`!3bAVaw8ua#WbNO6&iwTemK_wFvmCTvG0G+G^Q;_0K_OcD_Gl zWo6s;gO;Md|9-nqU#+EM>(;HSudj_RcUKVL0G)5FJ3{Q?c{`ezr*UM%7 zb)c%|*fQVQ_nwzn)&8=QtD7}H|Ha1Ra=%Y)-}iOx{+nB~uX~Hx&%Y28T6%Qr>ae@} zH?DpAZo={4)~oqS#*O{omorXbySsDNo?EBGK40Eq@B7&JfO^-WpX(JQq!bU%c)84o zNn^>`h_p`!C7N|k`z)KHF=ZLMx_bPjXD&<(3qWU3?X{b~zUjW5RPVgXFBjeS%k>|B z{K`nBr2KmAf#=2Bgql`d)>Pa&WvXG$L{Y!f9OW-A#*-OI!8QV0o??-dE zJ}=Yd-x3tH;N~4O`*JInt9Hg`?KQ%#U1>3MRcf5NbB@jOU+h=2SNvrE`tfM-!-5^L z>vfLE&ky3_Yd?JZ-o1ULUte7{S8OQ~78Z89K30d%rd& z7(}qo@$X}gSDQSu_F=1d-LJFxb)WU;)%|)I|KaOT28PzZ|Gw$YojZT$s@=bZw;oK; z4lFj+(6V{Bl#yMvfxY0I!I84lCU5=CYgwJH+-h93qVK~5l?g6_E^n7+w?#R)_|G)- zbGI&cetqZ_`;@cK85LXF7hM!D+7x9oC5Y?x_Wb&KUteEl`?*gZJb2IvI-=zMb#F(@ zxk0Yw$a^9OS4lLbwdtpDMf={r6t%!q_xP&6 zqWj%v&7Ty=&|)j+?p#~@VwJ;z*y&!W$1lH}6PTks)$3@#to65|{QUQu`P!W?yykzU z@MOOC{PW>^v#zd+wzje|1NEic7hl|bI`P`}Ru_x^YI=E@IXQ3k{QY)Ye#fg-tL+|r ztW`rDhzcc3)&>fPPl@te*aepqO+s!B>aKmPKS68F~Y(Y;p| zW^=u$v&*%9%)O}2V6S=gj0GN{8#MT;npMxAy)uurOJX4tgYK>Q3$kk4)(7ub>sr*H z!_o2Nb5%&h_jk&vHLq#H-?|Dkq&PTnc8Pyb3@h@Ocw*5STA)A!*cv&xPA z9Jkq=ea^RhJ(iWaUBqd_?wyu3)up8mKJDArd4H~z={FanmA^tIlYOMaD*w;U-}mza zXe48LY;3H#fYY2`U!Sfz>!BUCX2;re}6uo|36e|qQ{3^kERFj)~wuFR_lG* z*7RTkd*Ivp<^9mk<@%Xr^P{i zW-HqkX(*gFda`wo!(*qFsS5>?Z}p@}7&A6ls&GCPIr_5DO4-1?T*Se5YN6F!SG8q_ zA3FT$@4NlBL!fDHlLJHYlIdy&KC#EXII$)4+%!FZB5cO(#S%@+yt69~mpphmeI0{V zal>MX1lAL0!+pE#r-i;zJMbvqc4;0xX`NivyLfH+}xf7|{^U)iJH~m{M+~rsmEz`+2?sN3z&n zFI%tB{1DR2P$ryxxjT(Vid|&CqVQ6~aFhj!7KjFkG&&Da1^Bhw92hcT}VA zFX!adn;z0>D5TP9aAa!JbraLth}m^r0XzqpF1#_>8+2>#UAwg4ibUNFJLIoTk>35& z+GqP3jwxC%Bv~1reBZsxEGOgl>t)MUy{@Wumy?s7Yq_9b-*0KqNAdWYj|UI8^V?US zHN9T*=+)}=|6(*mvM#>szPSJ8%gl<;XU)$u9oQtx%CzAOBL~CA;N^a`U!P8ozgP5p zZh706BhONdEVmXvKi6LOefRyp-u3nM>%WHYJ@q_0^7@@W|K9D|_4*E9yF7yib3o4v zy&0bj8~*bc^s}A#-Lj>0+4(&;GP}x>gbOYzo>=}#BGX`(e!%CyFF&gXdoiBgWVWM6 zQdl$4m9bSIK!i23vZ*JqOTx)Rh{eU=7?Wb$I(HsLm#@jiv$ok~$TKjoH2%0^ljF2C zJDDf`@>aXM%a3K(r15TGF!{Q^&T~elEWhyl=;%GC_dv_x2X2dsh!1zFcopSMy}!%a=zQ_T`#L$^HhF!Ti&(W+#`R1NIf3l^T~c>43y8Q@J_NP4>vL{wczFKRt5-q4 zzrC&gbW(l(pI;9Tx5qzkU&pxO>5FhvlRLYXt$cZI!SUeskY0bsU0w-|v4VegG`M+w z&^^c!=b`;%yQZkZL**k?Q9TPXTXspVVZP8X{~3P>2V>-UaEb`M)14gCFnrUwXwQ>zKqG z%i?9~ z|Mu3_*C&0{COm{p3htM*Ft9KN zFA-BWjb$=k^X0pZ#*go3w#>b1quX(H&ekU1-0f4uGSV+a2`@dXR`K!e=FiOM^0ij? z26?SKtaxo*%*_uocUfJvw=gmJ>SedS?{Juit7X-f7uukE$jowX6jU3{+*6&E^=dYQ z!eOK7Mhu}ZYd@bgw`XN=o%wO9UHt7`&z|*{UAYqTHf*`Yk{(0lwMTDUV6!Xxpl#Hw zU*d4&Tzy94y3D}PFAwPo=WuleU2BXsBhTOCVsVs7x| z_NYS-XKOB<}~(~yV_0jLr*jDaU`@kUwj^P`o{ye&abW)7y}rW|G(-J z{dcJbm&PrXfM4--w)~z3u_Bwzd^j!&FXg@+`Md9}ob%o!-Wd;$XD8QG-&*MV`mDXt z(bI3wc<;V*plSa_8@3y-n73rV&YhF&v3sdcBF~Xqw=VeJy;~};!g5>F(o$pF>gh|T zFk19$OgVMj-EH29Cq~wsIOOMFHRoi= z>gwh*b7gd$=6Egsr_Gbkl@mT!KHjW!n$_Z!!4~F4Wjtq=pOKjE@s;sa+Ll-m3B`n( zz55SUiq5(o&#+Ws$Bd_G7PoqDY>3hGJ5xO8hArQ-)NfI9YULhWT4FG9_pKDQ$@490 ztKO7%IZfX?(|Z0C#vKhkXU@E_x3c-ek?a#F!us^;$%$tUIdgWII$kzgy2Y-c_!3(o z=ZnPCo0K@R?q2eZkG(uORr1qMn0a)#5(1#T-#YyNwm{^KRB zUE*X}TlMGS$B#Ri1Ey6jeZw5nB4b_sZM)$9`q1RitJ~%*3TQI)aC&&@rdzV**MoWv zn*tn;aQW?ie53B!G{xE9)h@iKvy1(GxqqqdK6rUsWYM;34IUfcpWl^x(ERxCe+*u%E+sQQGK$yEeaEdM%Cm9a>b)CW8S*n1 zJ#%N0l%8Gt^U2AJ_v0=c(+|{SE)bkF_34v)|NhCh?75dV|MJaAlT%w0dRVUR51mka zf#qkd&jxK)!_#H1j{QNe*VuWyHNMQ)A@Cy7-fb1PdAd;pn*(1{myYy4N9Kks=4bz+ zQVJafHkU0AZ`{aVGUIYwSF_T!8pqhpj~$QaY-3N}D;uyg`02LI-E89g)3w)Zo^`{U zeR0+PE1G)3oo^02Je*#)V)`Ym(|>}@8pSn(KmIuIudz{HW6CknV+DTGBx75)ZO-s5 z*w?@@V_~9E{?5{CEa|VSAFt<=k!?yi_x|$P2QhZ>2F<0yH?r7XGdxi~!NAHPRzK6X zvO)Dsyg|Iz>YbeEA}r>%`N_{{;8{}Q^#1Rt7FQAL+4zDSx%R2i_baz zEg?6#vGiJU!Qoq1p1AS0S&S)_j>BwCXprud?HgwY@L2 z*S{=`oqzp}ss3hZt>gOh^$Tz5*gWVuE--uYBK$2V`uyC#D#z_Pq`}& zc*mV$h-nHq_Tpb-V|+-}-m|_BKk!;cnkmiPvnSQ=;Liic-Z$r%EOy;;@JeA=7RR+B zKDP}mLCh^&;;h@29scwG_U=v3J-Yt zp|IuemU|rX8}<3@*Zk*lzrkNuEOAXI$6`;K?7agOiU!;_b~^|dw9QOR;9Gdn&?@Tt zimO?kYwcEP8Q0gnIJN4`nl)kBZnKQ{cl51Wx9-%bS7G(P>e5eL{Io`)!==ubiMcoM z$!|{&*Ru~VbDo(ZKjVOl&o3FD#z`elJ31e4`Vs$wcRfo~Lu`$Di-5xC&#UG8dd~b` zo?E!pG=k}l(yPP>OGB$)MgHl}=l-s$>SLc`G+p4)lcJaZ&(GFyocqUUY69EIm*>0s z{coAc^55F=$do}NZH+obUUas>#dB66?uGE;~{iWr8zd(Wa(o?P6;-A)coHGzxd-xZ_dWO|g zPdz$*_^|%9ri5$7GS`=jXnL>L*)dZ{kTgfJqA7{RpeRuiv@#NC1!q?R=Eu)?* zpFG;VYtMckBG} z=e(=#FPphDB~<-4W9X7sm9zi#t_;k}RndGAxR!16^K(z*HKxDrPS}_l!L-Ne%-S_P zb0s8y2A+sGSsbf=Lcx(CV;zg{6Lz*klkXKTx!&}K!*lx{ahK(j=V_Z7bo5_6Ht{*P z)WwAdzZnb4J6Q1tr_bD|`z-ZOm+1<% z?^jt|zc~21)cHwVy7cq&PjT+nuib0=(-aTSIuPD?@#jzfUAJz%tN8V>y?#dVUB6S7 zSbI%3uH6-*Q0uiMVUc z;f)(x=lAx<*QIyYZxWNzVT@C?P_nkQ`xoUNeS6({ex5&?R(*#p+?`H@YCGC`2)s8I zSa4^Wmzr3uy~ks&7c3_%*@bMIA2YagUOM#W*`iKOhdkXT<|2bb7DOP&k+|+q<|xpwavLq$Zy!AJ?%xVV$nT+R?MI$Du=k$QD*1X>3VQyQ>$*IRumM@=vUrJ`qKh33ow#8qmu}zDM%?G@fm( zFIrpH2yIWZWn}$XcyGtdnKK{hpPzqzzfjv6HnH1G-VL`qq69+hBxGtj1)M-mX+L4P zu=kSwq@Xx9r!!q5r;n#CJLKWAygd7wXFv}#gV)q{1$D{H$NW+(n(Sd)ny;YvK&tLdXZGsOl{%}pt2OfPl$|!`vHEY8r!Urc zB&h|~&3{wfBdFpg9kvCGPfHFIW_FQ2wB(d%nxzrpKL?b|0SRrZ}bcOXTmJ40WHA!YL9 zR&)KB8Qbp0mT^pDDCgL!u%gDo;z!Ymh_5FPx9^{>yku_SxmKYo&CE=0tZWm)>Mp2c zS~c@LZs1&@w&C8HkT>N5jEk3Q%liK8%;xy&ZnWrN+waclN3Y1aEfabFx@GySBQI?n zUM8P5D7<{ie{Xe$k=wDz8@KkUyppbE@Azo^Tt@KVLz!mp>0|S@_UFB|x=O48)t7i2uon5|c#tg}o2Fs>MEOjnPQ96Cf zXNCQg#e0+XKQ{5W;81sJtE-*tih}}^`3+{(uM*)iy5Yg!uq4?3zzU}AX>93pi%m}` zMH=r^QZhKUcw5*WBO9CV>lZKP&Nn#r_K~YKhj)WDzXH($JX{Z%32_g;6opReEb z_5JNBR;&AJc|CNsMv6pDcHsktSuSl`88{~{I^@FD!r)=2ew?wr`09RjVR?_P;4{uz z*~&B4#A>$IoYe~FZkXZoK;^+2HitQ~0x7N0_^8RRQ zH#5yUCT;J%xP;3apFZf8d85E`r?>allbf4=ia&eqy}UfDOiW9MF-}>5p+{G5-a58} z_wBylNVd1xQvZMQ8o`o|Vvku96NK2OmJ46Cnbz;Ey(UCJ#V?$(gIVg+j$NGITPihD zvOaz1O!Ap5{pty)WZ(lA##dZEU!R(qH=F-1+jjWL-6y~MuM2wm#A#mg=4$=)viSKY z{dH?{eBQip(Gj_kXK-DjSX=w?ev8sq@6SE^oNste)4fC^*~60klth8Efm5E#1s=^$ zx1U5zD%|&S$BZK-#j#3Jk0sLN7rbnYJbrTVviNj$A!)mrS3W;`zRjxa*SpEfEVJSk z8dO@kw-?L#`Rng5{q$t%{_Ri7-zJJaC_NChHLB+OY5jPOX{UcK@0a zDj%nFO>ntaU))^Ad-L3~8M;B0c}6GAGE3KJIGp^hekScMi`uiD3luqTIW+9boU>xZ zm$lm_J*hE$_WoI~|K<5pZm}#3ynK3Mu}a4SHpW;<$(hw(FD*T~UGwR`sb@|-dX>QW z!Afz#6?;RQKk=8X`ltX5gfRjTs0>26PY z)>XXZ^P|T{w*89})=<$nufNl0kM5N?ZM6;aR;TaFs=9yf*Uw7<(h4vBFY{RQOX_E> z!?kOx#M#-G$Hzrz)LvY4VEYZm2&O%K4h%Ap5f;H3)6@5tzPT}9{{8#^dWuVIQ+zIn zIl6p!;<3@gTk!mXjoofZ$KIWNCG$v)!%KV3i$4`-o-nO6{BnwO=Cm(f0)}1d7SAr} z!{_C!zfE_4xAVCq(*$nCcM7gW>$sIt*-}+CzJGGgx$?E^&%Up9sD4};h$F=pcs zh4V_z1}+m-1p+>E@lJ4AbaS0(s6upOxQguX0D%KCU75bnD}MWIpI3HV-}7q?1GUSBmNbYlstU?`SViap2A&EXK&b% zwt=Ohxw*)1|E#&TmZ2IvwU0kMd~fshL3914{mY;4YY1|ZJRH-^!}(aIXx{Rgg&tCW ze=!(xE?`(PY39>D10%+wbHQmFg%YQo^LO;xuK3G(rRal6nH}OvOgj1+Q|#_+$$j)R zP3`To^-pu;EO*Tokhkh!ke2?sva|DazMqfI-&@}gf7oqgFCFDy) zJdf>R9=01cvnEcRw04hy$LeQ{6Vg16W$6W!*2^DL&06;T>K3uSDP|(_43naCBhEaZ zeqUOC{~y1dF`K!k+;`c)QZaeqhJE{foV&UC|EH%{uCVX_`k?u|ebtK#n{8!f+5Z?E znGkfQQtp|B`n=ao6+43L9T^^uU+H<;c&V%ui=8 zFR%ad^T?6v`xZ4n_~mwfzn5OMd)KcDiAfWJzS!{Jw&|~!$G-0I5f=@qMO?-a{uS5G zCf?QYdn(=ZW%c!&EQZ;?IBu0jh`h{u_-^}-weNzLXM|O6l6n$jTFS`q>gxsr^X;E5 z9y#)~y!&V`=k0B;p0%nTm@OeQ(O+LbewLpmmci(FbHe8XkpZ7s_b}W>b%7G8oiVY0nKlEShyvROMlw8lszqH_3taz zq5YHd?CT7UB|luB9ur4wXnP_IzvVvHszGXC?-|=Xc^`*T&778Tl`v zYS)jAUq9ZB+Q3r5&B(Z6!@hkV{ylh*t*#$i^?u>*_y2m2v$4(F`~E=V@!hv>e>XZO zE7W$UROU;}bPiXE?Wfs2e3C71-R=?L{rxq@@#gar5{I+4o+_QZD{j4VzK#qlgD2mD zQ!8({{7gLd&*Wieq4NH?FAh0hR*VljlM&;P+ zFy32qW5xCB`)ADg^Wx8!FP*!;zmc@4`tabrjBUk-?Z2z4`L|cbiKp^32)wi6+tug% z#G&(!ZPWpewq>=qepgMta)RZU#aT6@+SE_X3~S#__i)jWzP@W=ujKKDC7*;YO^yn- zWMG)`>D8^M)S9||UtYe+`FJ^K<)r5_xpMb)dAu98TWBPgEQ^qtuPJuizo%zUVaES| z%An%jod5e*+l||oFW-N*)LN6lVN1~L*A_1XoMhH!sa$R2zVgSB!DC|lG|t-!a_a;Z z$T3V5o%7<=ofyVh+gTrW3T@Sx!N~AHK`G!=_S2%=Z=arS)qnOZ%q}`EPX1$U*7f^) zeG@o8m^mH&ZmxIk>|Dutv#l@Ry}5IF{r!FIayvesQwLosoD*OEec`v5b?t@kn3TkG zU$e@roTRea>99-7GM+S-Cp{Z2B$yZuNc&_cIF?%HzUxxpVo>Vm(Es+F@wrkHZ=r!Q zN5;;Wb!T;S*Uq0kUw^Kq*y-f0Q5&lE?w9Y*=J#&ct|-88YU{067v8*Cx$x%A;Mlis zcmFDTFSq0UfyQKJc0QT+uiw@#*u6Np{#>awN5f%3%g2G!xF%kcxhCcvq~GKf=fkxwFf3r+Arg%;L=#=>O(9_e|@oU$GEw8oT z;`QFFUDbhm#jm>5RRI#Sv_#rsj{iLR?%cV(wR`t6e}8wo{K-7)cNH1&{^uRa-H-n> zyMDKoK`*sugMkQZ?nfr~LIKxL@>VSjQy1-K;1!P0a9H`qZ#|!*SLGU^)I-ASm7RKa zeNxVRl3y&wvLp4*qp9>06_-oEPd>tf60ZtVRXaR1%i?{8nfe0l%w&B}21=-c<^ynd(D zvW2;UyF-P`K4A&NM1~1j0vc!SC6hfgS-nN7N zf}XjjY!A}AE_VHTw(-(S$3j+yoSlAp>*;;+({vw3nl!6AbT3Pov-xY(+AVRbr!Jd5 zy`7OE=6IoX(av?TZ{Jpzm6yJK{r1h9*YDoEd7YR4{&i0N{o4~yf8ACYxBdC9Sj+2g zIW%IYwz(XW5Cx4!TrX`bUMDd1ef#RGt3VxRIhHNYekbmX;}6%K`pawSBXKjcSKd#H za#sd@oSwZkL6;-r>ZG|x!xA|!B%Mx}v$^a2bnSidaZ&N%Rd;vxJ>Jzf`Si}b>T{*m zMf0vlayLyXvP-t%appK2;N_6k#a~cT^6>D?nU8K7q--@>Tgwrrh0Y z7i{1DedoJ(@0LF~^3rQ*QBiH}-|*|zd19mkbESXs)=Gwnf^+-Z85kHCJYD@<);T3K F0RY&&9!&rM literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/titlebar.rgba b/demo/src/app/scout/data/titlebar.rgba new file mode 100644 index 0000000000000000000000000000000000000000..17a86e8ddc8c14939d3e1b4df7ff682ac93c7c75 GIT binary patch literal 4096 zcmd<(<-H*(DR~Nn#l^)>fv|*x#A#`1=~FT?G9Y{!N`u(Q7+D;|24Rpqj0Umc7}p?xTCrotj%AxSZ(g=_>(*u4w{Ks*YuB#jyLaziwtM$(5Dmj1 zK2dnru3d0-AU4SSEnBvL?byD3`?6K5RxQ`i(73?E!*h4{?%k*U|Nnm*3E%nu|35Yi zQiCpzO$?VBkXkU_y?ggbZf@>73=9nS5)%_o{QC9l+yDRn|Iq`3>`F*TI1aL#fq~(^ zfPlc}ix)4R`~Uy{`~Uy{fB66Z|7RLuklF7qUc7i7WGBdODBh5jl{NSG@83uN|Njq) zi_`!A|3CBp|9=oo4Gc5u)bHQFkEExk&tYI-*a)@zJ_7^8CVqbY_A_VB^#A|=f5HF% z{})2(`Tzg_rv`?bdHVF}2?7EF?I1gm?Pp+MXpM=9$^QBCXW9S%|0`&LfBpJZ784Vb z1+oi^{cYUb+(`!x90>XU|9=E6@PPvdLV0+25*Zj6+OXNrz`&5??d|RM-gD%KyLiRHfVSc6L^rGG&U?^y$;3rcIkh76$1BnE^5jWG2XLV(r({ z(^E`OPnRk!EtM=ODIp7k^n%O)nMJDoIyyRvVPRoX$;ru*lwgn9@VfsL3fXo7!2{M~l`<YEtMvZ=|F2C=3^J3GlapZi>*nU>`u+QN>;M1% z+fWn#`0>Nq)6>%xpZ~eIxDt2l*x~>G|NkIb;5~cx1aNb6DbyIe0+Ru$B!Rx{Qv)dFD>!m!-pGrczEFL4<;riQ2TFVe0==mU%!5> z{{R2~`v3p`ucHxO|MTb1)lpGVQ$X!ckli56!^5-j#EBD|{{R1fk{}F(SydM-4wEgGLpRl$!sQpb-{Oi}Rqv7Ggs9b<>k||va)8$%F3Q&XJ@~`&CPw9kB{%P zfPlbhd3pIWW@ct*JUl#3`}z5u_Vx7zV<`KKkB`q8UteDk28n}cWHB%wNgiaz88Gi0@=yI!EplsuH2iHVt$larpl;Ly;}a9z80ZSnf`>x(os zHC-4O7})9M0463TYkhtF&>J^yn1e8g&BVlHLu>mP85u!VD2s@Q#3v>u8Wk26%78G4 zEi5dY$iTp$%*4b5QbQ}u$HBo7Z)a!c+TGo)6cZC81;QY`#~Nhy+ zL3V>MC=5ViAUTj4kXi-?22m)Cvj;=CiW0T7t?Ya^eDHHyaz9EFT|ViK(fnPi<|jR!2vNTzGgm zirvU=0Lg*WfYh3pn0WK@@)okPvPy&eLPj`na&mH}Dk>@_q@|@9_xAQG6&4msySlpK zv>zk~QUg*8(gV`V$;p|_z`#Iq;mpX$=q4yASmfp9W!KWuqTbrtDjOCShShH5FaW6m zsRii)=@k$VC}Ct|^d!;$8fD@ z%wk|*&?3?ed`wJCg_4qzNud1P(b1vW+}tc18X79;=;$cn*qi(jY%NI5+7`x0s@>wMMcXP85tvRm5rc0!oJQ4>==MYTp!zF=jg8GrMn*=( z($W%K&U$)!N|J*?lqjrG*GQX;eqOlIB{|DiUkW6`2GL?Kji=a|4IM<|4;t^|9=V;r~d!{ zA0LLP1L*a{Ob#C#8(X5Con7PFwQC!%U%%da^XAPy5WaQm z)`VNPZcVy<`}UOEw{K6qbLS2m-@0{c3P=vB2CV+ZjT=26Gi+^b8$o6=Ffj08T8CAH zm4kyrQ%_GX(A(QPDL6PdE+8NvJ|G|!WgjSPv064NIB+2V0ObxL&;S4c literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/vera16.tff b/demo/src/app/scout/data/vera16.tff new file mode 100644 index 0000000000000000000000000000000000000000..12175d8e4403f7e51c40b7f61f98556ed3b760a6 GIT binary patch literal 52036 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

$) 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 Wf@>%_d(^F?Aut*Oqai@!5C8x>a=M!U literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/vera18.tff b/demo/src/app/scout/data/vera18.tff new file mode 100644 index 0000000000000000000000000000000000000000..766385d4e22c9ad2ba1297bb3170d74ad5a22f98 GIT binary patch literal 63612 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 literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/vera20.tff b/demo/src/app/scout/data/vera20.tff new file mode 100644 index 0000000000000000000000000000000000000000..184d8b4cc78e3a2ab594507c49d8e9de799b65a9 GIT binary patch literal 80131 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 literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/vera22.tff b/demo/src/app/scout/data/vera22.tff new file mode 100644 index 0000000000000000000000000000000000000000..99bc189f0fb7e8b4baf500a2181417934ba0812a GIT binary patch literal 96920 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 literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/verabi10.tff b/demo/src/app/scout/data/verabi10.tff new file mode 100644 index 0000000000000000000000000000000000000000..df938fa118bfe603c04c14dbb165a23f6d9b73f6 GIT binary patch literal 26458 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 P)5oKuwv2|rU=INR+k90L literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/verai16.tff b/demo/src/app/scout/data/verai16.tff new file mode 100644 index 0000000000000000000000000000000000000000..8d8e2f5c3168c6684632b274762442ce2b1b7a49 GIT binary patch literal 52656 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 RQ5!}>U^E0qLx7wR005w&{?7ma literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/whitebar.rgba b/demo/src/app/scout/data/whitebar.rgba new file mode 100644 index 0000000000000000000000000000000000000000..8be87c1e26cc735af6e0cf8dfe09dccd5252a878 GIT binary patch literal 1024 zcmZR;1OEU2FM&rIs}xAj|NsAM{{R2~>i_@$U;h99|Ly<(|F8f5|3C5n|Nr7xH9-ac z|Nrmw|NsBn|NsC0{T~zjg~~ZYm7`Gq|Npo9|NsAq|NsBLz|;yCdHMhU|KtDv{|`pi z{Qv)d;s5{tZ-wfA@c;k+k5D)K`~Uwx$X<|pKL7v!A0!8|1Ef|2X2$>j|6~6D|G)YF z|NjU7|NnpP|Ns9t|NsAg|NsC0PoNm2?*>#3q-M+i|Nrx0`hWfUHR=EV{~P}Q|G)G9 z|NjU6|NnpN|NsA|{{R1f28uy!kQhh~q-Fz14^023PoI|j|NnpW|Ns9t{r~@e`~Uy{ zcmMzYfA9bQ|Mx>Nhz$}0$$`|Ye)sO(lHxw`?f*N z0_g#;dTkVGtXpciFdZ-IkKw<^TTutAGFgee}zhFHzi#tPdCa`Sa&^^#A~?%6B{f literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/genode/launcher.cc b/demo/src/app/scout/genode/launcher.cc new file mode 100644 index 000000000..fa1889773 --- /dev/null +++ b/demo/src/app/scout/genode/launcher.cc @@ -0,0 +1,29 @@ +/* + * \brief Pseudo launcher for the Genode version of Scout + * \author Norman Feske + * \date 2006-08-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include "elements.h" + +static Launchpad launchpad(Genode::env()->ram_session()->quota()); + + +/************************ + ** Launcher interface ** + ************************/ + +void Launcher::launch() +{ + launchpad.start_child(prg_name(), quota(), Genode::Dataspace_capability()); +} diff --git a/demo/src/app/scout/genode/platform_genode.cc b/demo/src/app/scout/genode/platform_genode.cc new file mode 100644 index 000000000..2914ca1c7 --- /dev/null +++ b/demo/src/app/scout/genode/platform_genode.cc @@ -0,0 +1,382 @@ +/* + * \brief Main program of Genode version of Scout + * \author Norman Feske + * \date 2006-08-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "config.h" + +static int _scr_w; +static int _scr_h; +static Framebuffer::Session::Mode _scr_mode; +static Input::Event *_ev_buf; +static char *_scr_adr; +static char *_buf_adr; +static int _mx, _my; +static int _flip_state; /* visible buffer (0..first, 1..second) */ + +static Nitpicker::Connection *_nitpicker; +static Timer::Session *_timer; +static unsigned long _timer_tick; +static int _init_flag; +static bool _view_initialized; +static int _vx, _vy, _vw, _vh; /* view geometry */ + + +/** + * Create view and bring it to front + * + * This function is executed once during the construction of the static + * 'View_client' object in the 'view' function. + */ +static Nitpicker::View_capability create_and_top_view() +{ + Nitpicker::View_capability cap = _nitpicker->create_view(); + Nitpicker::View_client(cap).stack(Nitpicker::View_capability(), true, true); + Nitpicker::View_client(cap).viewport(_vx, _vy, _vw, _vh, 0, _flip_state ? -_scr_h : 0, true); + return cap; +} + + +/** + * Return Nitpicker view for the application + * + * On the first call of this function, the view gets created as static object. + * All subsequent calls just return the pointer to this object. + */ +static Nitpicker::View *view() +{ + static Nitpicker::View_client view(create_and_top_view()); + _view_initialized = true; + return &view; +} + + +using namespace Genode; + + +void *operator new(size_t size) +{ + void *addr = env()->heap()->alloc(size); + if (!addr) { + PERR("env()->heap() has consumed %zd", env()->heap()->consumed()); + PERR("env()->ram_session()->quota = %zd", env()->ram_session()->quota()); + throw Genode::Allocator::Out_of_memory(); + } + return addr; +} + + +/***************** + ** Event queue ** + *****************/ + +class Eventqueue +{ + private: + + static const int queue_size = 1024; + + int _head; + int _tail; + Semaphore _sem; + Genode::Lock _head_lock; /* synchronize add */ + + Event _queue[queue_size]; + + public: + + /** + * Constructor + */ + Eventqueue(): _head(0), _tail(0) + { + memset(_queue, 0, sizeof(_queue)); + } + + void add(Event *ev) + { + Lock::Guard lock_guard(_head_lock); + + if ((_head + 1)%queue_size != _tail) { + + _queue[_head] = *ev; + _head = (_head + 1)%queue_size; + _sem.up(); + } + } + + void get(Event *dst_ev) + { + _sem.down(); + *dst_ev = _queue[_tail]; + _tail = (_tail + 1)%queue_size; + } + + int pending() { return _head != _tail; } + +} _evqueue; + + +/****************** + ** Timer thread ** + ******************/ + +class Timer_thread : public Thread<4096> +{ + private: + + void _import_events() + { + if (_nitpicker->input()->is_pending() == false) return; + + for (int i = 0, num = _nitpicker->input()->flush(); i < num; i++) + { + Event ev; + Input::Event e = _ev_buf[i]; + + if (e.type() == Input::Event::RELEASE + || e.type() == Input::Event::PRESS) { + _mx = e.ax(); + _my = e.ay(); + ev.assign(e.type() == Input::Event::PRESS ? Event::PRESS : Event::RELEASE, + e.ax(), e.ay(), e.keycode()); + _evqueue.add(&ev); + } + + if (e.type() == Input::Event::MOTION) { + _mx = e.ax(); + _my = e.ay(); + ev.assign(Event::MOTION, e.ax(), e.ay(), e.keycode()); + _evqueue.add(&ev); + } + } + } + + public: + + /** + * Constructor + * + * Start thread immediately on construction. + */ + Timer_thread() { start(); } + + void entry() + { + while (1) { + Event ev; + ev.assign(Event::TIMER, _mx, _my, 0); + _evqueue.add(&ev); + + _import_events(); + + _timer->msleep(10); + _timer_tick += 10; + } + } +}; + + +/************************ + ** Platform interface ** + ************************/ + +/** + * Initialization + */ +Platform::Platform(unsigned vx, unsigned vy, unsigned vw, unsigned vh, + unsigned max_vw, unsigned max_vh) +: _max_vw(max_vw), _max_vh(max_vh) +{ + _vx = vx, _vy = vy, _vw = vw, _vh = vh; + + Config::mouse_cursor = 0; + Config::browser_attr = 7; + + /* + * Create temporary nitpicker session just to determine the screen size + * + * NOTE: This approach has the disadvantage creating the nitpicker session + * is not an atomic operation. In theory, both session requests may be + * propagated to different nitpicker instances. + */ + _nitpicker = new (env()->heap()) Nitpicker::Connection(); + _nitpicker->framebuffer()->info(&_scr_w, &_scr_h, &_scr_mode); + destroy(env()->heap(), _nitpicker); + + if (_max_vw) _scr_w = min(_max_vw, _scr_w); + if (_max_vh) _scr_h = min(_max_vh, _scr_h); + + /* + * Allocate a nitpicker buffer double as high as the physical screen to + * use the upper/lower halves for double-buffering. + */ + _nitpicker = new (env()->heap()) Nitpicker::Connection(_scr_w, _scr_h*2, false, _scr_mode); + + static Timer::Connection timer; + _timer = &timer; + + int dummy = 0; + _nitpicker->framebuffer()->info(&dummy, &dummy, &_scr_mode); + + /* + * We use the upper half the allocated nitpicker buffer for '_scr_adr' + * and the lower half for '_buf_adr'. + */ + _scr_adr = env()->rm_session()->attach(_nitpicker->framebuffer()->dataspace()); + _buf_adr = (char *)_scr_adr + _scr_w*_scr_h*Framebuffer::Session::bytes_per_pixel(_scr_mode); + _ev_buf = env()->rm_session()->attach(_nitpicker->input()->dataspace()); + + new (env()->heap()) Timer_thread(); + + /* mark platform as successfully initialized */ + _init_flag = 1; +} + + +/** + * Platform information + */ +int Platform::initialized() { return _init_flag; } +void *Platform::scr_adr() { return _scr_adr; } +void *Platform::buf_adr() { return _buf_adr; } +int Platform::scr_w() { return _scr_w; } +int Platform::scr_h() { return _scr_h; } + + +/** + * Return pixel format used by the platform + */ +Platform::pixel_format Platform::scr_pixel_format() { return RGB565; } + + +/** + * Exchange foreground and back buffers + */ +void Platform::flip_buf_scr() +{ + char *tmp = _buf_adr; + _buf_adr = _scr_adr; + _scr_adr = tmp; + _flip_state ^= 1; + + /* enable new foreground buffer by configuring the view port */ + view_geometry(_vx, _vy, _vw, _vh); +} + + +/** + * Copy background buffer pixels to foreground buffer + */ +void Platform::copy_buf_to_scr(int x, int y, int w, int h) +{ + Genode::size_t bpp = Framebuffer::Session::bytes_per_pixel(_scr_mode); + + /* copy background buffer to foreground buffer */ + int len = w*bpp; + int linelen = _scr_w*bpp; + + char *src = _buf_adr + (y*_scr_w + x)*bpp; + char *dst = _scr_adr + (y*_scr_w + x)*bpp; + + blit(src, linelen, dst, linelen, len, h); +// for (int j = 0; j < h; j++, dst += linelen, src += linelen) +// memcpy(dst, src, len); +} + + +/** + * Flush pixels of specified area + */ +void Platform::scr_update(int x, int y, int w, int h) +{ + if (w <= 0 || h <= 0) return; + + if (_flip_state) y += _scr_h; + + /* + * Initialize Nitpicker view + * + * We defer the initialization of the Nitpicker view to the occurrence of + * the first refresh to avoid artifacts during the startup of the program. + * Previous version used to create the view some time before calling + * 'refresh' for the first time. During that time, moving the mouse over + * the designated view area resulted in parts of the buffer to become + * visible. + */ + view(); + + /* refresh part of the buffer */ + _nitpicker->framebuffer()->refresh(x, y, w, h); +} + + +void Platform::top_view() +{ + if (_view_initialized) + view()->stack(Nitpicker::View_capability(), true, true); +} + + +/** + * Report view geometry changes to Nitpicker. + */ +void Platform::view_geometry(int x, int y, int w, int h, int do_redraw) +{ + _vx = x; _vy = y; _vw = w; _vh = h; + if (_view_initialized) + view()->viewport(_vx, _vy, _vw, _vh, 0, _flip_state ? -_scr_h : 0, do_redraw); +} + + +int Platform::vx() { return _vx; } +int Platform::vy() { return _vy; } +int Platform::vw() { return _vw; } +int Platform::vh() { return _vh; } + + +/** + * Provide timer tick information + */ +unsigned long Platform::timer_ticks() +{ + return _timer_tick; +} + + +/** + * Check if an event is pending + */ +int Platform::event_pending() +{ + return _evqueue.pending(); +} + + +/** + * Wait for an event, Zzz...zz.. + */ +void Platform::get_event(Event *out_e) +{ + _evqueue.get(out_e); +} diff --git a/demo/src/app/scout/genode/startup.cc b/demo/src/app/scout/genode/startup.cc new file mode 100644 index 000000000..f3ed6ee89 --- /dev/null +++ b/demo/src/app/scout/genode/startup.cc @@ -0,0 +1,17 @@ +/* + * \brief Scout native startup code for Genode + * \date 2006-08-28 + * \author Norman Feske + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +int native_startup(int argc, char **argv) +{ + return 0; +} diff --git a/demo/src/app/scout/genode/target.mk b/demo/src/app/scout/genode/target.mk new file mode 100644 index 000000000..3955624fd --- /dev/null +++ b/demo/src/app/scout/genode/target.mk @@ -0,0 +1,35 @@ +TARGET = scout +LIBS = libpng_static libz_static mini_c launchpad scout_widgets +SRC_CC = main.cc doc.cc \ + browser_window.cc png_image.cc \ + navbar.cc about.cc \ + launcher.cc + +CC_OPT += -DPNG_USER_CONFIG + +INC_DIR = $(PRG_DIR)/../include $(PRG_DIR)/../include/genode + +vpath % $(PRG_DIR)/../data +vpath %.cc $(PRG_DIR)/../common + +SRC_BIN += cover.rgba \ + forward.rgba \ + backward.rgba \ + home.rgba \ + index.rgba \ + about.rgba \ + pointer.rgba \ + nav_next.rgba \ + nav_prev.rgba + +SRC_BIN += ior.map + +# +# Browser content +# +CONTENT_IMAGES = launchpad.png liquid_fb_small.png x-ray_small.png \ + setup.png genode_logo.png + +SRC_BIN += $(addprefix $(REP_DIR)/doc/img/, $(CONTENT_IMAGES)) + +vpath % $(REP_DIR)/doc/img diff --git a/demo/src/app/scout/include/browser.h b/demo/src/app/scout/include/browser.h new file mode 100644 index 000000000..9968a959a --- /dev/null +++ b/demo/src/app/scout/include/browser.h @@ -0,0 +1,162 @@ +/* + * \brief Browser interface + * \date 2005-11-03 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _BROWSER_H_ +#define _BROWSER_H_ + +#include "elements.h" +#include "history.h" + + +extern Document *create_about(); + +class Browser +{ + protected: + + Document *_document; + Document *_about; + History _history; + int _voffset; + int _ypos; + + /** + * Define content to present in browser window + */ + virtual void _content(Element *content) = 0; + + /** + * Request current content + */ + virtual Element *_content() = 0; + + public: + + /** + * Constructor + */ + explicit Browser(int voffset = 0) + { + _document = 0; + _about = create_about(); + _ypos = 0; + _voffset = voffset; + } + + virtual ~Browser() { } + + /** + * Accessor functions + */ + virtual int ypos() { return _ypos; }; + + /** + * Return current browser position + */ + virtual int view_x() { return 0; } + virtual int view_y() { return 0; } + + /** + * Define vertical scroll offset of document + */ + virtual void ypos(int ypos) = 0; + + /** + * Format browser window + */ + virtual void format(int w, int h) { } + + /** + * Travel backward in history + * + * \retval 1 success + * \retval 0 end of history is reached + */ + virtual int go_backward() + { + _history.assign(curr_anchor()); + if (!_history.go(History::BACKWARD)) return 0; + go_to(_history.curr(), 0); + return 1; + } + + /** + * Follow history forward + * + * \retval 1 success, + * \retval 0 end of history is reached + */ + virtual int go_forward() + { + _history.assign(curr_anchor()); + if (!_history.go(History::FORWARD)) return 0; + go_to(_history.curr(), 0); + return 1; + } + + /** + * Follow specified link location + * + * \param add_history if set to 1, add new location to history + */ + virtual void go_to(Anchor *anchor, int add_history = 1) + { + if (!anchor) return; + + if (add_history) { + _history.assign(curr_anchor()); + _history.add(anchor); + } + + Element *new_content = anchor->chapter(); + if (new_content) + _content(new_content); + + ypos(0); + ypos(_ypos - anchor->abs_y() + _voffset); + + if (new_content) { + new_content->curr_link_destination(0); + new_content->refresh(); + } + } + + /** + * Get current anchor + * + * The current anchor is the element that is visible at the + * top of the browser window. It depends on the scroll position. + * We need to store these elements in the history to recover + * the right viewport on the history entries even after + * reformatting the document. + */ + virtual Anchor *curr_anchor() = 0; + + /** + * Display table of contents + */ + int go_toc() { go_to(_document->toc, 1); return 1; } + + /** + * Go to title page + */ + int go_home() { go_to(_document); return 1; } + + /** + * Go to about page + */ + int go_about() { go_to(_about); return 1; } +}; + + +#endif /* _BROWSER_H_ */ diff --git a/demo/src/app/scout/include/browser_window.h b/demo/src/app/scout/include/browser_window.h new file mode 100644 index 000000000..d908fd02b --- /dev/null +++ b/demo/src/app/scout/include/browser_window.h @@ -0,0 +1,159 @@ +/* + * \brief Browser window interface + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _BROWSER_WINDOW_H_ +#define _BROWSER_WINDOW_H_ + +#include "elements.h" +#include "widgets.h" +#include "sky_texture.h" +#include "refracted_icon.h" +#include "scrollbar.h" +#include "platform.h" +#include "redraw_manager.h" +#include "browser.h" +#include "window.h" +#include "titlebar.h" + + +template +class Browser_window : public Scrollbar_listener, + public Browser, + public Window +{ + enum { + ICON_HOME = 0, + ICON_BACKWARD = 1, + ICON_FORWARD = 2, + ICON_INDEX = 3, + ICON_ABOUT = 4 + }; + + private: + + /** + * Constants + */ + enum { + _NUM_ICONS = 5, /* number of icons */ + _IW = 64, /* browser icon width */ + _IH = 64, /* browser icon height */ + _TH = 32, /* height of title bar */ + _PANEL_W = 320, /* panel tile width */ + _PANEL_H = _IH, /* panel tile height */ + _SB_XPAD = 5, /* hor. pad of scrollbar */ + _SB_YPAD = 10, /* vert. pad of scrollbar */ + _SCRATCH = 7 /* scratching factor */ + }; + + /** + * General properties + */ + int _attr; /* attribute mask */ + + /** + * Widgets + */ + Titlebar _titlebar; + Sky_texture _texture; + PT _icon_fg [_NUM_ICONS][_IH][_IW]; + unsigned char _icon_fg_alpha [_NUM_ICONS][_IH][_IW]; + Refracted_icon _icon [_NUM_ICONS]; + PT _icon_backbuf [_IH*2][_IW*2]; + PT _panel_fg [_PANEL_H][_PANEL_W]; + unsigned char _panel_fg_alpha [_PANEL_H][_PANEL_W]; + short _panel_distmap [_PANEL_H*2][_PANEL_W*2]; + Refracted_icon _panel; + PT _panel_backbuf [_PANEL_H*2][_PANEL_W*2]; + Horizontal_shadow _shadow; + Scrollbar _scrollbar; + Fade_icon _glow_icon[_NUM_ICONS]; + Docview _docview; + Fade_icon _sizer; + + protected: + + /** + * Browser interface + */ + void _content(Element *content); + Element *_content(); + + public: + + /** + * Browser window attributes + */ + enum { + ATTR_SIZER = 0x1, /* browser window has resize handle */ + ATTR_TITLEBAR = 0x2, /* browser window has titlebar */ + ATTR_BORDER = 0x4, /* draw black outline around browser */ + }; + + /** + * Constructor + * + * \param scr_adr base address of screen buffer + * \param scr_w width of screen buffer + * \param scr_h height of screen buffer + * \param doc initial content + * \param w, h initial size of the browser window + */ + Browser_window(Document *content, Platform *pf, + Redraw_manager *redraw, int max_w, int max_h, + int attr = ATTR_SIZER | ATTR_TITLEBAR); + + /** + * Return visible document offset + */ + inline int doc_offset() { return 10 + _IH + (_attr & ATTR_TITLEBAR ? _TH : 0); } + + /** + * Define vertical scroll offset of document + * + * \param update_scrollbar if set to one, adjust scrollbar properties + * to the new view position. + */ + void ypos_sb(int ypos, int update_scrollbar = 1); + + /** + * Browser interface + */ + void format(int w, int h); + void ypos(int ypos) { ypos_sb(ypos, 1); } + Anchor *curr_anchor(); + Browser *browser() { return this; } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y) + { + ::Parent_element::draw(c, x, y); + + if (_attr & ATTR_BORDER) { + Color col(0, 0, 0); + c->draw_box(0, 0, _w, 1, col); + c->draw_box(0, _h - 1, _w, 1, col); + c->draw_box(0, 1, 1, _h - 2, col); + c->draw_box(_w - 1, 1, 1, _h - 2, col); + } + }; + + /** + * Scrollbar listener interface + */ + void handle_scroll(int view_pos); +}; + +#endif /* _BROWSER_WINDOW_H_ */ diff --git a/demo/src/app/scout/include/canvas.h b/demo/src/app/scout/include/canvas.h new file mode 100644 index 000000000..e8b99ed6b --- /dev/null +++ b/demo/src/app/scout/include/canvas.h @@ -0,0 +1,325 @@ +/* + * \brief Generic interface of graphics backend and chunky template + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CANVAS_H_ +#define _CANVAS_H_ + +#include "color.h" +#include "font.h" + + +class Canvas +{ + protected: + + int _clip_x1, _clip_y1; /* left-top of clipping area */ + int _clip_x2, _clip_y2; /* right-bottom of clipping area */ + + int _w, _h; /* current size of canvas */ + int _capacity; /* max number of pixels */ + + public: + + virtual ~Canvas() { } + + /** + * Define clipping rectangle + */ + void clip(int x, int y, int w, int h) + { + /* calculate left-top and right-bottom points of clipping rectangle */ + _clip_x1 = x; + _clip_y1 = y; + _clip_x2 = x + w - 1; + _clip_y2 = y + h - 1; + + /* check against canvas boundaries */ + if (_clip_x1 < 0) _clip_x1 = 0; + if (_clip_y1 < 0) _clip_y1 = 0; + if (_clip_x2 >= _w && w > 0) _clip_x2 = _w - 1; + if (_clip_y2 >= _h && w > 0) _clip_y2 = _h - 1; + } + + /** + * Request clipping rectangle + */ + int clip_x1() { return _clip_x1; } + int clip_y1() { return _clip_y1; } + int clip_x2() { return _clip_x2; } + int clip_y2() { return _clip_y2; } + + int w() { return _w; } + int h() { return _h; } + + /** + * Set logical size of canvas + */ + int set_size(int w, int h) + { + if (w*h > _capacity) return -1; + _w = w; + _h = h; + clip(0, 0, w - 1, h - 1); + return 0; + } + + /** + * Draw filled box + */ + virtual void draw_box(int x, int y, int w, int h, Color c) = 0; + + /** + * Draw string + */ + virtual void draw_string(int x, int y, Font *font, Color color, const char *str, int len) = 0; + + /** + * Return base address + */ + virtual void *addr() = 0; + + /** + * Define base address of pixel data + */ + virtual void addr(void *) = 0; + + /** + * Anonymous texture struct + */ + class Texture {}; + + /** + * Allocate texture container + */ + virtual Texture *alloc_texture(int w, int h) = 0; + + /** + * Free texture container + */ + virtual void free_texture(Texture *texture) = 0; + + /** + * Assign rgba values to texture line + */ + virtual void set_rgba_texture(Texture *dst, unsigned char *rgba, int len, int y) = 0; + + /** + * Draw texture + */ + virtual void draw_texture(Texture *src, int x, int y) { } +}; + + +template +class Pixel_rgba +{ + private: + + /** + * Shift left with positive or negative shift value + */ + inline int shift(int value, int shift) + { + return shift > 0 ? value << shift : value >> -shift; + } + + public: + + static const int r_mask = R_MASK, r_shift = R_SHIFT; + static const int g_mask = G_MASK, g_shift = G_SHIFT; + static const int b_mask = B_MASK, b_shift = B_SHIFT; + static const int a_mask = A_MASK, a_shift = A_SHIFT; + + ST pixel; + + /** + * Constructors + */ + Pixel_rgba() {} + + Pixel_rgba(int red, int green, int blue, int alpha = 255) + { + rgba(red, green, blue, alpha); + } + + /** + * Assign new rgba values + */ + void rgba(int red, int green, int blue, int alpha = 255) + { + pixel = (shift(red, r_shift) & r_mask) + | (shift(green, g_shift) & g_mask) + | (shift(blue, b_shift) & b_mask) + | (shift(alpha, a_shift) & a_mask); + } + + inline int r() { return shift(pixel & r_mask, -r_shift); } + inline int g() { return shift(pixel & g_mask, -g_shift); } + inline int b() { return shift(pixel & b_mask, -b_shift); } + + /** + * Multiply pixel with alpha value + */ + static inline Pixel_rgba blend(Pixel_rgba pixel, int alpha); + + /** + * Mix two pixels at the ratio specified as alpha + */ + static inline Pixel_rgba mix(Pixel_rgba p1, Pixel_rgba p2, int alpha); + + /** + * Compute average color value of two pixels + */ + static inline Pixel_rgba avr(Pixel_rgba p1, Pixel_rgba p2); + + /** + * Compute average color value of four pixels + */ + static inline Pixel_rgba avr(Pixel_rgba p1, Pixel_rgba p2, + Pixel_rgba p3, Pixel_rgba p4) + { + return avr(avr(p1, p2), avr(p3, p4)); + } +} __attribute__((packed)); + + +template +class Chunky_canvas : public Canvas +{ + protected: + + PT *_addr; /* base address of pixel buffer */ + + /** + * Utilities + */ + static inline int min(int a, int b) { return a < b ? a : b; } + static inline int max(int a, int b) { return a > b ? a : b; } + + public: + + /** + * Initialize canvas + */ + void init(PT *addr, long capacity) + { + _addr = addr; + _capacity = capacity; + _w = _h = 0; + _clip_x1 = _clip_y1 = 0; + _clip_x2 = _clip_y2 = 0; + } + + + /**************************************** + ** Implementation of Canvas interface ** + ****************************************/ + + void draw_box(int x1, int y1, int w, int h, Color color) + { + int x2 = x1 + w - 1; + int y2 = y1 + h - 1; + + /* check clipping */ + if (x1 < _clip_x1) x1 = _clip_x1; + if (y1 < _clip_y1) y1 = _clip_y1; + if (x2 > _clip_x2) x2 = _clip_x2; + if (y2 > _clip_y2) y2 = _clip_y2; + + if ((x1 > x2) || (y1 > y2)) return; + + PT pix(color.r, color.g, color.b); + PT *dst, *dst_line = _addr + _w*y1 + x1; + + int alpha = color.a; + + /* + * ??? + * + * Why can dst not be declared in the head of the inner for loop? + * Can I use the = operator for initializing a Pixel with a Color? + * + * ??? + */ + + if (alpha == Color::OPAQUE) + for (h = y2 - y1 + 1; h--; dst_line += _w) + for (dst = dst_line, w = x2 - x1 + 1; w--; dst++) + *dst = pix; + + else if (alpha != Color::TRANSPARENT) + for (h = y2 - y1 + 1; h--; dst_line += _w) + for (dst = dst_line, w = x2 - x1 + 1; w--; dst++) + *dst = PT::mix(*dst, pix, alpha); + } + + void draw_string(int x, int y, Font *font, Color color, const char *sstr, int len) + { + const unsigned char *str = (const unsigned char *)sstr; + + if (!str || !font) return; + + unsigned char *src = font->img; + int d, h = font->img_h; + + /* check top clipping */ + if ((d = _clip_y1 - y) > 0) { + src += d*font->img_w; + y += d; + h -= d; + } + + /* check bottom clipping */ + if ((d = y + h -1 - _clip_y2) > 0) + h -= d; + + if (h < 1) return; + + /* skip hidden glyphs */ + for ( ; *str && len && (x + font->wtab[*str] < _clip_x1); len--) + x += font->wtab[*str++]; + + PT *dst = _addr + y*_w; + PT pix(color.r, color.g, color.b); + int alpha = color.a; + + /* draw glyphs */ + for ( ; *str && len && (x <= _clip_x2); str++, len--) { + + int w = font->wtab[*str]; + int start = max(0, _clip_x1 - x); + int end = min(w - 1, _clip_x2 - x); + PT *d = dst + x; + unsigned char *s = src + font->otab[*str]; + + for (int j = 0; j < h; j++, s += font->img_w, d += _w) + for (int i = start; i <= end; i++) + if (s[i]) d[i] = PT::mix(d[i], pix, (alpha*s[i]) >> 8); + + x += w; + } + } + + void *addr() { return _addr; } + void addr(void *addr) { _addr = static_cast(addr); } + + Texture *alloc_texture(int w, int h); + void free_texture(Texture *texture); + void set_rgba_texture(Texture *dst, unsigned char *rgba, int len, int y); + void draw_texture(Texture *src, int x, int y); +}; + +#endif /* _CANVAS_H_ */ diff --git a/demo/src/app/scout/include/canvas_rgb565.h b/demo/src/app/scout/include/canvas_rgb565.h new file mode 100644 index 000000000..56f7e5024 --- /dev/null +++ b/demo/src/app/scout/include/canvas_rgb565.h @@ -0,0 +1,236 @@ +/* + * \brief Template specializations for the RGB565 pixel format + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CANVAS_RGB565_H_ +#define _CANVAS_RGB565_H_ + +#include "miscmath.h" +#include "canvas.h" +#include "alloc.h" + +typedef Pixel_rgba Pixel_rgb565; + + +template <> +inline Pixel_rgb565 Pixel_rgb565::blend(Pixel_rgb565 src, int alpha) +{ + Pixel_rgb565 res; + res.pixel = ((((alpha >> 3) * (src.pixel & 0xf81f)) >> 5) & 0xf81f) + | ((( alpha * (src.pixel & 0x07c0)) >> 8) & 0x07c0); + return res; +} + + +template <> +inline Pixel_rgb565 Pixel_rgb565::mix(Pixel_rgb565 p1, Pixel_rgb565 p2, int alpha) +{ + Pixel_rgba res; + + /* + * We substract the alpha from 264 instead of 255 to + * compensate the brightness loss caused by the rounding + * error of the blend function when having only 5 bits + * per channel. + */ + res.pixel = blend(p1, 264 - alpha).pixel + blend(p2, alpha).pixel; + return res; +} + + +template <> +inline Pixel_rgb565 Pixel_rgb565::avr(Pixel_rgb565 p1, Pixel_rgb565 p2) +{ + Pixel_rgb565 res; + res.pixel = ((p1.pixel&0xf7df)>>1) + ((p2.pixel&0xf7df)>>1); + return res; +} + + +static const int dither_size = 16; +static const int dither_mask = dither_size - 1; + +static const int dither_matrix[dither_size][dither_size] = { + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } +}; + + +typedef Chunky_canvas Canvas_rgb565; + + +class Texture_rgb565 : public Canvas::Texture +{ + private: + + int _w, _h; /* size of texture */ + unsigned char *_alpha; /* alpha channel */ + Pixel_rgb565 *_pixel; /* pixel data */ + + public: + + /** + * Constructor + */ + Texture_rgb565(int w, int h) + { + _w = w; + _h = h; + _alpha = (unsigned char *)scout_malloc(w*h); + _pixel = (Pixel_rgb565 *)scout_malloc(w*h*sizeof(Pixel_rgb565)); + } + + Texture_rgb565(Pixel_rgb565 *pixel, unsigned char *alpha, int w, int h): + _w(w), _h(h), _alpha(alpha), _pixel(pixel) { } + + /** + * Destructor + */ + ~Texture_rgb565() + { + scout_free(_alpha); + scout_free(_pixel); + _w = _h = 0; + } + + /** + * Accessor functions + */ + inline unsigned char *alpha() { return _alpha; } + inline Pixel_rgb565 *pixel() { return _pixel; } + inline int w() { return _w; } + inline int h() { return _h; } + + /** + * Convert rgba data line to texture + */ + void rgba(unsigned char *rgba, int len, int y) + { + if (len > _w) len = _w; + if (y < 0 || y >= _h) return; + + int const *dm = dither_matrix[y & dither_mask]; + + Pixel_rgb565 *dst_pixel = _pixel + y*_w; + unsigned char *dst_alpha = _alpha + y*_w; + + for (int i = 0; i < len; i++) { + + int v = dm[i & dither_mask] >> 5; + int r = *rgba++ + v; + int g = *rgba++ + v; + int b = *rgba++ + v; + int a = *rgba++ + v; + + dst_pixel[i].rgba(min(r, 255), min(g, 255), min(b, 255)); + dst_alpha[i] = min(a, 255); + } + } +}; + + +template <> +inline Canvas::Texture *Canvas_rgb565::alloc_texture(int w, int h) +{ + return new Texture_rgb565(w, h); +} + + +template <> +inline void Canvas_rgb565::free_texture(Canvas::Texture *texture) +{ + if (texture) delete static_cast(texture); +} + + +template <> +inline void Canvas_rgb565::set_rgba_texture(Canvas::Texture *dst, + unsigned char *rgba, int len, int y) +{ + (static_cast(dst))->rgba(rgba, len, y); +} + + +template <> +inline void Canvas_rgb565::draw_texture(Texture *src_texture, int x1, int y1) +{ + Texture_rgb565 *src = static_cast(src_texture); + unsigned char *src_alpha = src->alpha(); + Pixel_rgb565 *src_pixel = src->pixel(); + + int x2 = x1 + src->w() - 1; + int y2 = y1 + src->h() - 1; + + /* right clipping */ + if (x2 > _clip_x2) + x2 = _clip_x2; + + /* bottom clipping */ + if (y2 > _clip_y2) + y2 = _clip_y2; + + /* left clipping */ + if (x1 < _clip_x1) { + src_alpha += _clip_x1 - x1; + src_pixel += _clip_x1 - x1; + x1 = _clip_x1; + } + + /* top clipping */ + if (y1 < _clip_y1) { + int offset = (_clip_y1 - y1)*src->w(); + src_alpha += offset; + src_pixel += offset; + y1 = _clip_y1; + } + + /* check if there is anything left */ + if (x1 > x2 || y1 > y2) return; + + int w = x2 - x1 + 1; + int h = y2 - y1 + 1; + + Pixel_rgb565 *dst_pixel = _addr + y1*_w + x1; + + for (int j = 0; j < h; j++) { + + Pixel_rgb565 *sp = src_pixel; + unsigned char *sa = src_alpha; + Pixel_rgb565 *d = dst_pixel; + + /* copy texture line */ + for (int i = 0; i < w; i++, sp++, sa++, d++) + *d = Pixel_rgb565::mix(*d, *sp, *sa); + + /* add line offsets to source texture and destination */ + src_pixel += src->w(); + src_alpha += src->w(); + dst_pixel += _w; + } +} + + +#endif /* _CANVAS_RGB565_ */ diff --git a/demo/src/app/scout/include/color.h b/demo/src/app/scout/include/color.h new file mode 100644 index 000000000..2603ebef2 --- /dev/null +++ b/demo/src/app/scout/include/color.h @@ -0,0 +1,52 @@ +/* + * \brief Color representation + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _COLOR_H_ +#define _COLOR_H_ + + +class Color +{ + public: + + enum { + TRANSPARENT = 0, + OPAQUE = 255 + }; + + int r, g, b, a; + + /** + * Constructors + */ + Color(int red, int green, int blue, int alpha = 255) + { + rgba(red, green, blue, alpha); + } + + Color() { rgba(0, 0, 0); } + + /** + * Convenience function: Assign rgba values + */ + inline void rgba(int red, int green, int blue, int alpha = 255) + { + r = red; + g = green; + b = blue; + a = alpha; + } +}; + + +#endif /* _COLOR_H_ */ diff --git a/demo/src/app/scout/include/config.h b/demo/src/app/scout/include/config.h new file mode 100644 index 000000000..83ed6f73a --- /dev/null +++ b/demo/src/app/scout/include/config.h @@ -0,0 +1,26 @@ +/* + * \brief Scout configuration + * \date 2005-11-10 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +namespace Config +{ + extern int iconbar_detail; + extern int background_detail; + extern int mouse_cursor; + extern int browser_attr; +}; + + +#endif /* _CONFIG_H_ */ diff --git a/demo/src/app/scout/include/elements.h b/demo/src/app/scout/include/elements.h new file mode 100644 index 000000000..3dc598256 --- /dev/null +++ b/demo/src/app/scout/include/elements.h @@ -0,0 +1,810 @@ +/* + * \brief Document structure elements + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _ELEMENTS_H_ +#define _ELEMENTS_H_ + +#include "printf.h" +#include "string.h" +#include "canvas.h" +#include "event.h" +#include "color.h" +#include "fader.h" +#include "font.h" + + +/** + * Textual style + * + * A style describes the font, color and accentuations of tokens. + */ +class Style +{ + public: + + enum { + ATTR_BOLD = 0x1, + }; + + Font *font; + Color color; + int attr; + + Style(Font *f, Color c, int a) + { + font = f; + color = c; + attr = a; + } +}; + + +class Parent_element; +class Browser; + +class Element +{ + protected: + + int _x, _y; /* relative position managed by parent */ + int _w, _h; /* size managed by parent */ + int _min_w, _min_h; /* min size managed by element */ + Parent_element *_parent; /* parent in element hierarchy */ + Event_handler *_evh; /* event handler object */ + struct { + int mfocus : 1; /* element has mouse focus */ + int selected : 1; /* element has selected state */ + int takes_focus : 1; /* element highlights mouse focus */ + int link : 1; /* element is a link */ + int chapter : 1; /* display element as single page */ + int findable : 1; /* regard element in find function */ + int bottom : 1; /* place element to the bottom */ + } _flags; + + public: + + Element *next; /* managed by parent */ + + /** + * Constructor + */ + Element() + { + next = 0; + _parent = 0; + _min_w = 0; + _min_h = 0; + _x = _y = 0; + _w = _h = 0; + _evh = 0; + _flags.mfocus = _flags.selected = 0; + _flags.takes_focus = _flags.link = 0; + _flags.chapter = 0; + _flags.findable = 1; + _flags.bottom = 0; + } + + /** + * Destructor + */ + virtual ~Element(); + + /** + * Accessor functionse + */ + inline int min_w() { return _min_w; } + inline int min_h() { return _min_h; } + inline int x() { return _x; } + inline int y() { return _y; } + inline int w() { return _w; } + inline int h() { return _h; } + inline int is_link() { return _flags.link; } + inline int is_bottom() { return _flags.bottom; } + + inline void findable(int flag) { _flags.findable = flag; } + + /** + * Set geometry of the element + * + * This function should only be called by the immediate parent + * element. + */ + virtual void geometry(int x, int y, int w, int h) + { + _x = x; _y = y; _w = w; _h = h; + } + + /** + * Set/reset the mouse focus + */ + virtual void mfocus(int flag) + { + if ((_flags.mfocus == flag) || !_flags.takes_focus) return; + _flags.mfocus = flag; + refresh(); + } + + /** + * Define/request parent of an element + */ + inline void parent(Parent_element *parent) { _parent = parent; } + inline Parent_element *parent() { return _parent; } + + /** + * Define event handler object + */ + inline void event_handler(Event_handler *evh) { _evh = evh; } + + /** + * Check if element is completely clipped and draw it otherwise + */ + inline void try_draw(Canvas *c, int x, int y) + { + /* check if element is completely outside the clipping area */ + if ((_x + x > c->clip_x2()) || (_x + x + _w - 1 < c->clip_x1()) + || (_y + y > c->clip_y2()) || (_y + y + _h - 1 < c->clip_y1())) + return; + + /* call actual drawing function */ + draw(c, x, y); + } + + /** + * Format element and all child elements to specified width + */ + virtual void format_fixed_width(int w) { } + + /** + * Draw function + * + * This function must not be called directly. + * Instead, the function try_draw should be called. + */ + virtual void draw(Canvas *c, int x, int y) { } + + /** + * Find top-most element at specified position + * + * The default implementation can be used for elements without + * children. It just the element position and size against the + * specified position. + */ + virtual Element *find(int x, int y); + + /** + * Find the back-most element at specified y position + * + * This function is used to query a document element at + * the current scroll position of the window. This way, + * we can adjust the y position to the right value + * when we browse the history. + */ + virtual Element *find_by_y(int y); + + /** + * Request absolute position of an element + */ + int abs_x(); + int abs_y(); + + /** + * Update area of an element on screen + * + * We propagate the redraw request through the element hierarchy to + * the parent. The root parent should overwrite this function with + * a function that performs the actual redraw. + */ + virtual void redraw_area(int x, int y, int w, int h); + + /** + * Trigger the refresh of an element on screen + */ + inline void refresh() { redraw_area(0, 0, _w, _h); } + + /** + * Handle user input or timer event + */ + inline void handle_event(Event &ev) { if (_evh) _evh->handle(ev); } + + /** + * Request the chapter in which the element lives + */ + Element *chapter(); + + /** + * Request the browser in which the element lives + */ + virtual Browser *browser(); + + /** + * Fill image cache for element + */ + virtual void fill_cache(Canvas *c) { } + + /** + * Flush image cache for element + */ + virtual void flush_cache(Canvas *c) { } + + /** + * Propagate current link destination + * + * Elements that reference the specified link destination + * should give feedback. Other elements should ignore this + * function. + */ + virtual void curr_link_destination(Element *e) { } +}; + + +class Parent_element : public Element +{ + protected: + + Element *_first; + Element *_last; + + /** + * Format child element by a given width an horizontal offset + */ + int _format_children(int x, int w); + + public: + + /** + * Constructor + */ + Parent_element() { _first = _last = 0; } + + /** + * Adopt a child element + */ + void append(Element *e); + + /** + * Release child element from parent element + */ + void remove(Element *e); + + /** + * Dispose references to the specified element + * + * The element is not necessarily an immediate child but some element + * of the element-subtree. This function gets propagated to the root + * parent (e.g., user state manager), which can reset the mouse focus + * of the focused element vanishes. + */ + virtual void forget(Element *e); + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y); + Element *find(int x, int y); + Element *find_by_y(int y); + void fill_cache(Canvas *c); + void flush_cache(Canvas *c); + void curr_link_destination(Element *e); + void geometry(int x, int y, int w, int h); +}; + + +/** + * String token + * + * A Token is a group of characters that are handled as an atomic text unit. + * Line wrapping is performed at the granularity of tokens. + */ +class Token : public Element +{ + protected: + + const char *_str; /* start of string */ + int _len; /* length of string */ + Style *_style; /* textual style */ + Color _col; /* current text color */ + Color _outline; /* outline color */ + + public: + + /** + * Constructor + */ + Token(Style *style, const char *str, int len); + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y); + inline void refresh() { redraw_area(-1, 0, _w + 1, _h); } +}; + + +/** + * Link anchor + * + * An anchor marks a location within a document that can be addressed by a + * link. + */ +typedef Element Anchor; + + +/** + * Link that references an anchor within the document + */ +class Link +{ + protected: + + Anchor *_dst; /* link destination */ + + public: + + /** + * Constructor + */ + explicit Link(Anchor *dst) { _dst = dst; } + + /** + * Accessor function + */ + Anchor *dst() { return _dst; } +}; + + +/** + * Textual link + */ +class Link_token : public Token, public Link, public Event_handler, public Fader +{ + private: + + enum { _MAX_ALPHA = 50 }; + + public: + + /** + * Constructor + */ + Link_token(Style *style, const char *str, int len, Anchor *dst) + : Token(style, str, len), Link(dst) + { + _flags.takes_focus = 1; + _flags.link = 1; + _curr_value = 0; + event_handler(this); + } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y) + { + _outline.rgba(_style->color.r, + _style->color.g, + _style->color.b, _curr_value); + + ::Token::draw(c, x, y); + } + + void curr_link_destination(Element *dst) + { + if (dst == _dst && _curr_value != _MAX_ALPHA) + fade_to(_MAX_ALPHA, 50); + + if (dst != _dst && _curr_value != 0) + fade_to(0, 2); + } + + /** + * Event handler interface + */ + void handle(Event &e); + + /** + * Tick interface + */ + int on_tick() + { + /* call on_tick function of the fader */ + if (::Fader::on_tick() == 0) return 0; + + refresh(); + return 1; + } +}; + + +class Launchpad; +class Launcher_config; +class Launcher : public Anchor +{ + private: + + const char *_prg_name; /* null-terminated name of the program */ + int _active; + int _exec_once; + Launchpad *_launchpad; + unsigned long _quota; + Launcher_config *_config; + + public: + + /** + * Constructors + */ + Launcher(const char *prg_name, int exec_once = 0, + unsigned long quota = 0, Launcher_config *config = 0) : + _prg_name(prg_name), _active(1), + _exec_once(exec_once), _quota(quota), _config(config) { } + + Launcher(const char *prg_name, Launchpad *launchpad, + unsigned long quota, Launcher_config *config = 0) : + _prg_name(prg_name), _launchpad(launchpad), _quota(quota), + _config(config) { } + + int active() { return _active; } + + const char *prg_name() { return _prg_name; } + + void quota(unsigned long quota) { _quota = quota; } + + unsigned long quota() { return _quota; } + + Launcher_config *config() { return _config; } + + /** + * Launch program + */ + void launch(); +}; + + +/** + * Executable launcher link + * + * This is a special link that enables us to start external applications. + */ +class Launcher_link_token : public Link_token +{ + public: + + /** + * Constructor + */ + Launcher_link_token(Style *style, const char *str, int len, Launcher *l) + : Link_token(style, str, len, l) { } + + /** + * Event handler interface + */ + void handle(Event &e); +}; + + +/** + * Text block + * + * A block is a group of tokens that form a paragraph. A block layouts its + * tokens while using line wrapping. + */ +class Block : public Parent_element +{ + public: + + enum Alignment { LEFT, CENTER, RIGHT }; + enum Text_type { PLAIN, LINK, LAUNCHER }; + + private: + + int _second_indent; /* indentation of second line */ + Alignment _align; /* text alignment */ + + /** + * Append text to block + */ + void append_text(const char *str, Style *style, Text_type, + Anchor *a, Launcher *l); + + public: + + /** + * Constructors + */ + explicit Block(int second_indent = 0) + { + _align = LEFT; + _second_indent = second_indent; + } + + explicit Block(Alignment align) + { + _align = align; + _second_indent = 0; + } + + /** + * Define alignment of text + */ + void align(Alignment); + + /** + * Append a string of space-separated words + */ + void append_plaintext(const char *str, Style *style) + { + append_text(str, style, PLAIN, 0, 0); + } + + /** + * Append a string of space-separated words a link + * + * \param dst anchor that defines the link destination + */ + void append_linktext(const char *str, Style *style, Anchor *a) + { + append_text(str, style, LINK, a, 0); + } + + /** + * Append a string of space-separated words a launcher-link + */ + void append_launchertext(const char *str, Style *style, Launcher *l) + { + append_text(str, style, LAUNCHER, 0, l); + } + + /** + * Element interface + */ + void format_fixed_width(int w); +}; + + +/** + * Horizontally centered content + */ +class Center : public Parent_element +{ + public: + + /** + * Constructor + */ + explicit Center(Element *content = 0) + { + if (content) append(content); + } + + /** + * Element interface + */ + void format_fixed_width(int w); +}; + + +/** + * PNG Image + */ +class Png_image : public Element +{ + private: + + void *_png_data; + Canvas::Texture *_texture; + + public: + + /** + * Constructor + */ + explicit Png_image(void *png_data) + { + _png_data = png_data; + _texture = 0; + } + + /** + * Accessor functions + */ + inline void *png_data() { return _png_data; } + + /** + * Element interface + */ + void fill_cache(Canvas *c); + void flush_cache(Canvas *c); + void draw(Canvas *c, int x, int y); +}; + + +/** + * Document + */ +class Chapter; +class Document : public Parent_element +{ + public: + + Chapter *toc; /* table of contents */ + const char *title; /* document title */ + + /** + * Constructor + */ + Document() + { + toc = 0; title = ""; _flags.chapter = 1; + } + + /** + * Element interface + */ + void format_fixed_width(int w) + { + _min_h = _format_children(0, w); + _min_w = w; + } +}; + + +/** + * Chapter + */ +class Chapter : public Document { }; + + +/** + * Spacer + * + * A spacer is a place holder that consumes some screen space. It is used for + * tweaking the layout of the document. + */ +class Spacer : public Element +{ + public: + + /** + * Constructor + */ + Spacer(int w, int h) + { + _min_w = _w = w; + _min_h = _h = h; + } +}; + + +/** + * Verbatim text block + * + * A verbatim text block consists of a number of preformatted text lines. + * The text is printed in a monospaced font and the whole verbatim area + * has a shaded background. + */ +class Verbatim : public Parent_element +{ + public: + + Color bgcol; + + /** + * Constructor + */ + explicit Verbatim(Color bg) { bgcol = bg; } + + /** + * Append verbatim text line + */ + void append_textline(const char *str, Style *style) + { + append(new Token(style, str, strlen(str))); + } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y); + void format_fixed_width(int w); +}; + + +/** + * An iten consists of a item tag and a list of blocks + */ +class Item : public Parent_element +{ + public: + + int _tag_ident; + const char *_tag; + Style *_style; + + /** + * Constructor + */ + Item(Style *style, const char *str, int ident) + { + _style = style; + _tag = str; + _tag_ident = ident; + } + + /** + * Element interface + */ + void format_fixed_width(int w) + { + _min_h = _format_children(_tag_ident, w - _tag_ident); + _min_w = w; + } + + void draw(Canvas *c, int x, int y) + { + c->draw_string(_x + x, _y + y, _style->font, _style->color, _tag, 255); + Parent_element::draw(c, x, y); + } +}; + + +/** + * Document navigation bar + */ +class Generic_icon; +class Navbar : public Parent_element, public Fader +{ + private: + + Block *_next_title; + Block *_prev_title; + + Anchor *_next_anchor; + Anchor *_prev_anchor; + + public: + + /** + * These pointers must be initialized such that they + * point to valid Icon widgets that are used as graphics + * for the navigation bar. + */ + static Generic_icon *next_icon; + static Generic_icon *prev_icon; + static Generic_icon *nbox_icon; + static Generic_icon *pbox_icon; + + /** + * Constructor + */ + Navbar(); + + /** + * Define link to next and previous chapter + */ + void next_link(const char *title, Anchor *dst); + void prev_link(const char *title, Anchor *dst); + + /** + * Element interface + */ + void format_fixed_width(int w); + void draw(Canvas *c, int x, int y); + Element *find(int x, int y); + + /** + * Tick interface + */ + int on_tick(); +}; + +#endif /* _ELEMENTS_H_ */ diff --git a/demo/src/app/scout/include/event.h b/demo/src/app/scout/include/event.h new file mode 100644 index 000000000..81603b5e8 --- /dev/null +++ b/demo/src/app/scout/include/event.h @@ -0,0 +1,82 @@ +/* + * \brief User event representation + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _EVENT_H_ +#define _EVENT_H_ + +/** + * Event structure + * + * This event structure covers timer events as + * well as user input events. + */ +class Event +{ + public: + + /** + * Some sensibly choosen key and button codes + */ + enum code { + BTN_LEFT = 0x110, + KEY_Q = 16, + }; + + enum ev_type { + UNDEFINED = 0, + MOTION = 1, /* mouse moved */ + PRESS = 2, /* button/key pressed */ + RELEASE = 3, /* button/key released */ + TIMER = 4, /* timer event */ + QUIT = 5, /* quit application */ + REFRESH = 6, /* refresh screen */ + WHEEL = 7, /* mouse wheel */ + }; + + ev_type type; + int mx, my; /* mouse position */ + int wx, wy; /* wheel */ + int code; /* key code */ + + /** + * Assign new event information to event structure + */ + inline void assign(ev_type new_type, int new_mx, int new_my, int new_code) + { + type = new_type; + mx = new_mx; + my = new_my; + wx = 0; + wy = 0; + code = new_code; + } +}; + + +/** + * Event handler + */ +class Event_handler +{ + public: + + virtual ~Event_handler() { } + + /** + * Handle event + */ + virtual void handle(Event &e) = 0; +}; + + +#endif /* _EVENT_H_ */ diff --git a/demo/src/app/scout/include/fade_icon.h b/demo/src/app/scout/include/fade_icon.h new file mode 100644 index 000000000..c2dfce138 --- /dev/null +++ b/demo/src/app/scout/include/fade_icon.h @@ -0,0 +1,95 @@ +/* + * \brief Implementation of fading icon + * \date 2005-10-24 + * \author Norman Feske + * + * Fading icons are presented at a alpha value of 50 percent. + * When getting the mouse focus, we smoothly increase the + * alpha value to 100 percent. + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _FADE_ICON_H_ +#define _FADE_ICON_H_ + +#include "miscmath.h" +#include "widgets.h" +#include "fader.h" + + +template +class Fade_icon : public Fader, public Icon +{ + private: + + int _default_alpha; + int _focus_alpha; + + public: + + /** + * Constructor + */ + Fade_icon() + { + _curr_value = _dst_value = _default_alpha = 100; + _focus_alpha = 255; + step(12); + } + + /** + * Accessor functions + */ + int default_alpha() { return _default_alpha; } + + /** + * Define alpha value for unfocused icon + */ + void default_alpha(int alpha ) { _default_alpha = alpha; } + + /** + * Define alpha value when having the mouse focus + */ + void focus_alpha(int alpha) { _focus_alpha = alpha; } + + /** + * Tick interface + */ + int on_tick() + { + /* call on_tick function of the fader */ + if (::Fader::on_tick() == 0) return 0; + + Icon::alpha(_curr_value); + return 1; + } + + /** + * Icon interface + */ + void alpha(int alpha) + { + _curr_value = alpha; + ::Icon::alpha(alpha); + } + + /** + * Element interface + */ + void mfocus(int flag) + { + Icon::mfocus(flag); + int step = _focus_alpha - _default_alpha; + step *= flag ? 26 : 19; + fade_to(flag ? _focus_alpha : _default_alpha, step >> 8); + } +}; + + +#endif /* _FADE_ICON_H_ */ diff --git a/demo/src/app/scout/include/fader.h b/demo/src/app/scout/include/fader.h new file mode 100644 index 000000000..c39a62cc2 --- /dev/null +++ b/demo/src/app/scout/include/fader.h @@ -0,0 +1,78 @@ +/* + * \brief Fading class + * \date 2005-11-10 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _FADER_H_ +#define _FADER_H_ + +#include "miscmath.h" +#include "tick.h" + +/** + * Class that manages the fading of a derived class. + */ +class Fader : public Tick +{ + protected: + + int _curr_value; /* current value */ + int _dst_value; /* desired final value */ + int _step; + + public: + + virtual ~Fader() { } + + /** + * Fade to specified alpha value + */ + void fade_to(int dst_value, int step = -1) + { + if (step > 0) _step = step; + _dst_value = dst_value; + schedule(20); + } + + /** + * Set fading speed + */ + void step(int step) { _step = step; } + + /** + * Assign new current fading value + */ + void curr(int curr) + { + if (curr == _curr_value) return; + _curr_value = curr; + schedule(20); + } + + /** + * Tick interface + */ + int on_tick() + { + if (_curr_value == _dst_value) + return 0; + + if (_curr_value < _dst_value) + _curr_value = min(_curr_value + _step, _dst_value); + if (_curr_value > _dst_value) + _curr_value = max(_curr_value - _step, _dst_value); + + return 1; + } +}; + + +#endif /* _FADER_H_ */ diff --git a/demo/src/app/scout/include/font.h b/demo/src/app/scout/include/font.h new file mode 100644 index 000000000..0b7258f02 --- /dev/null +++ b/demo/src/app/scout/include/font.h @@ -0,0 +1,62 @@ +/* + * \brief Font representation + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _FONT_H_ +#define _FONT_H_ + +#include "scout_types.h" + +class Font +{ + private: + + typedef scout_int32_t int32_t; + + public: + + unsigned char *img; /* font image */ + int img_w, img_h; /* size of font image */ + int32_t *wtab; /* width table */ + int32_t *otab; /* offset table */ + + /** + * Construct font from a TFF data block + */ + explicit Font(const char *tff) + { + otab = (int32_t *)(tff); + wtab = (int32_t *)(tff + 1024); + img_w = *((int32_t *)(tff + 2048)); + img_h = *((int32_t *)(tff + 2052)); + img = (unsigned char *)(tff + 2056); + } + + /** + * Calculate width of string when printed with the font + */ + int str_w(const char *sstr, int len) + { + const unsigned char *str = (const unsigned char *)sstr; + int res = 0; + for (; str && *str && len; len--, str++) res += wtab[*str]; + return res; + } + + /** + * Calculate height of string when printed with the font + */ + int str_h(const char *str, int len) { return img_h; } +}; + + +#endif /* _FONT_H_ */ diff --git a/demo/src/app/scout/include/genode/alloc.h b/demo/src/app/scout/include/genode/alloc.h new file mode 100644 index 000000000..66d5b9abd --- /dev/null +++ b/demo/src/app/scout/include/genode/alloc.h @@ -0,0 +1,30 @@ +/* + * \brief Malloc/free wrappers for Genode + * \date 2008-07-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _GENODE_ALLOC_H_ +#define _GENODE_ALLOC_H_ + +#include + +static inline void *scout_malloc(unsigned long size) { + return Genode::env()->heap()->alloc(size); } + +static inline void scout_free(void *addr) { + + /* + * FIXME: We expect the heap to know the size of the + * block and thus, just specify zero as size. + */ + Genode::env()->heap()->free(addr, 0); } + +#endif diff --git a/demo/src/app/scout/include/genode/launcher_config.h b/demo/src/app/scout/include/genode/launcher_config.h new file mode 100644 index 000000000..2f23c9b34 --- /dev/null +++ b/demo/src/app/scout/include/genode/launcher_config.h @@ -0,0 +1,38 @@ +/* + * \brief Genode-specific Launcher support + * \author Norman Feske + * \date 2009-08-25 + * + * The generic Scout code uses the 'Launcher_config' as an opaque type. + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SCOUT__GENODE__LAUNCHER_CONFIG__ +#define _SCOUT__GENODE__LAUNCHER_CONFIG__ + +#include + +class Launcher_config +{ + private: + + Genode::Dataspace_capability _config_ds; + + public: + + /** + * Constructor + */ + Launcher_config(Genode::Dataspace_capability config_ds) + : _config_ds(config_ds) { } + + Genode::Dataspace_capability config_ds() { return _config_ds; } +}; + +#endif /* _SCOUT__GENODE__LAUNCHER_CONFIG__ */ diff --git a/demo/src/app/scout/include/genode/printf.h b/demo/src/app/scout/include/genode/printf.h new file mode 100644 index 000000000..c5fc980aa --- /dev/null +++ b/demo/src/app/scout/include/genode/printf.h @@ -0,0 +1,30 @@ +/* + * \brief Printf wrappers for Genode + * \date 2008-07-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _GENODE_PRINTF_H_ +#define _GENODE_PRINTF_H_ + +#include + +inline int printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + Genode::vprintf(format, list); + + va_end(list); + return 0; +} + +#endif diff --git a/demo/src/app/scout/include/genode/string.h b/demo/src/app/scout/include/genode/string.h new file mode 100644 index 000000000..624961fd7 --- /dev/null +++ b/demo/src/app/scout/include/genode/string.h @@ -0,0 +1,28 @@ +/* + * \brief String function wrappers for Genode + * \date 2008-07-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _GENODE_STRING_H_ +#define _GENODE_STRING_H_ + +#include + +inline Genode::size_t strlen(const char *s) +{ return Genode::strlen(s); } + +inline void *memset(void *s, int c, Genode::size_t n) +{ return Genode::memset(s, c, n); } + +inline void *memcpy(void *dest, const void *src, Genode::size_t n) { +return Genode::memcpy(dest, src, n); } + +#endif diff --git a/demo/src/app/scout/include/history.h b/demo/src/app/scout/include/history.h new file mode 100644 index 000000000..b2b44136f --- /dev/null +++ b/demo/src/app/scout/include/history.h @@ -0,0 +1,112 @@ +/* + * \brief Browser history buffer + * \date 2005-11-03 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _HISTORY_H_ +#define _HISTORY_H_ + +#include "elements.h" + +class History +{ + private: + + static const int _size = 128; /* history size */ + + int _idx; /* current position in history */ + Anchor *_history[_size]; /* ring buffer of history references */ + + /** + * Increment history position + * + * \offset The incrementation offset must be higher than -_size + */ + inline void _inc(int offset) { _idx = (_idx + _size + offset) % _size; } + + /** + * Return next index of current position + */ + inline int _next() { return (_idx + 1) % _size; } + + /** + * Return previous index of current position + */ + inline int _prev() { return (_idx + _size - 1) % _size; } + + public: + + /** + * Constructor + */ + History() + { + _idx = 0; + memset(_history, 0, sizeof(_history)); + } + + /** + * Request element at current history position + */ + Anchor *curr() { return _history[_idx]; } + + /** + * Add element to history. + * + * We increment the current history position and insert the + * new element there. If the new element is identical with + * with the old element at that position, we keep the following + * history elements. Otherwise, we follow a new link 'branch' + * and cut off the old one. + */ + void add(Anchor *e) + { + /* discard invalid history elements */ + if (!e) return; + + /* increment history position */ + _inc(1); + + /* do we just follow the forward path? */ + if (curr() == e) return; + + /* cut off old forward history branch */ + _history[_next()] = 0; + + /* insert new element */ + _history[_idx] = e; + } + + /** + * Assign new element to current history entry + */ + void assign(Anchor *e) { if (e) _history[_idx] = e; } + + /** + * Travel forward or backward in history + * + * \return 1 on success, + * 0 if end of history is reached + */ + enum direction { FORWARD, BACKWARD }; + int go(direction dir) + { + /* stop at the boundaries of the history */ + if (!_history[dir == FORWARD ? _next() : _prev()]) return 0; + + /* travel in history */ + _inc(dir == FORWARD ? 1 : -1); + return 1; + } +}; + + +#endif diff --git a/demo/src/app/scout/include/miscmath.h b/demo/src/app/scout/include/miscmath.h new file mode 100644 index 000000000..6e61febe6 --- /dev/null +++ b/demo/src/app/scout/include/miscmath.h @@ -0,0 +1,35 @@ +/* + * \brief Misc math functions used here and there + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _MISCMATH_H_ +#define _MISCMATH_H_ + +/** + * Calc min/max of two numbers + */ +template static inline T min(T a, T b) { return a < b ? a : b; } +template static inline T max(T a, T b) { return a > b ? a : b; } + + +/** + * Produce pseudo random values + */ +static inline int random(void) +{ + static unsigned int seed = 93186752; + const unsigned int a = 1588635695, q = 2, r = 1117695901; + seed = a*(seed % q) - r*(seed / q); + return seed; +} + +#endif /* _MISCMATH_H_ */ diff --git a/demo/src/app/scout/include/platform.h b/demo/src/app/scout/include/platform.h new file mode 100644 index 000000000..6512b08ae --- /dev/null +++ b/demo/src/app/scout/include/platform.h @@ -0,0 +1,161 @@ +/* + * \brief Platform abstraction + * \date 2005-10-24 + * \author Norman Feske + * + * This interface specifies the target-platform-specific functions. + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM_H_ +#define _PLATFORM_H_ + +#include "event.h" + +/* + * We use two buffers, a foreground buffer that is displayed on screen and a + * back buffer. While the foreground buffer must contain valid data all the + * time, the back buffer can be used to prepare pixel data. For example, + * drawing multiple pixel layers with alpha channel must be done in the back + * buffer to avoid artifacts on the screen. + */ +class Screen_update +{ + public: + + virtual ~Screen_update() { } + + /** + * Request screen base address + */ + virtual void *scr_adr() = 0; + + /** + * Request back buffer address + */ + virtual void *buf_adr() { return scr_adr(); } + + /** + * Flip fore and back buffers + */ + virtual void flip_buf_scr() { } + + /** + * Copy background buffer to foreground + */ + virtual void copy_buf_to_scr(int x, int y, int w, int h) { } + + /** + * Flush pixels of specified screen area + */ + virtual void scr_update(int x, int y, int w, int h) = 0; +}; + + +class Platform : public Screen_update +{ + private: + + int _max_vw, _max_vh; /* maximum view size */ + + public: + + enum pixel_format { + UNDEFINED = 0, + RGB565 = 1, + }; + + /** + * Constructor - initialize platform + * + * \param vx,vy initial view position + * \param vw,vw initial view width and height + * \param max_vw maximum view width + * + * When using the default value for 'max_vw', the window's + * max width will correspond to the screen size. + */ + Platform(unsigned vx, unsigned vy, unsigned vw, unsigned vh, + unsigned max_vw = 0, unsigned max_vh = 0); + + /** + * Check initialization state of the platform + * + * \retval 1 platform was successfully initialized + * \retval 0 platform initialization failed + */ + int initialized(); + + /** + * Request screen width and height + */ + int scr_w(); + int scr_h(); + + /** + * Request pixel format + */ + pixel_format scr_pixel_format(); + + /** + * Define geometry of viewport on screen + * + * The specified area is relative to the screen + * of the platform. + */ + void view_geometry(int x, int y, int w, int h, int do_redraw = 0); + + /** + * Bring Scouts view ontop + */ + void top_view(); + + /** + * View geometry accessor functions + */ + int vx(); + int vy(); + int vw(); + int vh(); + + /** + * Get timer ticks in miilliseconds + */ + unsigned long timer_ticks(); + + /** + * Request if an event is pending + * + * \retval 1 event is pending + * \retval 0 no event pending + */ + int event_pending(); + + /** + * Request event + * + * \param e destination where to store event information. + * + * If there is no event pending, this function blocks + * until there is an event to deliver. + */ + void get_event(Event *out_e); + + /** + * Screen update interface + */ + void *scr_adr(); + void *buf_adr(); + void flip_buf_scr(); + void copy_buf_to_scr(int x, int y, int w, int h); + void scr_update(int x, int y, int w, int h); + +}; + +#endif /* _PLATFORM_H_ */ diff --git a/demo/src/app/scout/include/redraw_manager.h b/demo/src/app/scout/include/redraw_manager.h new file mode 100644 index 000000000..f088242ff --- /dev/null +++ b/demo/src/app/scout/include/redraw_manager.h @@ -0,0 +1,150 @@ +/* + * \brief Simplistic redraw manager featuring redraw merging + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _REDRAW_MANAGER_H_ +#define _REDRAW_MANAGER_H_ + +#include "elements.h" + +class Redraw_manager +{ + private: + + int _x1, _y1; /* upper left pixel of dirty area */ + int _x2, _y2; /* lower right pixel of dirty area */ + int _cnt; /* nb of requests since last process */ + Element *_root; /* root element for drawing */ + Canvas *_canvas; /* graphics backend */ + Screen_update *_scr_update; /* flushing pixels in backend */ + int _w, _h; /* current size of output window */ + bool _scout_quirk; /* enable redraw quirk for scout */ + + public: + + /** + * Constructor + */ + Redraw_manager(Canvas *canvas, Screen_update *scr_update, int w, int h, + bool scout_quirk = false) + : + _cnt(0), _root(0), _canvas(canvas), _scr_update(scr_update), _w(w), _h(h), + _scout_quirk(scout_quirk) + { } + + /** + * Accessor functions + */ + inline Canvas *canvas() { return _canvas; } + + /** + * Define root element for issueing drawing operations + */ + inline void root(Element *root) { _root = root; } + + /** + * Collect redraw requests + */ + void request(int x, int y, int w, int h) + { + /* + * Scout redraw quirk + * + * Quick fix to avoid artifacts at the icon bar. + * The icon bar must always be drawn completely + * because of the interaction of the different + * layers. + */ + if (_scout_quirk && y < 64 + 32) { + h = max(h + y, 64 + 32); + w = _w; + x = 0; + y = 0; + } + + /* first request since last process operation */ + if (_cnt == 0) { + _x1 = x; _x2 = x + w - 1; + _y1 = y; _y2 = y + h - 1; + + /* merge subsequencing requests */ + } else { + _x1 = min(_x1, x); _x2 = max(_x2, x + w - 1); + _y1 = min(_y1, y); _y2 = max(_y2, y + h - 1); + } + _cnt++; + } + + /** + * Define size of visible redraw window + */ + void size(int w, int h) + { + if (w > _canvas->w()) + w = _canvas->w(); + + if (h > _canvas->h()) + h = _canvas->w(); + + _w = w; _h = h; + } + + /** + * Process redrawing operations + */ + void process() + { + if (_cnt == 0 || !_canvas || !_root) return; + + /* get actual drawing area (clipped against canvas dimensions) */ + int x1 = max(0, _x1); + int y1 = max(0, _y1); + int x2 = min(_w - 1, _x2); + int y2 = min(_h - 1, _y2); + + if (x1 > x2 || y1 > y2) return; + + _canvas->clip(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + + /* draw browser window into back buffer */ + _root->try_draw(_canvas, 0, 0); + + /* + * If we draw the whole area, we can flip the front + * and back buffers instead of copying pixels from the + * back to the front buffer. + */ + + /* detemine if the whole area must be drawn */ + if (x1 == 0 && x2 == _root->w() - 1 + && y1 == 0 && y2 == _root->h() - 1) { + + /* flip back end front buffers */ + _scr_update->flip_buf_scr(); + + /* apply future drawing operations on new back buffer */ + _canvas->addr(_scr_update->buf_adr()); + + } else + _scr_update->copy_buf_to_scr(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + + /* give notification about changed canvas area */ + if (_scr_update) + _scr_update->scr_update(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + + /* reset request state */ + _cnt = 0; + } +}; + + +#endif /* _REDRAW_MANAGER_H_ */ diff --git a/demo/src/app/scout/include/refracted_icon.h b/demo/src/app/scout/include/refracted_icon.h new file mode 100644 index 000000000..6dc4507dc --- /dev/null +++ b/demo/src/app/scout/include/refracted_icon.h @@ -0,0 +1,76 @@ +/* + * \brief Interface of refracted icon + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _REFRACTED_ICON_H_ +#define _REFRACTED_ICON_H_ + +#include "widgets.h" + +/** + * \param PT pixel type (must be Pixel_rgba compatible) + * \param DT distortion map entry type + */ +template +class Refracted_icon : public Element +{ + private: + + PT *_backbuf; /* pixel back buffer */ + int _filter_backbuf; /* backbuf filtering flag */ + DT *_distmap; /* distortion table */ + int _distmap_w, _distmap_h; /* size of distmap */ + PT *_fg; /* foreground pixels */ + unsigned char *_fg_alpha; /* foreground alpha values */ + + public: + + /** + * Define pixel back buffer for the icon. This buffer is used for the + * draw operation. It should have the same number of pixels as the + * distortion map. + */ + void backbuf(PT *backbuf, int filter_backbuf = 0) + { + _backbuf = backbuf; + _filter_backbuf = filter_backbuf; + } + + /** + * Scratch refraction map + */ + void scratch(int jitter); + + /** + * Define distortion map for the icon + */ + void distmap(DT *distmap, int distmap_w, int distmap_h) + { + _distmap = distmap; + _distmap_w = distmap_w; + _distmap_h = distmap_h; + } + + /** + * Define foreground pixels + */ + void foreground(PT *fg, unsigned char *fg_alpha) + { + _fg = fg; + _fg_alpha = fg_alpha; + } + + void draw(Canvas *c, int px, int py); +}; + + +#endif /* _REFRACTED_H_ */ diff --git a/demo/src/app/scout/include/scout_types.h b/demo/src/app/scout/include/scout_types.h new file mode 100644 index 000000000..98934774e --- /dev/null +++ b/demo/src/app/scout/include/scout_types.h @@ -0,0 +1,21 @@ +/* + * \brief Platform-dependent definition of fixed-size integer types + * \author Norman Feske + * \date 2009-04-15 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SCOUT_TYPES_H_ +#define _SCOUT_TYPES_H_ + +#include + +typedef Genode::int32_t scout_int32_t; + +#endif /* _SCOUT_TYPES_H_ */ diff --git a/demo/src/app/scout/include/scrollbar.h b/demo/src/app/scout/include/scrollbar.h new file mode 100644 index 000000000..4f7bb4448 --- /dev/null +++ b/demo/src/app/scout/include/scrollbar.h @@ -0,0 +1,106 @@ +/* + * \brief Scrollbar interface + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SCROLLBAR_H_ +#define _SCROLLBAR_H_ + +#include "widgets.h" +#include "fade_icon.h" + + +class Scrollbar_listener +{ + public: + + virtual ~Scrollbar_listener() { } + + /** + * Handle change of view position + */ + virtual void handle_scroll(int view_pos) = 0; +}; + + +template +class Scrollbar : public Parent_element +{ + public: + + static const int sb_elem_w = 32; /* scrollbar element width */ + static const int sb_elem_h = 32; /* scrollbar element height */ + + private: + + Fade_icon _uparrow; + Fade_icon _dnarrow; + Fade_icon _slider; + int _real_size; /* size of content */ + int _view_size; /* size of viewport */ + int _view_pos; /* viewport position */ + Scrollbar_listener *_listener; /* listener for scroll events */ + int _visibility; + + /** + * Utilities + */ + inline int _visible() { return _real_size > _view_size; } + + public: + + /** + * Constructor + */ + Scrollbar(); + + /** + * Accessor functions + */ + int real_size () { return _real_size; } + int view_size () { return _view_size; } + int view_pos () { return _view_pos; } + int slider_pos (); + int slider_size (); + + /** + * Set slider to specified position + */ + void slider_pos(int); + + /** + * Define scrollbar properties + */ + void view(int real_size, int view_size, int view_pos); + + /** + * Define listener to scroll events + */ + void listener(Scrollbar_listener *listener) { _listener = listener; } + + /** + * Notify listener about view port change + */ + void notify_listener(); + + /** + * Set geometry of scrollbar and layout scrollbar elements + */ + void geometry(int x, int y, int w, int h); + + /** + * Element interface + */ + Element *find(int x, int y); +}; + + +#endif /* _SCROLLBAR_H_ */ diff --git a/demo/src/app/scout/include/sky_texture.h b/demo/src/app/scout/include/sky_texture.h new file mode 100644 index 000000000..70d98997b --- /dev/null +++ b/demo/src/app/scout/include/sky_texture.h @@ -0,0 +1,43 @@ +/* + * \brief Sky texture interface + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SKY_TEXTURE_H_ +#define _SKY_TEXTURE_H_ + +#include "widgets.h" + + +/** + * \param PT pixel type (compatible to Pixel_rgba) + * \param TW tile width + * \param TH tile height + */ +template +class Sky_texture : public Texture +{ + private: + + short _bufs[3][TH][TW]; + short _buf[TH][TW]; + short _tmp[TH][TW]; + PT _coltab[16*16*16]; + PT _fallback[TH][TW]; /* fallback texture */ + + public: + + Sky_texture(); + + void draw(Canvas *c, int px, int py); +}; + +#endif /* _SKY_TEXTURE_H_ */ diff --git a/demo/src/app/scout/include/styles.h b/demo/src/app/scout/include/styles.h new file mode 100644 index 000000000..3ba5db9ab --- /dev/null +++ b/demo/src/app/scout/include/styles.h @@ -0,0 +1,53 @@ +/* + * \brief Document styles + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _STYLES_H_ +#define _STYLES_H_ + +extern char _binary_mono16_tff_start[]; +extern char _binary_verabi10_tff_start[]; +extern char _binary_vera16_tff_start[]; +extern char _binary_verai16_tff_start[]; +extern char _binary_vera18_tff_start[]; +//extern char _binary_cour14_tff_start[]; +//extern char _binary_newy14_tff_start[]; +//extern char _binary_helv14_tff_start[]; +extern char _binary_vera20_tff_start[]; +extern char _binary_vera24_tff_start[]; + +//Font default_font (&_binary_helv14_tff_start[0]); +static Font label_font (&_binary_verabi10_tff_start[0]); +static Font default_font (&_binary_vera16_tff_start[0]); +static Font italic_font (&_binary_verai16_tff_start[0]); +static Font mono_font (&_binary_mono16_tff_start[0]); +static Font chapter_font (&_binary_vera24_tff_start[0]); +static Font section_font (&_binary_vera20_tff_start[0]); +static Font subsection_font (&_binary_vera18_tff_start[0]); + +static Color default_color (0, 0, 0); +static Color text_color (20, 20, 20); +static Color verbatim_bgcol (0, 0, 0, 26); + +static Style plain_style (&default_font, text_color, 0); +static Style bold_style (&default_font, text_color, Style::ATTR_BOLD); +static Style mono_style (&mono_font, text_color, 0); +static Style italic_style (&italic_font, text_color, 0); + +static Style link_style (&default_font, Color(0, 0, 255), 0); + +static Style chapter_style (&chapter_font, default_color, 0); +static Style section_style (§ion_font, default_color, 0); +static Style subsection_style (&subsection_font, default_color, 0); +static Style navbar_style (&default_font, Color(0, 0, 0, 127), 0); + +#endif /* _STYLES_H_ */ diff --git a/demo/src/app/scout/include/tick.h b/demo/src/app/scout/include/tick.h new file mode 100644 index 000000000..5a46b4b2d --- /dev/null +++ b/demo/src/app/scout/include/tick.h @@ -0,0 +1,87 @@ +/* + * \brief Timed event scheduler interface + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TICK_H_ +#define _TICK_H_ + +class Tick; +class Tick +{ + public: + + typedef unsigned long time; + + private: + + /** + * Tick object attributes + */ + time _deadline; /* next deadline */ + time _period; /* duration between ticks */ + Tick *_next; /* next tick in tick list */ + int _active; /* set to one when active */ + + /** + * Enqueue tick into tick queue + */ + void _enqueue(); + + /** + * Dequeue tick from tick queue (for destruction of Tick) + */ + void _dequeue(); + + protected: + + /** + * Function to be called on when deadline is reached + * + * This function must be implemented by a derived class. + * If the return value is 1, the tick is scheduled again. + */ + virtual int on_tick() = 0; + + public: + + Tick() + { + _deadline = 0; + _period = 0; + _active = 0; + _next = 0; + } + + virtual ~Tick() { _dequeue(); } + + /** + * Schedule tick + * + * \param tick period + */ + void schedule(time period); + + /** + * Return the number of scheduled ticks + */ + static int ticks_scheduled(); + + /** + * Handle ticks + * + * \param now current time + */ + static void handle(time now); +}; + + +#endif /* _TICK_H_ */ diff --git a/demo/src/app/scout/include/titlebar.h b/demo/src/app/scout/include/titlebar.h new file mode 100644 index 000000000..11afbd4db --- /dev/null +++ b/demo/src/app/scout/include/titlebar.h @@ -0,0 +1,91 @@ +/* + * \brief Titlebar interface + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TITLEBAR_H_ +#define _TITLEBAR_H_ + +#include "widgets.h" + +#define TITLE_TFF _binary_vera18_tff_start +extern char TITLE_TFF[]; + + +/*************** + ** Title bar ** + ***************/ + +static Font title_font(TITLE_TFF); + +template +class Titlebar : public Parent_element +{ + private: + + Icon _fg; + const char *_txt; + int _txt_w, _txt_h, _txt_len; + + public: + + /** + * Define text displayed within titlebar + */ + void text(const char *txt) + { + _txt = txt ? txt : "Scout"; + _txt_w = title_font.str_w(_txt, strlen(_txt)); + _txt_h = title_font.str_h(_txt, strlen(_txt)); + _txt_len = strlen(_txt); + } + + /** + * Constructor + */ + Titlebar() + { + _fg.alpha(255); + _fg.findable(0); + text(0); + + append(&_fg); + } + + /** + * Define foreground of titlebar + */ + void rgba(unsigned char *rgba) { _fg.rgba(rgba, 0, 0); }; + + /** + * Element interface + */ + + void format_fixed_width(int w) + { + _min_w = w; + _min_h = 32; + _fg.geometry(0, 0, _min_w, _min_h); + } + + void draw(Canvas *c, int x, int y) + { + const int b = 180, a = 200; + c->draw_box(x + _x, y + _y, _w, _h, Color(b, b, b, a)); + + int _txt_x = x + _x + max((_w - _txt_w)/2, 8); + int _txt_y = y + _y + max((_h - _txt_h)/2, 0) - 1; + c->draw_string(_txt_x , _txt_y, &title_font, Color(0,0,0,200), _txt, strlen(_txt)); + ::Parent_element::draw(c, x, y); + } +}; + +#endif diff --git a/demo/src/app/scout/include/user_state.h b/demo/src/app/scout/include/user_state.h new file mode 100644 index 000000000..6ef3097bb --- /dev/null +++ b/demo/src/app/scout/include/user_state.h @@ -0,0 +1,164 @@ +/* + * \brief User state manager + * \date 2005-11-16 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _USER_STATE_H_ +#define _USER_STATE_H_ + +#include "window.h" +#include "elements.h" + + +class User_state : public Parent_element +{ + private: + + Window *_window; + Element *_root; /* root of element tree */ + Element *_mfocus; /* element that owns the current mouse focus */ + Element *_dst; /* current link destination */ + Element *_active; /* currently activated element */ + int _key_cnt; /* number of currently pressed keys */ + int _mx, _my; /* current mouse position */ + int _vx, _vy; /* current view offset */ + + /** + * Assign new mouse focus element + */ + void _assign_mfocus(Element *e, int force = 0) + { + /* return if mouse focus did not change */ + if (!force && e == _mfocus) return; + + /* tell old mouse focus to release focus */ + if (_mfocus) _mfocus->mfocus(0); + + /* assign new current mouse focus */ + _mfocus = e; + + /* notify new mouse focus */ + if (_mfocus) _mfocus->mfocus(1); + + /* determine new current link destination */ + Element *old_dst = _dst; + if (_mfocus && _mfocus->is_link()) { + Link_token *l = static_cast(_mfocus); + _dst = l->dst(); + } else + _dst = 0; + + /* nofify element tree about new link destination */ + if (_dst != old_dst) + _root->curr_link_destination(_dst); + } + + public: + + /** + * Constructor + */ + User_state(Window *window, Element *root, int vx, int vy) + { + _mfocus = _dst = _active = 0; + _window = window; + _root = root; + _key_cnt = 0; + _vx = vx; + _vy = vy; + } + + /** + * Accessor functions + */ + int mx() { return _mx; } + int my() { return _my; } + int vx() { return _vx; } + int vy() { return _vy; } + + /** + * Apply input event to mouse focus state + */ + void handle_event(Event &ev) + { + _key_cnt += ev.type == Event::PRESS ? 1 : 0; + _key_cnt -= ev.type == Event::RELEASE ? 1 : 0; + + if (_key_cnt < 0) _key_cnt = 0; + + if (_active) + _active->handle_event(ev); + + /* find element under the mouse cursor */ + _mx = ev.mx; + _my = ev.my; + Element *e = _root->find(_mx, _my); + + switch (ev.type) { + + case Event::PRESS: + + if (_key_cnt != 1) break; + if (!e) break; + + _active = e; + _active->handle_event(ev); + + _vx = _window->view_x(); + _vy = _window->view_y(); + + _assign_mfocus(_root->find(ev.mx, ev.my), 1); + + break; + + case Event::RELEASE: + + if (_key_cnt == 0) { + _vx = _window->view_x(); + _vy = _window->view_y(); + _active = 0; + _assign_mfocus(e); + } + break; + + case Event::MOTION: + + if (!_active && e) e->handle_event(ev); + if (_key_cnt == 0) + _assign_mfocus(e); + break; + + case Event::WHEEL: + + if (_key_cnt == 0) + _window->ypos(_window->ypos() + 23 * ev.my); + break; + + default: + + break; + } + } + + + /******************** + ** Parent element ** + ********************/ + + void forget(Element *e) + { + if (_mfocus == e) _mfocus = 0; + if (_dst == e) _dst = 0; + if (_active == e) _active = 0; + } +}; + +#endif /* _USER_STATE_H_ */ diff --git a/demo/src/app/scout/include/widgets.h b/demo/src/app/scout/include/widgets.h new file mode 100644 index 000000000..0b183576e --- /dev/null +++ b/demo/src/app/scout/include/widgets.h @@ -0,0 +1,165 @@ +/* + * \brief Basic user interface elements + * \date 2005-10-24 + * \author Norman Feske + */ + +/* + * Copyright (C) 2005-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _WIDGETS_H_ +#define _WIDGETS_H_ + +#include "elements.h" + + +class Texture : public Element { }; + + +class Docview : public Parent_element +{ + private: + + Texture *_bg; + Element *_cont; + int _voffset; + int _right_pad; + int _padx; + + public: + + /** + * Constructor + */ + explicit Docview(int padx = 7): + _bg(0), _cont(0), _voffset(0), _right_pad(0), _padx(padx) { } + + /** + * Accessor functions + */ + Element *content() { return _cont; } + + /** + * Define content to be presented in the Docview + */ + inline void content(Element *cont) + { + _cont = cont; + _last = _first = 0; + append(cont); + } + + inline void voffset(int voffset) { _voffset = voffset; } + + /** + * Define background texture + */ + inline void texture(Texture *bg) { _bg = bg; } + + /** + * Define right padding + */ + inline void right_pad(int pad) { _right_pad = pad; } + + /** + * Element interface + */ + void format_fixed_width(int w); + void draw(Canvas *c, int x, int y); + Element *find(int x, int y); + void geometry(int x, int y, int w, int h); +}; + + +template +class Horizontal_shadow : public Element +{ + public: + + explicit Horizontal_shadow(int height = 8) { _min_h = height; } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y); + Element *find(int x, int y) { return 0; } + void format_fixed_width(int w) { _min_w = w; } +}; + + +class Generic_icon : public Element +{ + public: + + /** + * Request current alpha value + */ + virtual int alpha() = 0; + + /** + * Define alpha value of the icon + */ + virtual void alpha(int alpha) = 0; +}; + + +template +class Icon : public Generic_icon +{ + private: + + PT _pixel [H][W]; /* icon pixels in PT pixel format */ + unsigned char _alpha [H][W]; /* alpha channel of icon pixels */ + unsigned char _shadow [H][W]; /* shadow calculation buffer */ + int _icon_alpha; /* alpha value of whole icon */ + + public: + + /** + * Constructor + */ + Icon(); + + /** + * Define new icon pixels from rgba buffer + * + * \param vshift vertical shift of pixels + * \param shadow shadow divisor, low value -> dark shadow + * special case zero -> no shadow + * + * The buffer must contains W*H pixels. Each pixels consists + * of four bytes, red, green, blue, and alpha. + */ + void rgba(unsigned char *src, int vshift = 0, int shadow = 4); + + /** + * Define icon to be a glow of an rgba image + * + * \param src source rgba image to extract the glow's shape from + * \param c glow color + */ + void glow(unsigned char *src, Color c); + + /** + * Generic_icon interface + */ + int alpha() { return _icon_alpha; } + virtual void alpha(int alpha) + { + _icon_alpha = alpha; + refresh(); + } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y); + Element *find(int x, int y); +}; + + +#endif /* _WIDGETS_H_ */ diff --git a/demo/src/app/scout/include/window.h b/demo/src/app/scout/include/window.h new file mode 100644 index 000000000..4fa815ca1 --- /dev/null +++ b/demo/src/app/scout/include/window.h @@ -0,0 +1,222 @@ +/* + * \brief Window interface + * \author Norman Feske + * \date 2006-08-30 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _WINDOW_H_ +#define _WINDOW_H_ + +#include "elements.h" +#include "platform.h" +#include "redraw_manager.h" + + +/********************** + ** Window interface ** + **********************/ + +class Window : public Parent_element +{ + private: + + Platform *_pf; + int _max_w; /* max width of window */ + int _max_h; /* max height of window */ + Redraw_manager *_redraw; /* redraw manager */ + + public: + + Window(Platform *pf, Redraw_manager *redraw, int max_w, int max_h) + : + _pf(pf), _max_w(max_w), _max_h(max_h), _redraw(redraw) + { + /* init element attributes */ + _x = _y = 0; + _w = pf->vw(); + _h = pf->vh(); + } + + virtual ~Window() { } + + /** + * Return current window position + */ + virtual int view_x() { return _pf->vx(); } + virtual int view_y() { return _pf->vy(); } + virtual int view_w() { return _pf->vw(); } + virtual int view_h() { return _pf->vh(); } + + /** + * Accessors + */ + Platform *pf() { return _pf; } + int max_w() { return _max_w; } + int max_h() { return _max_h; } + Redraw_manager *redraw() { return _redraw; } + + /** + * Bring window to front + */ + virtual void top() { _pf->top_view(); } + + /** + * Move window to new position + */ + virtual void vpos(int x, int y) { + _pf->view_geometry(x, y, _pf->vw(), _pf->vh(), 1); } + + /** + * Define vertical scroll offset + */ + virtual void ypos(int ypos) { } + virtual int ypos() { return 0; } + + /** + * Format window + */ + virtual void format(int w, int h) { } + + /** + * Element interface + * + * This function just collects the specified regions to be + * redrawn but does not perform any immediate drawing + * operation. The actual drawing must be initiated by + * calling the process_redraw function. + */ + void redraw_area(int x, int y, int w, int h) { + _redraw->request(x, y, w, h); } +}; + + +/******************** + ** Event handlers ** + ********************/ + +class Drag_event_handler : public Event_handler +{ + protected: + + int _key_cnt; /* number of curr. pressed keys */ + int _cmx, _cmy; /* original mouse position */ + int _omx, _omy; /* current mouse positon */ + + virtual void start_drag() = 0; + virtual void do_drag() = 0; + + public: + + /** + * Constructor + */ + Drag_event_handler() { _key_cnt = 0; } + + /** + * Event handler interface + */ + void handle(Event &ev) + { + if (ev.type == Event::PRESS) _key_cnt++; + if (ev.type == Event::RELEASE) _key_cnt--; + + if (_key_cnt == 0) return; + + /* first click starts dragging */ + if ((ev.type == Event::PRESS) && (_key_cnt == 1)) { + _cmx = _omx = ev.mx; + _cmy = _omy = ev.my; + start_drag(); + } + + /* check if mouse was moved */ + if ((ev.mx == _cmx) && (ev.my == _cmy)) return; + + /* remember current mouse position */ + _cmx = ev.mx; + _cmy = ev.my; + + do_drag(); + } +}; + + +class Sizer_event_handler : public Drag_event_handler +{ + protected: + + Window *_window; + int _obw, _obh; /* original window size */ + + /** + * Event handler interface + */ + void start_drag() + { + _obw = _window->view_w(); + _obh = _window->view_h(); + } + + void do_drag() + { + /* calculate new window size */ + int nbw = _obw + _cmx - _omx; + int nbh = _obh + _cmy - _omy; + + _window->format(nbw, nbh); + } + + public: + + /** + * Constructor + */ + Sizer_event_handler(Window *window) + { + _window = window; + } +}; + + +class Mover_event_handler : public Drag_event_handler +{ + protected: + + Window *_window; + int _obx, _oby; /* original launchpad position */ + + void start_drag() + { + _obx = _window->view_x(); + _oby = _window->view_y(); + _window->top(); + } + + void do_drag() + { + int nbx = _obx + _cmx - _omx; + int nby = _oby + _cmy - _omy; + + _window->vpos(nbx, nby); + } + + public: + + /** + * Constructor + */ + Mover_event_handler(Window *window) + { + _window = window; + } +}; + + +#endif diff --git a/demo/src/lib/launchpad/launchpad.cc b/demo/src/lib/launchpad/launchpad.cc new file mode 100644 index 000000000..668ffe1dd --- /dev/null +++ b/demo/src/lib/launchpad/launchpad.cc @@ -0,0 +1,370 @@ +/* + * \brief Launchpad child management + * \author Norman Feske + * \date 2006-09-01 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + + +/*************** + ** Launchpad ** + ***************/ + +Launchpad::Launchpad(unsigned long initial_quota) +: + _initial_quota(initial_quota), + _sliced_heap(env()->ram_session(), env()->rm_session()) +{ + /* names of services provided by the parent */ + static const char *names[] = { + + /* core services */ + "CAP", "RAM", "RM", "PD", "CPU", "IO_MEM", "IO_PORT", + "IRQ", "ROM", "LOG", "SIGNAL", + + /* services expected to got started by init */ + "Nitpicker", "Init", "Timer", "PCI", + + 0 /* null-termination */ + }; + for (unsigned i = 0; names[i]; i++) + _parent_services.insert(new (env()->heap()) + Parent_service(names[i])); +} + + +/** + * Check if a program with the specified name already exists + */ +bool Launchpad::_child_name_exists(const char *name) +{ + Launchpad_child *c = _children.first(); + + for ( ; c; c = c->List::Element::next()) + if (strcmp(c->name(), name) == 0) + return true; + + return false; +} + + +/** + * Create a unique name based on the filename + * + * If a program with the filename as name already exists, we + * add a counting number as suffix. + */ +void Launchpad::_get_unique_child_name(const char *filename, char *dst, int dst_len) +{ + Lock::Guard lock_guard(_children_lock); + + char buf[64]; + char suffix[8]; + suffix[0] = 0; + + for (int cnt = 1; true; cnt++) { + + /* build program name composed of filename and numeric suffix */ + snprintf(buf, sizeof(buf), "%s%s", filename, suffix); + + /* if such a program name does not exist yet, we are happy */ + if (!_child_name_exists(buf)) { + strncpy(dst, buf, dst_len); + return; + } + + /* increase number of suffix */ + snprintf(suffix, sizeof(suffix), ".%d", cnt + 1); + } +} + + +Launchpad_child *Launchpad::start_child(const char *filename, + unsigned long ram_quota, + Genode::Dataspace_capability config_ds) +{ + printf("starting %s with quota %ld\n", filename, ram_quota); + + /* find unique name for new child */ + char unique_name[64]; + _get_unique_child_name(filename, unique_name, sizeof(unique_name)); + printf("using unique child name \"%s\"\n", unique_name); + + if (ram_quota > env()->ram_session()->avail()) { + PERR("Child's ram quota is higher than our available quota, using available quota"); + ram_quota = env()->ram_session()->avail() - 256*1000; + } + + size_t metadata_size = 4096*16 + sizeof(Launchpad_child); + + if (metadata_size > ram_quota) { + PERR("Too low ram_quota to hold child metadata"); + return 0; + } + + ram_quota -= metadata_size; + + /* lookup executable elf binary */ + Dataspace_capability file_cap; + Rom_session_capability rom_cap; + try { + /* + * When creating a ROM connection for a non-existing file, the + * constructor of 'Rom_connection' throws a 'Parent::Service_denied' + * exception. + */ + Rom_connection rom(filename, unique_name); + rom.on_destruction(Rom_connection::KEEP_OPEN); + rom_cap = rom.cap(); + file_cap = rom.dataspace(); + } catch (...) { + printf("Error: Could not access file \"%s\" from ROM service.\n", filename); + return 0; + } + + /* create ram session for child with some of our own quota */ + Ram_connection ram; + ram.on_destruction(Ram_connection::KEEP_OPEN); + ram.ref_account(env()->ram_session_cap()); + env()->ram_session()->transfer_quota(ram.cap(), ram_quota); + + /* create cpu session for child */ + Cpu_connection cpu(unique_name); + cpu.on_destruction(Cpu_connection::KEEP_OPEN); + + if (!ram.cap().valid() || !cpu.cap().valid()) { + if (ram.cap().valid()) { + PWRN("Failed to create CPU session"); + env()->parent()->close(ram.cap()); + } + if (cpu.cap().valid()) { + PWRN("Failed to create RAM session"); + env()->parent()->close(cpu.cap()); + } + env()->parent()->close(rom_cap); + PERR("Our quota is %zd", env()->ram_session()->quota()); + return 0; + } + + Rm_connection rm; + rm.on_destruction(Rm_connection::KEEP_OPEN); + if (!rm.cap().valid()) { + PWRN("Failed to create RM session"); + env()->parent()->close(ram.cap()); + env()->parent()->close(cpu.cap()); + env()->parent()->close(rom_cap); + return 0; + } + + Launchpad_child *c = new (&_sliced_heap) + Launchpad_child(unique_name, file_cap, ram.cap(), + cpu.cap(), rm.cap(), rom_cap, + &_cap_session, &_parent_services, &_child_services, + config_ds, this); + + Lock::Guard lock_guard(_children_lock); + _children.insert(c); + + add_child(unique_name, ram_quota, c, c->heap()); + return c; +} + + +/** + * Watchdog-guarded child destruction mechanism + * + * During the destruction of a child, all sessions of the child are getting + * closed. A server, however, may refuse to answer a close call. We detect + * this case using a watchdog mechanism, unblock the 'close' call, and + * proceed with the closing the other remaining sessions. + */ +class Child_destructor_thread : Thread<2*4096> +{ + private: + + Launchpad_child *_curr_child; /* currently destructed child */ + Allocator *_curr_alloc; /* child object'sallocator */ + Lock _submit_lock; /* only one submission at a time */ + Lock _activate_lock; /* submission protocol */ + bool _ready; /* set if submission is completed */ + int _watchdog_cnt; /* watchdog counter in milliseconds */ + + /** + * Thread entry function + */ + void entry() { + while (true) { + + /* wait for next submission */ + _activate_lock.lock(); + + /* + * Eventually long-taking operation that involves the + * closing of all session of the child. This procedure + * may need blocking cancellation to proceed in the + * case servers are unresponsive. + */ + try { + destroy(_curr_alloc, _curr_child); + } catch (Blocking_canceled) { + PERR("Suspicious cancellation\n"); + } + + _ready = true; + } + } + + public: + + /* + * Watchdog timer granularity in milliseconds. This value defined + * after how many milliseconds the watchdog is activated. + */ + enum { WATCHDOG_GRANULARITY_MS = 10 }; + + /** + * Constructor + */ + Child_destructor_thread() : + _curr_child(0), _curr_alloc(0), + _activate_lock(Lock::LOCKED), + _ready(true) + { + start(); + } + + /** + * Destruct child, coping with unresponsive servers + * + * \param alloc Child object's allocator + * \param child Child to destruct + * \param timeout_ms Maximum destruction time until the destructing + * thread gets waken up to give up the close call to + * an unreponsive server. + */ + void submit_for_destruction(Allocator *alloc, Launchpad_child *child, + Timer::Session *timer, int timeout_ms) + { + /* block until destructor thread is ready for new submission */ + Lock::Guard _lock_guard(_submit_lock); + + /* register submission values */ + _curr_child = child; + _curr_alloc = alloc; + _ready = false; + _watchdog_cnt = 0; + + /* wake up the destruction thread */ + _activate_lock.unlock(); + + /* + * Now, the destruction thread attempts to close all the + * child's sessions. Check '_ready' flag periodically. + */ + while (!_ready) { + + /* give the destruction thread some time to proceed */ + timer->msleep(WATCHDOG_GRANULARITY_MS); + _watchdog_cnt += WATCHDOG_GRANULARITY_MS; + + /* check if we reached the timeout */ + if (_watchdog_cnt > timeout_ms) { + + /* + * The destruction seems to got stuck, let's shake it a + * bit to proceed and reset the watchdog counter to give + * the next blocking operation a chance to execute. + */ + cancel_blocking(); + _watchdog_cnt = 0; + } + } + } +}; + + +/** + * Construct a timer session for the watchdog timer on demand + */ +static Timer::Session *timer_session() +{ + static Timer::Connection timer; + return &timer; +} + + +/** + * Destruct Launchpad_child, cope with infinitely blocking server->close calls + * + * The arguments correspond to the 'Child_destructor_thread::submit_for_destruction' + * function. + */ +static void destruct_child(Allocator *alloc, Launchpad_child *child, + Timer::Session *timer, int timeout) +{ + /* lazily construct child-destructor thread */ + static Child_destructor_thread child_destructor; + + /* if no timer session was provided by our caller, we have create one */ + if (!timer) + timer = timer_session(); + + child_destructor.submit_for_destruction(alloc, child, timer, timeout); +} + + +void Launchpad::exit_child(Launchpad_child *child, + Timer::Session *timer, + int session_close_timeout_ms) +{ + remove_child(child->name(), child->heap()); + + Lock::Guard lock_guard(_children_lock); + _children.remove(child); + + Rm_session_capability rm_session_cap = child->rm_session_cap(); + Ram_session_capability ram_session_cap = child->ram_session_cap(); + Cpu_session_capability cpu_session_cap = child->cpu_session_cap(); + Rom_session_capability rom_session_cap = child->rom_session_cap(); + + const Genode::Server *server = child->server(); + destruct_child(&_sliced_heap, child, timer, session_close_timeout_ms); + + env()->parent()->close(rm_session_cap); + env()->parent()->close(cpu_session_cap); + env()->parent()->close(rom_session_cap); + env()->parent()->close(ram_session_cap); + + /* + * The killed child may have provided services to other children. + * Since the server is dead by now, we cannot close its sessions + * in the cooperative way. Instead, we need to instruct each + * other child to forget about session associated with the dead + * server. Note that the 'child' pointer points a a no-more + * existing object. It is only used to identify the corresponding + * session. It must never by de-referenced! + */ + Launchpad_child *c = _children.first(); + for ( ; c; c = c->Genode::List::Element::next()) + c->revoke_server(server); +} diff --git a/demo/src/lib/libpng/contrib/png.c b/demo/src/lib/libpng/contrib/png.c new file mode 100644 index 000000000..1118e3ca7 --- /dev/null +++ b/demo/src/lib/libpng/contrib/png.c @@ -0,0 +1,847 @@ + +/* png.c - location for general purpose libpng functions + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#define PNG_NO_EXTERN +#include "png.h" + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_12 Your_png_h_is_not_version_1_2_12; + +/* Version information for C files. This had better match the version + * string defined in png.h. */ + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* png_libpng_ver was changed to a function in version 1.0.5c */ +const char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING; + +#ifdef PNG_READ_SUPPORTED + +/* png_sig was changed to a function in version 1.0.5c */ +/* Place to hold the signature string for a PNG file. */ +const png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; +#endif /* PNG_READ_SUPPORTED */ + +/* Invoke global declarations for constant strings for known chunk types */ +PNG_IHDR; +PNG_IDAT; +PNG_IEND; +PNG_PLTE; +PNG_bKGD; +PNG_cHRM; +PNG_gAMA; +PNG_hIST; +PNG_iCCP; +PNG_iTXt; +PNG_oFFs; +PNG_pCAL; +PNG_sCAL; +PNG_pHYs; +PNG_sBIT; +PNG_sPLT; +PNG_sRGB; +PNG_tEXt; +PNG_tIME; +PNG_tRNS; +PNG_zTXt; + +#ifdef PNG_READ_SUPPORTED +/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + +/* start of interlace block */ +const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + +/* offset to next interlace block */ +const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + +/* start of interlace block in the y direction */ +const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + +/* offset to next interlace block in the y direction */ +const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + +/* width of interlace block (used in assembler routines only) */ +#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW +const int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; +#endif + +/* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h +const int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; +*/ + +/* Mask to determine which pixels are valid in a pass */ +const int FARDATA png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; + +/* Mask to determine which pixels to overwrite while displaying */ +const int FARDATA png_pass_dsp_mask[] + = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_set_sig_bytes(png_structp png_ptr, int num_bytes) +{ + png_debug(1, "in png_set_sig_bytes\n"); + if (num_bytes > 8) + png_error(png_ptr, "Too many bytes for PNG signature."); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* (Obsolete) function to check signature bytes. It does not allow one + * to check a partial signature. This function might be removed in the + * future - use png_sig_cmp(). Returns true (nonzero) if the file is a PNG. + */ +int PNGAPI +png_check_sig(png_bytep sig, int num) +{ + return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib and clear it to 0. */ +#ifdef PNG_1_0_X +voidpf PNGAPI +#else +voidpf /* private */ +#endif +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=png_ptr; + png_uint_32 save_flags=p->flags; + png_uint_32 num_bytes; + + if (items > PNG_UINT_32_MAX/size) + { + png_warning (png_ptr, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_uint_32)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + +#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) + if (ptr == NULL) + return ((voidpf)ptr); + + if (num_bytes > (png_uint_32)0x8000L) + { + png_memset(ptr, 0, (png_size_t)0x8000L); + png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, + (png_size_t)(num_bytes - (png_uint_32)0x8000L)); + } + else + { + png_memset(ptr, 0, (png_size_t)num_bytes); + } +#endif + return ((voidpf)ptr); +} + +/* function to free memory for zlib */ +#ifdef PNG_1_0_X +void PNGAPI +#else +void /* private */ +#endif +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct\n"); + if(png_ptr == NULL) return (NULL); +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct\n"); + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#undef png_info_init +void PNGAPI +png_info_init(png_infop info_ptr) +{ + /* We only come here via pre-1.0.12-compiled applications */ + png_info_init_3(&info_ptr, 0); +} +#endif + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3\n"); + + if(png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof (png_info)); +} + +#ifdef PNG_FREE_ME_SUPPORTED +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if(freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if(freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer."); +} +#endif + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + +#if defined(PNG_TEXT_SUPPORTED) +/* free text item num or (if num == -1) all text items */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_TEXT) +#endif +{ + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +/* free any tRNS entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) +#endif +{ + png_free(png_ptr, info_ptr->trans); + info_ptr->valid &= ~PNG_INFO_tRNS; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif + info_ptr->trans = NULL; +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +/* free any sCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SCAL) +#endif +{ +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +/* free any pCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_PCAL) +#endif +{ + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i]=NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +/* free any iCCP entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ICCP) +#endif +{ + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +/* free a given sPLT entry, or (if num == -1) all sPLT entries */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SPLT) +#endif +{ + if (num != -1) + { + if(info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if(info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) +#else +if (mask & PNG_FREE_UNKN) +#endif +{ + if (num != -1) + { + if(info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if(info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +/* free any hIST entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_HIST) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) +#endif +{ + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +} +#endif + +/* free any PLTE entry that was internally allocated */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) +#endif +{ + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif + info_ptr->num_palette = 0; +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* free any image bits attached to the info structure */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ROWS) +#endif +{ + if(info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row]=NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers=NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_FREE_ME_SUPPORTED + if(num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +#endif +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy\n"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if !defined(PNG_NO_STDIO) +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io\n"); + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#if defined(_WIN32_WCE) + { + wchar_t time_buf[29]; + wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29, + NULL, NULL); + } +#else +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + sprintf(near_time_buf, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + sprintf(png_ptr->time_buffer, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif +#endif /* _WIN32_WCE */ + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#if 0 +/* Signature string for a PNG file. */ +png_bytep PNGAPI +png_sig_bytes(void) +{ + return ((png_bytep)"\211\120\116\107\015\012\032\012"); +} +#endif +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) "\n libpng version 1.2.12 - June 27, 2006\n\ + Copyright (c) 1998-2006 Glenn Randers-Pehrson\n\ + Copyright (c) 1996-1997 Andreas Dilger\n\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n"); + return ((png_charp) ""); +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); + return ((png_charp) ""); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); + return ((png_charp) ""); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_HEADER_VERSION_STRING); + return ((png_charp) ""); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if((png_ptr == NULL && chunk_name == NULL) || png_ptr->num_chunk_list<=0) + return 0; + p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5; + for (i = png_ptr->num_chunk_list; i; i--, p-=5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p+4)); + return 0; +} +#endif + +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + return (inflateReset(&png_ptr->zstream)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + + +#if defined(PNG_READ_SUPPORTED) +#if !defined(PNG_1_0_X) +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this INTERNAL function was added to libpng 1.2.0 */ +void /* PRIVATE */ +png_init_mmx_flags (png_structp png_ptr) +{ + png_ptr->mmx_rowbytes_threshold = 0; + png_ptr->mmx_bitdepth_threshold = 0; + +# if (defined(PNG_USE_PNGVCRD) || defined(PNG_USE_PNGGCCRD)) + + png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_COMPILED; + + if (png_mmx_support() > 0) { + png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU +# ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW + | PNG_ASM_FLAG_MMX_READ_COMBINE_ROW +# endif +# ifdef PNG_HAVE_ASSEMBLER_READ_INTERLACE + | PNG_ASM_FLAG_MMX_READ_INTERLACE +# endif +# ifndef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW + ; +# else + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB + | PNG_ASM_FLAG_MMX_READ_FILTER_UP + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + + png_ptr->mmx_rowbytes_threshold = PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT; + png_ptr->mmx_bitdepth_threshold = PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT; +# endif + } else { + png_ptr->asm_flags &= ~( PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU + | PNG_MMX_READ_FLAGS + | PNG_MMX_WRITE_FLAGS ); + } + +# else /* !((PNGVCRD || PNGGCCRD) && PNG_ASSEMBLER_CODE_SUPPORTED)) */ + + /* clear all MMX flags; no support is compiled in */ + png_ptr->asm_flags &= ~( PNG_MMX_FLAGS ); + +# endif /* ?(PNGVCRD || PNGGCCRD) */ +} + +#endif /* !(PNG_ASSEMBLER_CODE_SUPPORTED) */ + +/* this function was added to libpng 1.2.0 */ +#if !defined(PNG_USE_PNGGCCRD) && \ + !(defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD)) +int PNGAPI +png_mmx_support(void) +{ + return -1; +} +#endif +#endif /* PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/demo/src/lib/libpng/contrib/pngerror.c b/demo/src/lib/libpng/contrib/pngerror.c new file mode 100644 index 000000000..ad6ae0e82 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngerror.c @@ -0,0 +1,313 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)); +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == '#') + { + int offset; + for (offset=1; offset<15; offset++) + if (*(error_message+offset) == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i=0; iflags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0]='0'; + msg[1]='\0'; + error_message=msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} + +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == '#') + { + for (offset=1; offset<15; offset++) + if (*(warning_message+offset) == ' ') + break; + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message+offset); + } + else + png_default_warning(png_ptr, warning_message+offset); +} + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = '['; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = ']'; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = 0; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + png_strncpy(buffer+iout, error_message, 63); + buffer[iout+63] = 0; + } +} + +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+64]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); +} + +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+64]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == '#') + { + int offset; + char error_number[16]; + for (offset=0; offset<15; offset++) + { + error_number[offset] = *(error_message+offset+1); + if (*(error_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + error_number[offset-1]='\0'; + fprintf(stderr, "libpng error no. %s: %s\n", error_number, + error_message+offset); + } + else + fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset); + } + else +#endif + fprintf(stderr, "libpng error: %s\n", error_message); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf,png_ptr->jmpbuf,png_sizeof(jmp_buf)); + longjmp(jmpbuf, 1); + } +# else + longjmp(png_ptr->jmpbuf, 1); +# endif +#else + /* make compiler happy */ ; + if (png_ptr) + PNG_ABORT(); +#endif +#ifdef PNG_NO_CONSOLE_IO + /* make compiler happy */ ; + if (&error_message != NULL) + return; +#endif +} + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == '#') + { + int offset; + char warning_number[16]; + for (offset=0; offset<15; offset++) + { + warning_number[offset]=*(warning_message+offset+1); + if (*(warning_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + warning_number[offset-1]='\0'; + fprintf(stderr, "libpng warning no. %s: %s\n", warning_number, + warning_message+offset); + } + else + fprintf(stderr, "libpng warning: %s\n", warning_message); + } + else +# endif + fprintf(stderr, "libpng warning: %s\n", warning_message); +#else + /* make compiler happy */ ; + if (warning_message) + return; +#endif + /* make compiler happy */ ; + if (png_ptr) + return; +} + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if(png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngget.c b/demo/src/lib/libpng/contrib/pngget.c new file mode 100644 index 000000000..df7658587 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngget.c @@ -0,0 +1,937 @@ + +/* pngget.c - retrieval of values from info struct + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +png_uint_32 PNGAPI +png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + else + return(0); +} + +png_uint_32 PNGAPI +png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + else + return(0); +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +png_bytepp PNGAPI +png_get_rows(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + else + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->width; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->height; + } + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->bit_depth; + } + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->color_type; + } + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->filter_type; + } + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->interlace_type; + } + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->compression_type; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->y_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER || + info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) + { + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio"); + if (info_ptr->x_pixels_per_unit == 0) + return ((float)0.0); + else + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + return (0.0); +#endif + return ((float)0.0); +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +float PNGAPI +png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_x_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +float PNGAPI +png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_y_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + if(*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +png_byte PNGAPI +png_get_channels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + else + return (0); +} + +png_bytep PNGAPI +png_get_signature(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + else + return (NULL); +} + +#if defined(PNG_bKGD_SUPPORTED) +png_uint_32 PNGAPI +png_get_bKGD(png_structp png_ptr, png_infop info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function\n", "bKGD"); + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + return (0); +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_structp png_ptr, png_infop info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = (double)info_ptr->x_white; + if (white_y != NULL) + *white_y = (double)info_ptr->y_white; + if (red_x != NULL) + *red_x = (double)info_ptr->x_red; + if (red_y != NULL) + *red_y = (double)info_ptr->y_red; + if (green_x != NULL) + *green_x = (double)info_ptr->x_green; + if (green_y != NULL) + *green_y = (double)info_ptr->y_green; + if (blue_x != NULL) + *blue_x = (double)info_ptr->x_blue; + if (blue_y != NULL) + *blue_y = (double)info_ptr->y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = info_ptr->int_x_white; + if (white_y != NULL) + *white_y = info_ptr->int_y_white; + if (red_x != NULL) + *red_x = info_ptr->int_x_red; + if (red_y != NULL) + *red_y = info_ptr->int_y_red; + if (green_x != NULL) + *green_x = info_ptr->int_x_green; + if (green_y != NULL) + *green_y = info_ptr->int_y_green; + if (blue_x != NULL) + *blue_x = info_ptr->int_x_blue; + if (blue_y != NULL) + *blue_y = info_ptr->int_y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *file_gamma = (double)info_ptr->gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *int_file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && int_file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *int_file_gamma = info_ptr->int_gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#endif + +#if defined(PNG_sRGB_SUPPORTED) +png_uint_32 PNGAPI +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sRGB"); + *file_srgb_intent = (int)info_ptr->srgb_intent; + return (PNG_INFO_sRGB); + } + return (0); +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +png_uint_32 PNGAPI +png_get_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && profile != NULL && proflen != NULL) + { + png_debug1(1, "in %s retrieval function\n", "iCCP"); + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + /* compression_type is a dummy so the API won't have to change + if we introduce multiple compression types later. */ + *proflen = (int)info_ptr->iccp_proflen; + *compression_type = (int)info_ptr->iccp_compression; + return (PNG_INFO_iCCP); + } + return (0); +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sPLT(png_structp png_ptr, png_infop info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + *spalettes = info_ptr->splt_palettes; + return ((png_uint_32)info_ptr->splt_palettes_num); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +png_uint_32 PNGAPI +png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + png_debug1(1, "in %s retrieval function\n", "hIST"); + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) + +{ + if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL && + bit_depth != NULL && color_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "IHDR"); + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16) + png_error(png_ptr, "Invalid bit depth"); + *color_type = info_ptr->color_type; + if (info_ptr->color_type > 6) + png_error(png_ptr, "Invalid color type"); + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* check for potential overflow of rowbytes */ + if (*width == 0 || *width > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image width"); + if (*height == 0 || *height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image height"); + if (info_ptr->width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + { + png_warning(png_ptr, + "Width too large for libpng to process image data."); + } + return (1); + } + return (0); +} + +#if defined(PNG_oFFs_SUPPORTED) +png_uint_32 PNGAPI +png_get_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "oFFs"); + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + return (0); +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +png_uint_32 PNGAPI +png_get_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + png_debug1(1, "in %s retrieval function\n", "pCAL"); + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + return (0); +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_structp png_ptr, png_infop info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_pixel_width; + *height = info_ptr->scal_pixel_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + return (retval); +} +#endif + +png_uint_32 PNGAPI +png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, + int *num_palette) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + png_debug1(1, "in %s retrieval function\n", "PLTE"); + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d\n", *num_palette); + return (PNG_INFO_PLTE); + } + return (0); +} + +#if defined(PNG_sBIT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sBIT"); + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + return (0); +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +png_uint_32 PNGAPI +png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, + int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in %s retrieval function\n", + (png_ptr->chunk_name[0] == '\0' ? "text" + : (png_const_charp)png_ptr->chunk_name)); + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + if (num_text != NULL) + *num_text = info_ptr->num_text; + return ((png_uint_32)info_ptr->num_text); + } + if (num_text != NULL) + *num_text = 0; + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +png_uint_32 PNGAPI +png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + png_debug1(1, "in %s retrieval function\n", "tIME"); + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + return (0); +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +png_uint_32 PNGAPI +png_get_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep *trans, int *num_trans, png_color_16p *trans_values) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function\n", "tRNS"); + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans != NULL) + { + *trans = info_ptr->trans; + retval |= PNG_INFO_tRNS; + } + if (trans_values != NULL) + *trans_values = &(info_ptr->trans_values); + } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_values != NULL) + { + *trans_values = &(info_ptr->trans_values); + retval |= PNG_INFO_tRNS; + } + if(trans != NULL) + *trans = NULL; + } + if(num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + return (retval); +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +png_uint_32 PNGAPI +png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + *unknowns = info_ptr->unknown_chunks; + return ((png_uint_32)info_ptr->unknown_chunks_num); +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +png_byte PNGAPI +png_get_rgb_to_gray_status (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +png_voidp PNGAPI +png_get_user_chunk_ptr(png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_ptr : NULL); +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +png_uint_32 PNGAPI +png_get_compression_buffer_size(png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L); +} +#endif + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flags (png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->asm_flags : 0L); +} + +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flagmask (int flag_select) +{ + png_uint_32 settable_asm_flags = 0; + + if (flag_select & PNG_SELECT_READ) + settable_asm_flags |= + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | + PNG_ASM_FLAG_MMX_READ_INTERLACE | + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + /* no non-MMX flags yet */ + +#if 0 + /* GRR: no write-flags yet, either, but someday... */ + if (flag_select & PNG_SELECT_WRITE) + settable_asm_flags |= + PNG_ASM_FLAG_MMX_WRITE_ [whatever] ; +#endif /* 0 */ + + return settable_asm_flags; /* _theoretically_ settable capabilities only */ +} +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_flagmask (int flag_select, int *compilerID) +{ + png_uint_32 settable_mmx_flags = 0; + + if (flag_select & PNG_SELECT_READ) + settable_mmx_flags |= + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | + PNG_ASM_FLAG_MMX_READ_INTERLACE | + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; +#if 0 + /* GRR: no MMX write support yet, but someday... */ + if (flag_select & PNG_SELECT_WRITE) + settable_mmx_flags |= + PNG_ASM_FLAG_MMX_WRITE_ [whatever] ; +#endif /* 0 */ + + if (compilerID != NULL) { +#ifdef PNG_USE_PNGVCRD + *compilerID = 1; /* MSVC */ +#else +#ifdef PNG_USE_PNGGCCRD + *compilerID = 2; /* gcc/gas */ +#else + *compilerID = -1; /* unknown (i.e., no asm/MMX code compiled) */ +#endif +#endif + } + + return settable_mmx_flags; /* _theoretically_ settable capabilities only */ +} + +/* this function was added to libpng 1.2.0 */ +png_byte PNGAPI +png_get_mmx_bitdepth_threshold (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->mmx_bitdepth_threshold : 0); +} + +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_rowbytes_threshold (png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->mmx_rowbytes_threshold : 0L); +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ +#endif /* ?PNG_1_0_X */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* these functions were added to libpng 1.2.6 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_width_max : 0); +} +png_uint_32 PNGAPI +png_get_user_height_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_height_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngmem.c b/demo/src/lib/libpng/contrib/pngmem.c new file mode 100644 index 000000000..cfe3e0fde --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngmem.c @@ -0,0 +1,598 @@ + +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +/* Borland DOS special memory handler */ +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* if you change this, be sure to change the one in png.h also */ + +/* Allocate memory for a png_struct. The malloc and memset can be replaced + by a single call to calloc() if this is thought to improve performance. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Alternate version of png_create_struct, for use with user-defined malloc. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (png_get_copyright(NULL)); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); + } + else +#endif /* PNG_USER_MEM_SUPPORTED */ + struct_ptr = (png_voidp)farmalloc(size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ + farfree (struct_ptr); + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * Borland seems to have a problem in DOS mode for exactly 64K. + * It gives you a segment with an offset of 8 (perhaps to store its + * memory stuff). zlib doesn't like this at all, so we have to + * detect and deal with it. This code should not be needed in + * Windows or OS/2 modes, and only in 16 bit mode. This code has + * been updated by Alexander Lehmann for version 0.89 to waste less + * memory. + * + * Note that we can't use png_size_t for the "size" declaration, + * since on some systems a png_size_t is a 16-bit quantity, and as a + * result, we would be truncating potentially larger memory requests + * (which should cause a fatal error) and introducing major problems. + */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { + png_warning(png_ptr, "Cannot Allocate > 64K"); + ret = NULL; + } + else +#endif + + if (size != (size_t)size) + ret = NULL; + else if (size == (png_uint_32)65536L) + { + if (png_ptr->offset_table == NULL) + { + /* try to see if we need to do any of this fancy stuff */ + ret = farmalloc(size); + if (ret == NULL || ((png_size_t)ret & 0xffff)) + { + int num_blocks; + png_uint_32 total_size; + png_bytep table; + int i; + png_byte huge * hptr; + + if (ret != NULL) + { + farfree(ret); + ret = NULL; + } + + if(png_ptr->zlib_window_bits > 14) + num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); + else + num_blocks = 1; + if (png_ptr->zlib_mem_level >= 7) + num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); + else + num_blocks++; + + total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; + + table = farmalloc(total_size); + + if (table == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of Memory."); +#endif + return (NULL); + } + + if ((png_size_t)table & 0xfff0) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, + "Farmalloc didn't return normalized pointer"); + else + png_warning(png_ptr, + "Farmalloc didn't return normalized pointer"); +#endif + return (NULL); + } + + png_ptr->offset_table = table; + png_ptr->offset_table_ptr = farmalloc(num_blocks * + png_sizeof (png_bytep)); + + if (png_ptr->offset_table_ptr == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of memory."); +#endif + return (NULL); + } + + hptr = (png_byte huge *)table; + if ((png_size_t)hptr & 0xf) + { + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ + } + for (i = 0; i < num_blocks; i++) + { + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ + } + + png_ptr->offset_table_number = num_blocks; + png_ptr->offset_table_count = 0; + png_ptr->offset_table_count_free = 0; + } + } + + if (png_ptr->offset_table_count >= png_ptr->offset_table_number) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */ + else + png_warning(png_ptr, "Out of Memory."); +#endif + return (NULL); + } + + ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; + } + else + ret = farmalloc(size); + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL) + { + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */ + else + png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */ + } +#endif + + return (ret); +} + +/* free a pointer allocated by png_malloc(). In the default + configuration, png_ptr is not used, but is passed in case it + is needed. If ptr is NULL, return without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} + +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr->offset_table != NULL) + { + int i; + + for (i = 0; i < png_ptr->offset_table_count; i++) + { + if (ptr == png_ptr->offset_table_ptr[i]) + { + ptr = NULL; + png_ptr->offset_table_count_free++; + break; + } + } + if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) + { + farfree(png_ptr->offset_table); + farfree(png_ptr->offset_table_ptr); + png_ptr->offset_table = NULL; + png_ptr->offset_table_ptr = NULL; + } + } + + if (ptr != NULL) + { + farfree(ptr); + } +} + +#else /* Not the Borland DOS special memory handler */ + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); + } +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + struct_ptr = (png_voidp)farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + struct_ptr = (png_voidp)halloc(size,1); +# else + struct_ptr = (png_voidp)malloc(size); +# endif +#endif + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + + return (struct_ptr); +} + + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(struct_ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(struct_ptr); +# else + free(struct_ptr); +# endif +#endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr == NULL || size == 0) + return (NULL); + + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { +#ifndef PNG_USER_MEM_SUPPORTED + if(png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Cannot Allocate > 64K"); + else +#endif + return NULL; + } +#endif + + /* Check for overflow */ +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(ptr); +# else + free(ptr); +# endif +#endif +} + +#endif /* Not Borland DOS special memory handler */ + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will set up png_malloc() to issue a png_warning and return NULL + * instead of issuing a png_error, if it fails to allocate the requested + * memory. + */ +png_voidp PNGAPI +png_malloc_warn(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ptr; + png_uint_32 save_flags=png_ptr->flags; + + png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); + png_ptr->flags=save_flags; + return(ptr); +} +#endif + +png_voidp PNGAPI +png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memcpy_check."); + + return(png_memcpy (s1, s2, size)); +} + +png_voidp PNGAPI +png_memset_check (png_structp png_ptr, png_voidp s1, int value, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memset_check."); + + return (png_memset (s1, value, size)); + +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_structp png_ptr) +{ + return ((png_voidp)png_ptr->mem_ptr); +} +#endif /* PNG_USER_MEM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngpread.c b/demo/src/lib/libpng/contrib/pngpread.c new file mode 100644 index 000000000..7a0d13a78 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngpread.c @@ -0,0 +1,1578 @@ + +/* pngpread.c - read a png file in push mode + * + * Last changed in libpng 1.2.11 - June 7, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structp png_ptr, png_infop info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structp png_ptr, png_infop info_ptr) +{ + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } +#if defined(PNG_READ_tEXt_SUPPORTED) + case PNG_READ_tEXt_MODE: + { + png_push_read_tEXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + case PNG_READ_zTXt_MODE: + { + png_push_read_zTXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + case PNG_READ_iTXt_MODE: + { + png_push_read_iTXt(png_ptr, info_ptr); + break; + } +#endif + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + if(png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + { + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + + png_ptr->idat_size = png_ptr->push_length; + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + else + save_size = png_ptr->save_buffer_size; + + png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + else + save_size = png_ptr->current_buffer_size; + + png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i,istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr, + (png_uint_32)new_max); + png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_error(png_ptr, "Not enough compressed data"); + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + int ret; + + if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length) + png_error(png_ptr, "Extra compression data"); + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = (uInt)buffer_length; + for(;;) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK) + { + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_in) + png_error(png_ptr, "Extra compressed data"); + if (!(png_ptr->zstream.avail_out)) + { + png_push_process_row(png_ptr); + } + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + else if (ret == Z_BUF_ERROR) + break; + else + png_error(png_ptr, "Decompression Error"); + } + if (!(png_ptr->zstream.avail_out)) + { + if (( +#if defined(PNG_READ_INTERLACING_SUPPORTED) + png_ptr->interlaced && png_ptr->pass > 6) || + (!png_ptr->interlaced && +#endif + png_ptr->row_number == png_ptr->num_rows)) + { + if (png_ptr->zstream.avail_in) + png_warning(png_ptr, "Too much data in IDAT chunks"); + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + png_push_process_row(png_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + } + else + break; + } +} + +void /* PRIVATE */ +png_push_process_row(png_structp png_ptr) +{ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */ + } + if (png_ptr->pass == 2) /* pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 2) /* skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 2: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 3: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 4: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* pass 5 might be empty */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 5: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* skip top generated row */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + if (png_ptr->pass != 6) + break; + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Width of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + const int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; + */ + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + const int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +} + +#if defined(PNG_READ_tEXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place tEXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_tEXt_MODE; +} + +void /* PRIVATE */ +png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + if (text != key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + png_ptr->current_text = NULL; + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place zTXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + /* We can't handle zTXt chunks > 64K, since we don't have enough space + * to be able to store the uncompressed data. Actually, the threshold + * is probably around 32K, but it isn't as definite as 64K is. + */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_push_crc_skip(png_ptr, length); + return; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_zTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + png_size_t text_size, key_size; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + /* zTXt can't have zero text */ + if (text == key + png_ptr->current_text_size) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */ + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + png_ptr->zstream.next_in = (png_bytep )text; + png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - + (text - key)); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + key_size = text - key; + text_size = 0; + text = NULL; + ret = Z_STREAM_END; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) + { + if (text == NULL) + { + text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + key_size + 1)); + png_memcpy(text + key_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_memcpy(text, key, key_size); + text_size = key_size + png_ptr->zbuf_size - + png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc(png_ptr, text_size + + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + 1)); + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + if (ret != Z_STREAM_END) + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else + { + break; + } + + if (ret == Z_STREAM_END) + break; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (ret != Z_STREAM_END) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + + png_ptr->current_text = NULL; + png_free(png_ptr, key); + key = text; + text += key_size; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place iTXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_iTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) +{ + + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp key; + int comp_flag; + png_charp lang; + png_charp lang_key; + png_charp text; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (lang = key; *lang; lang++) + /* empty loop */ ; + + if (lang != key + png_ptr->current_text_size) + lang++; + + comp_flag = *lang++; + lang++; /* skip comp_type, always zero */ + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + for (text = lang_key; *text; text++) + /* empty loop */ ; + + if (text != key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = comp_flag + 2; + text_ptr->key = key; + text_ptr->lang = lang; + text_ptr->lang_key = lang_key; + text_ptr->text = text; + text_ptr->text_length = 0; + text_ptr->itxt_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_ptr->current_text = NULL; + + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to store iTXt chunk."); + } +} +#endif + +/* This function is called when we haven't found a handler for this + * chunk. If there isn't a problem with the chunk itself (ie a bad chunk + * name or a critical chunk), the chunk is (currently) silently ignored. + */ +void /* PRIVATE */ +png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + png_uint_32 skip=0; + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + + /* to quiet compiler warnings about unused info_ptr */ + if (info_ptr == NULL) + return; + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) + { + png_unknown_chunk chunk; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_strcpy((png_charp)chunk.name, (png_charp)png_ptr->chunk_name); + chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_crc_read(png_ptr, chunk.data, length); + chunk.size = length; +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + if ((*(png_ptr->read_user_chunk_fn)) (png_ptr, &chunk) <= 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + } + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + png_free(png_ptr, chunk.data); + } + else +#endif + skip=length; + png_push_crc_skip(png_ptr, skip); +} + +void /* PRIVATE */ +png_push_have_info(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +void PNGAPI +png_progressive_combine_row (png_structp png_ptr, + png_bytep old_row, png_bytep new_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + const int FARDATA png_pass_dsp_mask[7] = + {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; +#endif + if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ + png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); +} + +void PNGAPI +png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_structp png_ptr) +{ + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngread.c b/demo/src/lib/libpng/contrib/pngread.c new file mode 100644 index 000000000..28706aad6 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngread.c @@ -0,0 +1,1461 @@ + +/* pngread.c - read a PNG file + * + * Last changed in libpng 1.2.11 June 7, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +/* Create a PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ + +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate create PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + png_structp png_ptr; + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + int i; + + png_debug(1, "in png_create_read_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif + if (png_ptr == NULL) + return (NULL); + +#if !defined(PNG_1_0_X) +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_init_mmx_flags(png_ptr); /* 1.2.0 addition */ +#endif +#endif /* PNG_1_0_X */ + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif + + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Initialize PNG structure for reading, and allocate any memory needed. + This interface is deprecated in favour of the png_create_read_struct(), + and it will disappear as of libpng-1.3.0. */ +#undef png_read_init +void PNGAPI +png_read_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for reading is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by application for reading is too small."); + } + png_read_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + +void PNGAPI +png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i=0; + + png_structp png_ptr=*ptr_ptr; + + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_read_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_read_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if(png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + png_ptr = *ptr_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_info\n"); + /* If we haven't checked all of the PNG signature bytes, do so now. */ + if (png_ptr->sig_bytes < 8) + { + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + } + + for(;;) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + png_byte chunk_length[4]; + png_uint_32 length; + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name, + length); + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + if(png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + break; + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->idat_size = length; + png_ptr->mode |= PNG_HAVE_IDAT; + break; + } +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_update_info\n"); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + else + png_warning(png_ptr, + "Ignoring extra png_read_update_info() call; row buffer not reallocated"); + png_read_transform_info(png_ptr, info_ptr); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structp png_ptr) +{ + png_debug(1, "in png_start_read_image\n"); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; + const int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + const int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; +#endif + int ret; + png_debug2(1, "in png_read_row (row %lu, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined."); +#endif + } + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* if interlaced and we do not need a new row, combine row and return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + png_ptr->zstream.next_out = png_ptr->row_buf; + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + do + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, + (png_size_t)png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_error(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression error"); + + } while (png_ptr->zstream.avail_out); + + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + if(png_ptr->row_buf[0]) + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + if (row != NULL) + png_combine_row(png_ptr, row, + png_pass_mask[png_ptr->pass]); + } + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, 0xff); + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 0xff); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ + +void PNGAPI +png_read_rows(png_structp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows\n"); + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + else if(rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, png_bytep_NULL); + rp++; + } + else if(dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, png_bytep_NULL, dptr); + dp++; + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ +void PNGAPI +png_read_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i,image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image\n"); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass = png_set_interlace_handling(png_ptr); +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled."); + pass = 1; +#endif + + + image_height=png_ptr->height; + png_ptr->num_rows = image_height; /* Make sure this is set correctly */ + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, png_bytep_NULL); + rp++; + } + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structp png_ptr, png_infop info_ptr) +{ + png_byte chunk_length[4]; + png_uint_32 length; + + png_debug(1, "in png_read_end\n"); + png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + + do + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name); + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + } + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + png_crc_finish(png_ptr, length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL, end_info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; + png_voidp mem_ptr; +#endif + + png_debug(1, "in png_destroy_read_struct\n"); + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (end_info_ptr_ptr != NULL) + end_info_ptr = *end_info_ptr_ptr; + +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + + png_read_destroy(png_ptr, info_ptr, end_info_ptr); + + if (info_ptr != NULL) + { +#if defined(PNG_TEXT_SUPPORTED) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (end_info_ptr != NULL) + { +#if defined(PNG_READ_TEXT_SUPPORTED) + png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); +#endif +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)end_info_ptr); +#endif + *end_info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* free all memory used by the read (old method) */ +void /* PRIVATE */ +png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_read_destroy\n"); + if (info_ptr != NULL) + png_info_destroy(png_ptr, info_ptr); + + if (end_info_ptr != NULL) + png_info_destroy(png_ptr, end_info_ptr); + + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->prev_row); +#if defined(PNG_READ_DITHER_SUPPORTED) + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->dither_index); +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_table); +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_free(png_ptr, png_ptr->gamma_to_1); +#endif +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; +#else + if (png_ptr->flags & PNG_FLAG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#else + if (png_ptr->flags & PNG_FLAG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif +#endif +#if defined(PNG_READ_hIST_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->free_me &= ~PNG_FREE_HIST; +#else + if (png_ptr->flags & PNG_FLAG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + } +#endif +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + + inflateEnd(&png_ptr->zstream); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +#ifdef PNG_TEXT_SUPPORTED + png_free(png_ptr, png_ptr->current_text); +#endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + + /* Save the important info out of the png_struct, in case it is + * being used again. + */ +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + +} + +void PNGAPI +png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +{ + png_ptr->read_row_fn = read_row_fn; +} + + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_read_png(png_structp png_ptr, png_infop info_ptr, + int transforms, + voidp params) +{ + int row; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error(png_ptr,"Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + /* tell libpng to strip 16 bit/color files down to 8 bits per color + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or dithering. + */ + +#if defined(PNG_READ_INVERT_SUPPORTED) + /* invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + png_color_8p sig_bit; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + /* flip the RGB pixels to BGR (or RGBA to BGRA) + */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) + */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + /* swap bytes of 16 bit files to least significant byte first + */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); +#endif + if(info_ptr->row_pointers == NULL) + { + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * png_sizeof(png_bytep)); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ROWS; +#endif + for (row = 0; row < (int)info_ptr->height; row++) + { + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + if(transforms == 0 || params == NULL) + /* quiet compiler warnings */ return; + +} +#endif /* PNG_INFO_IMAGE_SUPPORTED */ +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngrio.c b/demo/src/lib/libpng/contrib/pngrio.c new file mode 100644 index 000000000..ce05cada7 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngrio.c @@ -0,0 +1,164 @@ + +/* pngrio.c - functions for data input + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +/* Read the data from whatever input you are using. The default routine + reads from a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered reads. This should never be asked + to read more then 64K on a 16 bit machine. */ +void /* PRIVATE */ +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4,"reading %d bytes\n", (int)length); + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL read function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = (png_size_t)fread(data, (png_size_t)1, length, + (png_FILE_p)png_ptr->io_ptr); +#endif + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fread(n_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) + err = 0; +#else + err = fread(buf, (png_size_t)1, read, io_ptr); +#endif + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if ((png_uint_32)check != (png_uint_32)length) + png_error(png_ptr, "read Error"); +} +#endif +#endif + +/* This function allows the application to supply a new input function + for libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png input data structure + io_ptr - pointer to user supplied structure containing info about + the input functions. May be NULL. + read_data_fn - pointer to a new input function that takes as its + arguments a pointer to a png_struct, a pointer to + a location where input data can be stored, and a 32-bit + unsigned int that is the number of bytes to be read. + To exit and output any fatal error messages the new write + function should call png_error(png_ptr, "Error msg"). */ +void PNGAPI +png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "It's an error to set both read_data_fn and write_data_fn in the "); + png_warning(png_ptr, + "same structure. Resetting write_data_fn to NULL."); + } + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->output_flush_fn = NULL; +#endif +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngrtran.c b/demo/src/lib/libpng/contrib/pngrtran.c new file mode 100644 index 000000000..bd1f59787 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngrtran.c @@ -0,0 +1,4219 @@ + +/* pngrtran.c - transforms the data in a row for PNG readers + * + * Last changed in libpng 1.2.11 June 15, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action\n"); + /* Tell libpng how we react to CRC errors in critical chunks */ + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + case PNG_CRC_WARN_DISCARD: /* not a valid action for critical data */ + png_warning(png_ptr, "Can't discard critical data on CRC error."); + case PNG_CRC_ERROR_QUIT: /* error/quit */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_ERROR_QUIT: /* error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_WARN_DISCARD: /* warn/discard data */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* handle alpha and tRNS via a background color */ +void PNGAPI +png_set_background(png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_debug(1, "in png_set_background\n"); + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_BACKGROUND; + png_memcpy(&(png_ptr->background), background_color, + png_sizeof(png_color_16)); + png_ptr->background_gamma = (float)background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); + + /* Note: if need_expand is set and color_type is either RGB or RGB_ALPHA + * (in which case need_expand is superfluous anyway), the background color + * might actually be gray yet not be flagged as such. This is not a problem + * for the current code, which uses PNG_BACKGROUND_IS_GRAY only to + * decide when to do the png_do_gray_to_rgb() transformation. + */ + if ((need_expand && !(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) || + (!need_expand && background_color->red == background_color->green && + background_color->red == background_color->blue)) + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip 16 bit depth files to 8 bit depth */ +void PNGAPI +png_set_strip_16(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_16\n"); + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +void PNGAPI +png_set_strip_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha\n"); + png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Dither file to 8 bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_dither" indicates + * whether we need a dithering cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct FAR * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort FAR * png_dsortp; +typedef png_dsort FAR * FAR * png_dsortpp; + +void PNGAPI +png_set_dither(png_structp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_uint_16p histogram, + int full_dither) +{ + png_debug(1, "in png_set_dither\n"); + png_ptr->transformations |= PNG_DITHER; + + if (!full_dither) + { + int i; + + png_ptr->dither_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + for (i = 0; i < num_palette; i++) + png_ptr->dither_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + Perhaps not the best solution, but good enough. */ + + int i; + + /* initialize an array to sort colors */ + png_ptr->dither_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the dither_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->dither_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + bubble sort, and running it until we have sorted + out enough colors. Note that we don't care about + sorting all the colors, just finding which are + least used. */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* to stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->dither_sort[j]] + < histogram[png_ptr->dither_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->dither_sort[j]; + png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1]; + png_ptr->dither_sort[j + 1] = t; + done = 0; + } + } + if (done) + break; + } + + /* swap the palette around, and set up a table, if necessary */ + if (full_dither) + { + int j = num_palette; + + /* put all the useful colors within the max, but don't + move the others */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* move all the used colors inside the max limit, and + develop a translation table */ + for (i = 0; i < maximum_colors; i++) + { + /* only move the colors we need to */ + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* indicate where the color went */ + png_ptr->dither_index[j] = (png_byte)i; + png_ptr->dither_index[i] = (png_byte)j; + } + } + + /* find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->dither_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* find the closest color to one we threw out */ + d_index = png_ptr->dither_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* point to closest color */ + png_ptr->dither_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->dither_sort); + png_ptr->dither_sort=NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + we need to go through a median cut routine, but those + don't always behave themselves with only a few colors + as input. So we will just find the closest two colors, + and throw out one of them (chosen somewhat randomly). + [We don't understand this at all, so if someone wants to + work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t=NULL; + + /* initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 * + png_sizeof (png_dsortp))); + for (i = 0; i < 769; i++) + hash[i] = NULL; +/* png_memset(hash, 0, 769 * png_sizeof (png_dsortp)); */ + + num_new_palette = num_palette; + + /* initial wild guess at how far apart the farthest pixel + pair we will be eliminating will be. Larger + numbers mean more areas will be allocated, Smaller + numbers run the risk of not saving enough data, and + having to do this all over again. + + I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_uint_32)(png_sizeof(png_dsort))); + if (t == NULL) + break; + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (!full_dither) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->dither_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[next_j]; + if ((int)png_ptr->dither_index[k] == + num_new_palette) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = (png_byte)num_new_palette; + png_ptr->palette_to_index[num_new_palette] = (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index=NULL; + png_ptr->index_to_palette=NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_dither) + { + int i; + png_bytep distance; + int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS + + PNG_DITHER_BLUE_BITS; + int num_red = (1 << PNG_DITHER_RED_BITS); + int num_green = (1 << PNG_DITHER_GREEN_BITS); + int num_blue = (1 << PNG_DITHER_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr, + (png_uint_32)(num_entries * png_sizeof (png_byte))); + + png_memset(png_ptr->palette_lookup, 0, num_entries * + png_sizeof (png_byte)); + + distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * + png_sizeof(png_byte))); + + png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) +/* Transform the image from the file_gamma to the screen_gamma. We + * only do transformations on images where the file_gamma and screen_gamma + * are not close reciprocals, otherwise it slows things down slightly, and + * also needlessly introduces small errors. + * + * We will turn off gamma transformation later if no semitransparent entries + * are present in the tRNS array for palette images. We can't do it here + * because we don't necessarily have the tRNS chunk yet. + */ +void PNGAPI +png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) +{ + png_debug(1, "in png_set_gamma\n"); + if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + png_ptr->transformations |= PNG_GAMMA; + png_ptr->gamma = (float)file_gamma; + png_ptr->screen_gamma = (float)scrn_gamma; +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + * + * GRP 20060307: In libpng-1.4.0, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha. + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_palette_to_rgb\n"); + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} + +#if !defined(PNG_1_0_X) +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand_gray_1_2_4_to_8\n"); + png_ptr->transformations |= PNG_EXPAND_tRNS; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +/* Deprecated as of libpng-1.2.9 */ +void PNGAPI +png_set_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_1_2_4_to_8\n"); + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif + + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +void PNGAPI +png_set_gray_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb\n"); + png_ptr->transformations |= PNG_GRAY_TO_RGB; +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, + double green) +{ + int red_fixed = (int)((float)red*100000.0 + 0.5); + int green_fixed = (int)((float)green*100000.0 + 0.5); + png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); +} +#endif + +void PNGAPI +png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray\n"); + switch(error_action) + { + case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#if defined(PNG_READ_EXPAND_SUPPORTED) + png_ptr->transformations |= PNG_EXPAND; +#else + { + png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED."); + png_ptr->transformations &= ~PNG_RGB_TO_GRAY; + } +#endif + { + png_uint_16 red_int, green_int; + if(red < 0 || green < 0) + { + red_int = 6968; /* .212671 * 32768 + .5 */ + green_int = 23434; /* .715160 * 32768 + .5 */ + } + else if(red + green < 100000L) + { + red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); + green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); + } + else + { + png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); + red_int = 6968; + green_int = 23434; + } + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(32768-red_int-green_int); + } +} +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn\n"); +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +#ifdef PNG_LEGACY_SUPPORTED + if(read_user_transform_fn) + png_warning(png_ptr, + "This version of libpng does not support user transforms"); +#endif +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ +void /* PRIVATE */ +png_init_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_init_read_transformations\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if(png_ptr != NULL) +#endif + { +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \ + || defined(PNG_READ_GAMMA_SUPPORTED) + int color_type = png_ptr->color_type; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + { + /* expand background and tRNS chunks */ + switch (png_ptr->bit_depth) + { + case 1: + png_ptr->background.gray *= (png_uint_16)0xff; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0xff; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 2: + png_ptr->background.gray *= (png_uint_16)0x55; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x55; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 4: + png_ptr->background.gray *= (png_uint_16)0x11; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x11; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 8: + case 16: + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + } + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) +#endif + { + /* invert the alpha channel (in tRNS) unless the pixels are + going to be expanded, in which case leave it for later */ + int i,istop; + istop=(int)png_ptr->num_trans; + for (i=0; itrans[i] = (png_byte)(255 - png_ptr->trans[i]); + } + } +#endif + + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + png_ptr->background_1 = png_ptr->background; +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + + if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) + && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) + < PNG_GAMMA_THRESHOLD)) + { + int i,k; + k=0; + for (i=0; inum_trans; i++) + { + if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff) + k=1; /* partial transparency is present */ + } + if (k == 0) + png_ptr->transformations &= (~PNG_GAMMA); + } + + if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && + png_ptr->gamma != 0.0) + { + png_build_gamma_table(png_ptr); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* could skip if no transparency and + */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + default: + g = 1.0; /* back_1 */ + gs = 1.0; /* back */ + } + + if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + else + { + back.red = (png_byte)(pow( + (double)png_ptr->background.red/255, gs) * 255.0 + .5); + back.green = (png_byte)(pow( + (double)png_ptr->background.green/255, gs) * 255.0 + .5); + back.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, gs) * 255.0 + .5); + } + + back_1.red = (png_byte)(pow( + (double)png_ptr->background.red/255, g) * 255.0 + .5); + back_1.green = (png_byte)(pow( + (double)png_ptr->background.green/255, g) * 255.0 + .5); + back_1.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, g) * 255.0 + .5); + } + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else + /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); + double g = 1.0; + double gs = 1.0; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + } + + png_ptr->background_1.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, g) * m + .5); + png_ptr->background.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, gs) * m + .5); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + png_ptr->background_1.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, g) * m + .5); + png_ptr->background_1.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, g) * m + .5); + png_ptr->background_1.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, g) * m + .5); + png_ptr->background.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, gs) * m + .5); + png_ptr->background.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, gs) * m + .5); + png_ptr->background.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, gs) * m + .5); + } + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + } + } + else + /* transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + /* No GAMMA transformation */ + if ((png_ptr->transformations & PNG_BACKGROUND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (png_ptr->trans[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if ((png_ptr->transformations & PNG_SHIFT) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + png_uint_16 i; + png_uint_16 istop = png_ptr->num_palette; + int sr = 8 - png_ptr->sig_bit.red; + int sg = 8 - png_ptr->sig_bit.green; + int sb = 8 - png_ptr->sig_bit.blue; + + if (sr < 0 || sr > 8) + sr = 0; + if (sg < 0 || sg > 8) + sg = 0; + if (sb < 0 || sb > 8) + sb = 0; + for (i = 0; i < istop; i++) + { + png_ptr->palette[i].red >>= sr; + png_ptr->palette[i].green >>= sg; + png_ptr->palette[i].blue >>= sb; + } + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ + } +#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ + && !defined(PNG_READ_BACKGROUND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_transform_info\n"); +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + { + if (png_ptr->transformations & PNG_EXPAND_tRNS) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + else + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; + info_ptr->num_trans = 0; + info_ptr->background = png_ptr->background; + } +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = png_ptr->gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_ptr->int_gamma; +#endif + } +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) + info_ptr->bit_depth = 8; +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#if defined(PNG_READ_FILLER_SUPPORTED) + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* if adding a true alpha channel not just filler */ +#if !defined(PNG_1_0_X) + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +#endif + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + if(info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width); + +#if !defined(PNG_READ_EXPAND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_read_transformations\n"); +#if !defined(PNG_USELESS_TESTS_SUPPORTED) + if (png_ptr->row_buf == NULL) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + + sprintf(msg, "NULL row buffer for row %ld, pass %d", png_ptr->row_number, + png_ptr->pass); + png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif + } +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans, png_ptr->num_trans); + } + else + { + if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values)); + else + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1); + if(rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if(png_ptr->transformations == PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + if(png_ptr->transformations == PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* +From Andreas Dilger e-mail to png-implement, 26 March 1998: + + In most cases, the "simple transparency" should be done prior to doing + gray-to-RGB, or you will have to test 3x as many bytes to check if a + pixel is transparent. You would also need to make sure that the + transparency information is upgraded to RGB. + + To summarize, the current flow is: + - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + with background "in place" if transparent, + convert to RGB if necessary + - Gray + alpha -> composite with gray background and remove alpha bytes, + convert to RGB if necessary + + To support RGB backgrounds for gray images we need: + - Gray + simple transparency -> convert to RGB + simple transparency, compare + 3 or 6 bytes and composite with background + "in place" if transparent (3x compare/pixel + compared to doing composite with gray bkgrnd) + - Gray + alpha -> convert to RGB + alpha, composite with background and + remove alpha bytes (3x float operations/pixel + compared with composite on gray background) + + Greg's change will do this. The reason it wasn't done before is for + performance, as this increases the per-pixel operations. If we would check + in advance if the background was gray or RGB, and position the gray-to-RGB + transform appropriately, then it would save a lot of work/time. + */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0 ) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) + png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values), &(png_ptr->background) +#if defined(PNG_READ_GAMMA_SUPPORTED) + , &(png_ptr->background_1), + png_ptr->gamma_table, png_ptr->gamma_from_1, + png_ptr->gamma_to_1, png_ptr->gamma_16_table, + png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, + png_ptr->gamma_shift +#endif +); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if ((png_ptr->transformations & PNG_GAMMA) && +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + !((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->gamma_table, png_ptr->gamma_16_table, + png_ptr->gamma_shift); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->dither_index); + if(png_ptr->row_info.rowbytes == (png_uint_32)0) + png_error(png_ptr, "png_do_dither returned rowbytes=0"); + } +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if we did not do so above */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* user read transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->user_transform_depth) + png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; + if(png_ptr->user_transform_channels) + png_ptr->row_info.channels = png_ptr->user_transform_channels; +#endif + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + } +#endif + +} + +#if defined(PNG_READ_PACK_SUPPORTED) +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth < 8) +#else + if (row_info->bit_depth < 8) +#endif + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +{ + png_debug(1, "in png_do_unshift\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && sig_bits != NULL && +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int c; + png_uint_16 value = 0; + png_uint_32 row_width = row_info->width; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = row_info->bit_depth - sig_bits->red; + shift[channels++] = row_info->bit_depth - sig_bits->green; + shift[channels++] = row_info->bit_depth - sig_bits->blue; + } + else + { + shift[channels++] = row_info->bit_depth - sig_bits->gray; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = row_info->bit_depth - sig_bits->alpha; + } + + for (c = 0; c < channels; c++) + { + if (shift[c] <= 0) + shift[c] = 0; + else + value = 1; + } + + if (!value) + return; + + switch (row_info->bit_depth) + { + case 2: + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (bp = row, i = 0; i < istop; i++) + { + *bp >>= 1; + *bp++ &= 0x55; + } + break; + } + case 4: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | + (png_byte)((int)0xf >> shift[0])); + + for (i = 0; i < istop; i++) + { + *bp >>= shift[0]; + *bp++ &= mask; + } + break; + } + case 8: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_width * channels; + + for (i = 0; i < istop; i++) + { + *bp++ >>= shift[i%channels]; + } + break; + } + case 16: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_width; + + for (i = 0; i < istop; i++) + { + value = (png_uint_16)((*bp << 8) + *(bp + 1)); + value >>= shift[i%channels]; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* chop rows of bit depth 16 down to 8 */ +void /* PRIVATE */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth == 16) +#else + if (row_info->bit_depth == 16) +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + png_uint_32 istop = row_info->width * row_info->channels; + + for (i = 0; i> 8)) >> 8; + * + * Approximate calculation with shift/add instead of multiply/divide: + * *dp = ((((png_uint_32)(*sp) << 8) | + * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; + * + * What we actually do to avoid extra shifting and conversion: + */ + + *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); +#else + /* Simply discard the low order byte */ + *dp = *sp; +#endif + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } + } + } +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from G to GX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + /* This changes the data from G to XG */ + else + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from GG to GGXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from GG to XXGG */ + else + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from RGB to RGBX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from RGB to XRGB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + /* This changes the data from RRGGBB to XXRRGGBB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } + } /* COLOR_TYPE == RGB */ +} +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb\n"); + if (row_info->bit_depth >= 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels += (png_byte)2; + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * + * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) + +{ + png_uint_32 i; + + png_uint_32 row_width = row_info->width; + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red+gc*green+bc*blue)>>15]; + } + else + *(dp++) = *(sp-1); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>15); + } + else + *(dp++) = *(sp-1); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + } + } + } + } + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1 + [(rc*red + gc*green + bc*blue)>>15]; + *(dp++) = *(sp++); /* alpha */ + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = *(sp++); /* alpha */ + } + } + } + else /* RGBA bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc * red_1 + + gc * green_1 + bc * blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + } + } + row_info->channels -= (png_byte)2; + row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + return rgb_error; +} +#endif + +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette\n"); + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + case 2: + num_palette = 4; + color_inc = 0x55; + break; + case 4: + num_palette = 16; + color_inc = 0x11; + break; + case 8: + num_palette = 256; + color_inc = 1; + break; + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} + +/* This function is currently unused. Do we really need it? */ +#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED) +void /* PRIVATE */ +png_correct_palette(png_structp png_ptr, png_colorp palette, + int num_palette) +{ + png_debug(1, "in png_correct_palette\n"); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND)) + { + png_color back, back_1; + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g; + + g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN || + fabs(g - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = png_ptr->background.red; + back.green = png_ptr->background.green; + back.blue = png_ptr->background.blue; + } + else + { + back.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + g = 1.0 / png_ptr->background_gamma; + + back_1.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back_1.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back_1.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_uint_32 i; + + for (i = 0; i < (png_uint_32)num_palette; i++) + { + if (i < png_ptr->num_trans && png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + png_byte v, w; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + else + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (palette[i].red == (png_byte)png_ptr->trans_values.gray) + { + palette[i] = back; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + } + else +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_color back; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < (int)png_ptr->num_trans; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i].red = back.red; + palette[i].green = back.green; + palette[i].blue = back.blue; + } + else if (png_ptr->trans[i] != 0xff) + { + png_composite(palette[i].red, png_ptr->palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, png_ptr->palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, png_ptr->palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } + else /* assume grayscale palette (what else could it be?) */ + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (i == (png_byte)png_ptr->trans_values.gray) + { + palette[i].red = (png_byte)png_ptr->background.red; + palette[i].green = (png_byte)png_ptr->background.green; + palette[i].blue = (png_byte)png_ptr->background.blue; + } + } + } + } +#endif +} +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_background(png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background +#if defined(PNG_READ_GAMMA_SUPPORTED) + , png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift +#endif + ) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + int shift; + + png_debug(1, "in png_do_background\n"); + if (background != NULL && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || + (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == trans_values->gray) + { + *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 7; + sp++; + } + else + shift--; + } + break; + } + case 2: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x03); + png_byte g = (png_byte)((gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03); + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + break; + } + case 4: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x0f); + png_byte g = (png_byte)((gamma_table[p | + (p << 4)] >> 4) & 0x0f); + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + break; + } + case 8: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + else + { + *sp = gamma_table[*sp]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + } + } + break; + } + case 16: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + } + } + break; + } + } + break; + } + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + } + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->gray; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->gray); + *dp = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_byte a = *(sp + 1); + + if (a == 0xff) + { + *dp = *sp; + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) + { + *dp = (png_byte)background->gray; + } + else + { + png_composite(*dp, *sp, a, background_1->gray); + } +#else + *dp = (png_byte)background->gray; +#endif + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, background_1->gray); + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *dp = (png_byte)((w >> 8) & 0xff); + *(dp + 1) = (png_byte)(w & 0xff); + } +#endif + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 2); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, background_1->gray); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#endif + } + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + *(dp + 1) = gamma_table[*(sp + 1)]; + *(dp + 2) = gamma_table[*(sp + 2)]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->red); + *dp = gamma_from_1[w]; + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, background_1->green); + *(dp + 1) = gamma_from_1[w]; + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, background_1->blue); + *(dp + 2) = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = *sp; + *(dp + 1) = *(sp + 1); + *(dp + 2) = *(sp + 2); + } + else if (a == 0) + { + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_composite(*dp, *sp, a, background->red); + png_composite(*(dp + 1), *(sp + 1), a, + background->green); + png_composite(*(dp + 2), *(sp + 2), a, + background->blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v, w, x; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, background_1->red); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *dp = (png_byte)((x >> 8) & 0xff); + *(dp + 1) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, background_1->green); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *(dp + 2) = (png_byte)((x >> 8) & 0xff); + *(dp + 3) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, background_1->blue); + x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; + *(dp + 4) = (png_byte)((x >> 8) & 0xff); + *(dp + 5) = (png_byte)(x & 0xff); + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 6); + } + else if (a == 0) + { + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, background->red); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, background->green); + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, b, a, background->blue); + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + row_info->channels--; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift) +{ + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + ((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_colorp palette, png_bytep trans, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift += 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + switch (row_info->bit_depth) + { + case 8: + { + if (trans != NULL) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + else + *dp-- = trans[*sp]; + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + break; + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_color_16p trans_value) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (png_uint_16)(gray*0xff); + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + gray = (png_uint_16)(gray*0x55); + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + gray = (png_uint_16)(gray*0x11); + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_value != NULL) + { + if (row_info->bit_depth == 8) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (((png_uint_16)*(sp) | + ((png_uint_16)*(sp - 1) << 8)) == gray) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + { + if (row_info->bit_depth == 8) + { + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == trans_value->red && + *(sp - 1) == trans_value->green && + *(sp - 0) == trans_value->blue) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if ((((png_uint_16)*(sp - 4) | + ((png_uint_16)*(sp - 5) << 8)) == trans_value->red) && + (((png_uint_16)*(sp - 2) | + ((png_uint_16)*(sp - 3) << 8)) == trans_value->green) && + (((png_uint_16)*(sp - 0) | + ((png_uint_16)*(sp - 1) << 8)) == trans_value->blue)) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +void /* PRIVATE */ +png_do_dither(png_row_infop row_info, png_bytep row, + png_bytep palette_lookup, png_bytep dither_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_dither\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && + palette_lookup && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* this looks real messy, but the compiler will reduce + it down to a reasonable formula. For example, with + 5 bits per color, we get: + p = (((r >> 3) & 0x1f) << 10) | + (((g >> 3) & 0x1f) << 5) | + ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + dither_lookup && row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + *sp = dither_lookup[*sp]; + } + } + } +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +#if defined(PNG_READ_GAMMA_SUPPORTED) +static int png_gamma_shift[] = + {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + */ +void /* PRIVATE */ +png_build_gamma_table(png_structp png_ptr) +{ + png_debug(1, "in png_build_gamma_table\n"); + + if (png_ptr->bit_depth <= 8) + { + int i; + double g; + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + + + png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + else + { + double g; + int i, j, shift, num; + int sig_bit; + png_uint_32 ig; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = (int)png_ptr->sig_bit.red; + if ((int)png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + if ((int)png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + { + sig_bit = (int)png_ptr->sig_bit.gray; + } + + if (sig_bit > 0) + shift = 16 - sig_bit; + else + shift = 0; + + if (png_ptr->transformations & PNG_16_TO_8) + { + if (shift < (16 - PNG_MAX_GAMMA_8)) + shift = (16 - PNG_MAX_GAMMA_8); + } + + if (shift > 8) + shift = 8; + if (shift < 0) + shift = 0; + + png_ptr->gamma_shift = (png_byte)shift; + + num = (1 << (8 - shift)); + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) + { + double fin, fout; + png_uint_32 last, max; + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + } + + g = 1.0 / g; + last = 0; + for (i = 0; i < 256; i++) + { + fout = ((double)i + 0.5) / 256.0; + fin = pow(fout, g); + max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); + while (last <= max) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)( + (png_uint_16)i | ((png_uint_16)i << 8)); + last++; + } + } + while (last < ((png_uint_32)num << 8)) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)65535L; + last++; + } + } + else + { + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_table[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p ))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_to_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_from_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } +} +#endif +/* To do: install integer version of png_build_gamma_table here */ +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); + *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0+s1+65536L) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2+s1+65536L) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngrutil.c b/demo/src/lib/libpng/contrib/pngrutil.c new file mode 100644 index 000000000..5b68b21e7 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngrutil.c @@ -0,0 +1,3123 @@ + +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.2.11 June 4, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +#if defined(_WIN32_WCE) +/* strtod() function is not supported on WindowsCE */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +__inline double strtod(const char *nptr, char **endptr) +{ + double result = 0; + int len; + wchar_t *str, *end; + + len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0); + str = (wchar_t *)malloc(len * sizeof(wchar_t)); + if ( NULL != str ) + { + MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len); + result = wcstod(str, &end); + len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL); + *endptr = (char *)nptr + (png_strlen(nptr) - len + 1); + free(str); + } + return result; +} +# endif +#endif + +png_uint_32 PNGAPI +png_get_uint_31(png_structp png_ptr, png_bytep buf) +{ + png_uint_32 i = png_get_uint_32(buf); + if (i > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range."); + return (i); +} +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 PNGAPI +png_get_uint_32(png_bytep buf) +{ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); + + return (i); +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format, and it is + * assumed that the machine format for signed integers is the same. */ +png_int_32 PNGAPI +png_get_int_32(png_bytep buf) +{ + png_int_32 i = ((png_int_32)(*buf) << 24) + + ((png_int_32)(*(buf + 1)) << 16) + + ((png_int_32)(*(buf + 2)) << 8) + + (png_int_32)(*(buf + 3)); + + return (i); +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 PNGAPI +png_get_uint_16(png_bytep buf) +{ + png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) + + (png_uint_16)(*(buf + 1))); + + return (i); +} +#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */ + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) +{ + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + are reading a ancillary or critical chunk, and how the program has set + things up, we may calculate the CRC on the data and print a message. + Returns '1' if there was a CRC error, '0' otherwise. */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + png_size_t i; + png_size_t istop = png_ptr->zbuf_size; + + for (i = (png_size_t)skip; i > istop; i -= istop) + { + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + } + if (i) + { + png_crc_read(png_ptr, png_ptr->zbuf, i); + } + + if (png_crc_error(png_ptr)) + { + if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || + (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + { + png_chunk_warning(png_ptr, "CRC error"); + } + else + { + png_chunk_error(png_ptr, "CRC error"); + } + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + the data it has read thus far. */ +int /* PRIVATE */ +png_crc_error(png_structp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + else + return (0); +} + +#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ + defined(PNG_READ_iCCP_SUPPORTED) +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +png_charp /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + static char msg[] = "Error decoding compressed text"; + png_charp text; + png_size_t text_size; + + if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + int ret = Z_OK; + png_ptr->zstream.next_in = (png_bytep)(chunkdata + prefix_size); + png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + text_size = 0; + text = NULL; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_warning(png_ptr, png_ptr->zstream.msg); + else + png_warning(png_ptr, msg); + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (text == NULL) + { + text_size = prefix_size + png_sizeof(msg) + 1; + text = (png_charp)png_malloc_warn(png_ptr, text_size); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk"); + } + png_memcpy(text, chunkdata, prefix_size); + } + + text[text_size - 1] = 0x00; + + /* Copy what we can of the error message into the text chunk */ + text_size = (png_size_t)(chunklength - (text - chunkdata) - 1); + text_size = png_sizeof(msg) > text_size ? text_size : + png_sizeof(msg); + png_memcpy(text + prefix_size, msg, text_size + 1); + break; + } + if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) + { + if (text == NULL) + { + text_size = prefix_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out; + text = (png_charp)png_malloc_warn(png_ptr, text_size + 1); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk."); + } + png_memcpy(text + prefix_size, png_ptr->zbuf, + text_size - prefix_size); + png_memcpy(text, chunkdata, prefix_size); + *(text + text_size) = 0x00; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(text_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); + if (text == NULL) + { + png_free(png_ptr, tmp); + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk.."); + } + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + (png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = 0x00; + } + if (ret == Z_STREAM_END) + break; + else + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + } + if (ret != Z_STREAM_END) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[52]; + + if (ret == Z_BUF_ERROR) + sprintf(umsg,"Buffer error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else if (ret == Z_DATA_ERROR) + sprintf(umsg,"Data error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else + sprintf(umsg,"Incomplete compressed datastream in %s chunk", + png_ptr->chunk_name); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, + "Incomplete compressed datastream in chunk other than IDAT"); +#endif + text_size=prefix_size; + if (text == NULL) + { + text = (png_charp)png_malloc_warn(png_ptr, text_size+1); + if (text == NULL) + { + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory for text."); + } + png_memcpy(text, chunkdata, prefix_size); + } + *(text + text_size) = 0x00; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + png_free(png_ptr, chunkdata); + chunkdata = text; + *newlength=text_size; + } + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[50]; + + sprintf(umsg, "Unknown zTXt compression type %d", comp_type); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif + + *(chunkdata + prefix_size) = 0x00; + *newlength=prefix_size; + } + + return chunkdata; +} +#endif + +/* read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR\n"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_error(png_ptr, "Out of place IHDR"); + + /* check the length */ + if (length != 13) + png_error(png_ptr, "Invalid IHDR chunk"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* find number of channels */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); + png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth); + png_debug1(3,"channels = %d\n", png_ptr->channels); + png_debug1(3,"rowbytes = %lu\n", png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifndef PNG_NO_POINTER_INDEXING + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before PLTE"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid PLTE after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_error(png_ptr, "Duplicate PLTE chunk"); + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring PLTE chunk in grayscale PNG"); + png_crc_finish(png_ptr, length); + return; + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_warning(png_ptr, "Invalid palette chunk"); + png_crc_finish(png_ptr, length); + return; + } + else + { + png_error(png_ptr, "Invalid palette chunk"); + } + } + + num = (int)length / 3; + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually NEED the PLTE chunk (ie for a paletted image), we do + whatever the normal CRC configuration tells us. However, if we + have an RGB image, the PLTE can be considered ancillary, so + we will act as though it is. */ +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + we have two options: an error abort, or a warning and we + ignore the data in this chunk (which should be OK, since + it's considered ancillary for a RGB or RGBA image). */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_error(png_ptr, "CRC error"); + } + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + png_set_PLTE(png_ptr, info_ptr, palette, num); + +#if defined(PNG_READ_tRNS_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + if (png_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); + png_ptr->num_trans = (png_uint_16)num; + } + if (info_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); + info_ptr->num_trans = (png_uint_16)num; + } + } + } +#endif + +} + +void /* PRIVATE */ +png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + { + png_error(png_ptr, "No image in file"); + } + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + if (length != 0) + { + png_warning(png_ptr, "Incorrect IEND chunk length"); + } + png_crc_finish(png_ptr, length); + + if (&info_ptr == NULL) /* quiet compiler warnings about unused info_ptr */ + return; +} + +#if defined(PNG_READ_gAMA_SUPPORTED) +void /* PRIVATE */ +png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before gAMA"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid gAMA after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place gAMA chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate gAMA chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 4) + { + png_warning(png_ptr, "Incorrect gAMA chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = (png_fixed_point)png_get_uint_32(buf); + /* check for zero gamma */ + if (igamma == 0) + { + png_warning(png_ptr, + "Ignoring gAMA chunk with gamma=0"); + return; + } + +#if defined(PNG_READ_sRGB_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "gamma = (%d/100000)\n", (int)igamma); +#endif + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float)igamma / (float)100000.0; +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->gamma = file_gamma; +# endif + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, igamma); +#endif +} +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +void /* PRIVATE */ +png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT\n"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sBIT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sBIT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + { + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sBIT chunk"); + } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_warning(png_ptr, "Duplicate sBIT chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + else + truelen = (png_size_t)png_ptr->channels; + + if (length != truelen || length > 4) + { + png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +void /* PRIVATE */ +png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[4]; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue; + + png_uint_32 uint_x, uint_y; + + png_debug(1, "in png_handle_cHRM\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before cHRM"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid cHRM after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Missing PLTE before cHRM"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate cHRM chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 32) + { + png_warning(png_ptr, "Incorrect cHRM chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x > 80000L || uint_y > 80000L || + uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM white point"); + png_crc_finish(png_ptr, 24); + return; + } + int_x_white = (png_fixed_point)uint_x; + int_y_white = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM red point"); + png_crc_finish(png_ptr, 16); + return; + } + int_x_red = (png_fixed_point)uint_x; + int_y_red = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM green point"); + png_crc_finish(png_ptr, 8); + return; + } + int_x_green = (png_fixed_point)uint_x; + int_y_green = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM blue point"); + png_crc_finish(png_ptr, 0); + return; + } + int_x_blue = (png_fixed_point)uint_x; + int_y_blue = (png_fixed_point)uint_y; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float)int_x_white / (float)100000.0; + white_y = (float)int_y_white / (float)100000.0; + red_x = (float)int_x_red / (float)100000.0; + red_y = (float)int_y_red / (float)100000.0; + green_x = (float)int_x_green / (float)100000.0; + green_y = (float)int_y_green / (float)100000.0; + blue_x = (float)int_x_blue / (float)100000.0; + blue_y = (float)int_y_blue / (float)100000.0; +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) + if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) + { + if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n", + white_x, white_y, red_x, red_y); + fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n", + green_x, green_y, blue_x, blue_y); +#else + fprintf(stderr,"wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", + int_x_white, int_y_white, int_x_red, int_y_red); + fprintf(stderr,"gx=%ld, gy=%ld, bx=%ld, by=%ld\n", + int_x_green, int_y_green, int_x_blue, int_y_blue); +#endif +#endif /* PNG_NO_CONSOLE_IO */ + } + png_crc_finish(png_ptr, 0); + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue); +#endif + if (png_crc_finish(png_ptr, 0)) + return; +} +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) +void /* PRIVATE */ +png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + int intent; + png_byte buf[1]; + + png_debug(1, "in png_handle_sRGB\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sRGB"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sRGB after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sRGB chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + { + png_warning(png_ptr, "Duplicate sRGB chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 1) + { + png_warning(png_ptr, "Incorrect sRGB chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 1); + if (png_crc_finish(png_ptr, 0)) + return; + + intent = buf[0]; + /* check for bad intent */ + if (intent >= PNG_sRGB_INTENT_LAST) + { + png_warning(png_ptr, "Unknown sRGB intent"); + return; + } + +#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) + { + png_fixed_point igamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + igamma=info_ptr->int_gamma; +#else +# ifdef PNG_FLOATING_POINT_SUPPORTED + igamma=(png_fixed_point)(info_ptr->gamma * 100000.); +# endif +#endif + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_FIXED_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=(%d/100000)\n",(int)png_ptr->int_gamma); +# else +# ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=%f\n",png_ptr->gamma); +# endif +# endif +#endif + } + } +#endif /* PNG_READ_gAMA_SUPPORTED */ + +#ifdef PNG_READ_cHRM_SUPPORTED +#ifdef PNG_FIXED_POINT_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_READ_cHRM_SUPPORTED */ + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#if defined(PNG_READ_iCCP_SUPPORTED) +void /* PRIVATE */ +png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_charp chunkdata; + png_byte compression_type; + png_bytep pC; + png_charp profile; + png_uint_32 skip = 0; + png_uint_32 profile_size, profile_length; + png_size_t slength, prefix_length, data_length; + + png_debug(1, "in png_handle_iCCP\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iCCP"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid iCCP after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place iCCP chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) + { + png_warning(png_ptr, "Duplicate iCCP chunk"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iCCP chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (profile = chunkdata; *profile; profile++) + /* empty loop to find end of name */ ; + + ++profile; + + /* there should be at least one zero (the compression type byte) + following the separator, and we should be on it */ + if ( profile >= chunkdata + slength) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Malformed iCCP chunk"); + return; + } + + /* compression_type should always be zero */ + compression_type = *profile++; + if (compression_type) + { + png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); + compression_type=0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 + wrote nonzero) */ + } + + prefix_length = profile - chunkdata; + chunkdata = png_decompress_chunk(png_ptr, compression_type, chunkdata, + slength, prefix_length, &data_length); + + profile_length = data_length - prefix_length; + + if ( prefix_length > data_length || profile_length < 4) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Profile size field missing from iCCP chunk"); + return; + } + + /* Check the profile_size recorded in the first 32 bits of the ICC profile */ + pC = (png_bytep)(chunkdata+prefix_length); + profile_size = ((*(pC ))<<24) | + ((*(pC+1))<<16) | + ((*(pC+2))<< 8) | + ((*(pC+3)) ); + + if(profile_size < profile_length) + profile_length = profile_size; + + if(profile_size > profile_length) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Ignoring truncated iCCP profile."); + return; + } + + png_set_iCCP(png_ptr, info_ptr, chunkdata, compression_type, + chunkdata + prefix_length, profile_length); + png_free(png_ptr, chunkdata); +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_sPLT_SUPPORTED) +void /* PRIVATE */ +png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep chunkdata; + png_bytep entry_start; + png_sPLT_t new_palette; +#ifdef PNG_NO_POINTER_INDEXING + png_sPLT_entryp pp; +#endif + int data_length, entry_size, i; + png_uint_32 skip = 0; + png_size_t slength; + + png_debug(1, "in png_handle_sPLT\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sPLT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sPLT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "sPLT chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_bytep)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (entry_start = chunkdata; *entry_start; entry_start++) + /* empty loop to find end of name */ ; + ++entry_start; + + /* a sample depth should follow the separator, and we should be on it */ + if (entry_start > chunkdata + slength) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + data_length = (slength - (entry_start - chunkdata)); + + /* integrity-check the data length */ + if (data_length % entry_size) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + new_palette.nentries = (png_int_32) ( data_length / entry_size); + if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX / + png_sizeof(png_sPLT_entry))) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0; i < new_palette.nentries; i++) + { + png_sPLT_entryp pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* discard all chunk data except the name and stash that */ + new_palette.name = (png_charp)chunkdata; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, chunkdata); + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_tRNS_SUPPORTED) +void /* PRIVATE */ +png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_tRNS\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tRNS"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid tRNS after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_warning(png_ptr, "Duplicate tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_values.gray = png_get_uint_16(buf); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, buf, (png_size_t)length); + png_ptr->num_trans = 1; + png_ptr->trans_values.red = png_get_uint_16(buf); + png_ptr->trans_values.green = png_get_uint_16(buf + 2); + png_ptr->trans_values.blue = png_get_uint_16(buf + 4); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* Should be an error, but we can cope with it. */ + png_warning(png_ptr, "Missing PLTE before tRNS"); + } + if (length > (png_uint_32)png_ptr->num_palette || + length > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + if (length == 0) + { + png_warning(png_ptr, "Zero length tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, readbuf, (png_size_t)length); + png_ptr->num_trans = (png_uint_16)length; + } + else + { + png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_values)); +} +#endif + +#if defined(PNG_READ_bKGD_SUPPORTED) +void /* PRIVATE */ +png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[6]; + + png_debug(1, "in png_handle_bKGD\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before bKGD"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid bKGD after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before bKGD"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_warning(png_ptr, "Duplicate bKGD chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + else + truelen = 2; + + if (length != truelen) + { + png_warning(png_ptr, "Incorrect bKGD chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.index = buf[0]; + if(info_ptr->num_palette) + { + if(buf[0] > info_ptr->num_palette) + { + png_warning(png_ptr, "Incorrect bKGD chunk index value"); + return; + } + png_ptr->background.red = + (png_uint_16)png_ptr->palette[buf[0]].red; + png_ptr->background.green = + (png_uint_16)png_ptr->palette[buf[0]].green; + png_ptr->background.blue = + (png_uint_16)png_ptr->palette[buf[0]].blue; + } + } + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = + png_ptr->background.gray = png_get_uint_16(buf); + } + else + { + png_ptr->background.red = png_get_uint_16(buf); + png_ptr->background.green = png_get_uint_16(buf + 2); + png_ptr->background.blue = png_get_uint_16(buf + 4); + } + + png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); +} +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +void /* PRIVATE */ +png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before hIST"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid hIST after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before hIST"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_warning(png_ptr, "Duplicate hIST chunk"); + png_crc_finish(png_ptr, length); + return; + } + + num = length / 2 ; + if (num != (unsigned int) png_ptr->num_palette || num > + (unsigned int) PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect hIST chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +void /* PRIVATE */ +png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pHYs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pHYs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_warning(png_ptr, "Duplicate pHYs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect pHYs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +void /* PRIVATE */ +png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before oFFs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid oFFs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_warning(png_ptr, "Duplicate oFFs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect oFFs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +/* read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp purpose; + png_int_32 X0, X1; + png_byte type, nparams; + png_charp buf, units, endptr; + png_charpp params; + png_size_t slength; + int i; + + png_debug(1, "in png_handle_pCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_warning(png_ptr, "Duplicate pCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)\n", + length + 1); + purpose = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (purpose == NULL) + { + png_warning(png_ptr, "No memory for pCAL purpose."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)purpose, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, purpose); + return; + } + + purpose[slength] = 0x00; /* null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string\n"); + for (buf = purpose; *buf; buf++) + /* empty loop */ ; + + endptr = purpose + slength; + + /* We need to have at least 12 bytes after the purpose string + in order to get the parameter information. */ + if (endptr <= buf + 12) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters\n"); + /* Check that we have the right number of parameters for known + equation types. */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_free(png_ptr, purpose); + return; + } + else if (type >= PNG_EQUATION_LAST) + { + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array\n"); + params = (png_charpp)png_malloc_warn(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_charp))) ; + if (params == NULL) + { + png_free(png_ptr, purpose); + png_warning(png_ptr, "No memory for pCAL params."); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < (int)nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d\n", i); + for (params[i] = buf; *buf != 0x00 && buf <= endptr; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + png_free(png_ptr, params); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams, + units, params); + + png_free(png_ptr, purpose); + png_free(png_ptr, params); +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +/* read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp buffer, ep; +#ifdef PNG_FLOATING_POINT_SUPPORTED + double width, height; + png_charp vp; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp swidth, sheight; +#endif +#endif + png_size_t slength; + + png_debug(1, "in png_handle_sCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)\n", + length + 1); + buffer = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (buffer == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)buffer, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, buffer); + return; + } + + buffer[slength] = 0x00; /* null terminate the last string */ + + ep = buffer + 1; /* skip unit byte */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + width = strtod(ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed width string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); + return; + } + png_memcpy(swidth, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + for (ep = buffer; *ep; ep++) + /* empty loop */ ; + ep++; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + height = strtod(ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed height string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); + return; + } + png_memcpy(sheight, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + if (buffer + slength < ep +#ifdef PNG_FLOATING_POINT_SUPPORTED + || width <= 0. || height <= 0. +#endif + ) + { + png_warning(png_ptr, "Invalid sCAL data"); + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif + return; + } + + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_sCAL(png_ptr, info_ptr, buffer[0], width, height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_sCAL_s(png_ptr, info_ptr, buffer[0], swidth, sheight); +#endif +#endif + + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif +} +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +void /* PRIVATE */ +png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Out of place tIME chunk"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_warning(png_ptr, "Duplicate tIME chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_warning(png_ptr, "Incorrect tIME chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + png_size_t slength; + int ret; + + png_debug(1, "in png_handle_tEXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tEXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + key = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (key == NULL) + { + png_warning(png_ptr, "No memory to process text chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)key, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, key); + return; + } + + key[slength] = 0x00; + + for (text = key; *text; text++) + /* empty loop to find end of key */ ; + + if (text != key + slength) + text++; + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process text chunk."); + png_free(png_ptr, key); + return; + } + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = text; + text_ptr->text_length = png_strlen(text); + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to process text chunk."); +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp text; + int comp_type; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_zTXt\n"); + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before zTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"zTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr,"Out of memory processing zTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (text = chunkdata; *text; text++) + /* empty loop */ ; + + /* zTXt must have some text after the chunkdataword */ + if (text == chunkdata + slength) + { + comp_type = PNG_TEXT_COMPRESSION_NONE; + png_warning(png_ptr, "Zero length zTXt chunk"); + } + else + { + comp_type = *(++text); + if (comp_type != PNG_TEXT_COMPRESSION_zTXt) + { + png_warning(png_ptr, "Unknown compression type in zTXt chunk"); + comp_type = PNG_TEXT_COMPRESSION_zTXt; + } + text++; /* skip the compression_method byte */ + } + prefix_len = text - chunkdata; + + chunkdata = (png_charp)png_decompress_chunk(png_ptr, comp_type, chunkdata, + (png_size_t)length, prefix_len, &data_len); + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process zTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = comp_type; + text_ptr->key = chunkdata; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = chunkdata + prefix_len; + text_ptr->text_length = data_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store zTXt chunk."); +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp key, lang, text, lang_key; + int comp_flag; + int comp_type = 0; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_iTXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"iTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process iTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (lang = chunkdata; *lang; lang++) + /* empty loop */ ; + lang++; /* skip NUL separator */ + + /* iTXt must have a language tag (possibly empty), two compression bytes, + translated keyword (possibly empty), and possibly some text after the + keyword */ + + if (lang >= chunkdata + slength) + { + comp_flag = PNG_TEXT_COMPRESSION_NONE; + png_warning(png_ptr, "Zero length iTXt chunk"); + } + else + { + comp_flag = *lang++; + comp_type = *lang++; + } + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + for (text = lang_key; *text; text++) + /* empty loop */ ; + text++; /* skip NUL separator */ + + prefix_len = text - chunkdata; + + key=chunkdata; + if (comp_flag) + chunkdata = png_decompress_chunk(png_ptr, comp_type, chunkdata, + (size_t)length, prefix_len, &data_len); + else + data_len=png_strlen(chunkdata + prefix_len); + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process iTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = (int)comp_flag + 1; + text_ptr->lang_key = chunkdata+(lang_key-key); + text_ptr->lang = chunkdata+(lang-key); + text_ptr->itxt_length = data_len; + text_ptr->text_length = 0; + text_ptr->key = chunkdata; + text_ptr->text = chunkdata + prefix_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store iTXt chunk."); +} +#endif + +/* This function is called when we haven't found a handler for a + chunk. If there isn't a problem with the chunk itself (ie bad + chunk name, CRC, or a critical chunk), the chunk is silently ignored + -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which + case it will be saved away to be written out later. */ +void /* PRIVATE */ +png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_unknown\n"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* not an IDAT */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) || + (png_ptr->read_user_chunk_fn != NULL)) + { + png_unknown_chunk chunk; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_strcpy((png_charp)chunk.name, (png_charp)png_ptr->chunk_name); + chunk.data = (png_bytep)png_malloc(png_ptr, length); + chunk.size = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunk.data, length); +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + if ((*(png_ptr->read_user_chunk_fn)) (png_ptr, &chunk) <= 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + { + png_free(png_ptr, chunk.data); + png_chunk_error(png_ptr, "unknown critical chunk"); + } + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + } + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + png_free(png_ptr, chunk.data); + } + else +#endif + skip = length; + + png_crc_finish(png_ptr, skip); + +#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if (&info_ptr == NULL) /* quiet compiler warnings about unused info_ptr */ + return; +#endif +} + +/* This function is called to verify that a chunk name is valid. + This function can't have the "critical chunk check" incorporated + into it, since in the future we will need to be able to call user + functions to handle unknown critical chunks after we check that + the chunk name itself is valid. */ + +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + +void /* PRIVATE */ +png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +{ + png_debug(1, "in png_check_chunk_name\n"); + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { + png_chunk_error(png_ptr, "invalid chunk type"); + } +} + +/* Combines the row recently read in with the existing pixels in the + row. This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined, + a zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ +#ifndef PNG_HAVE_ASSEMBLER_COMBINE_ROW +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1,"in png_combine_row\n"); + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + } + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_inc, s_start, s_end; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x01; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 2: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x03; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 4: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + default: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + png_byte m = 0x80; + + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + png_memcpy(dp, sp, pixel_bytes); + } + + sp += pixel_bytes; + dp += pixel_bytes; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + } + } +} +#endif /* !PNG_HAVE_ASSEMBLER_COMBINE_ROW */ + +#ifdef PNG_READ_INTERLACING_SUPPORTED +#ifndef PNG_HAVE_ASSEMBLER_READ_INTERLACE /* else in pngvcrd.c, pnggccrd.c */ +/* OLD pre-1.0.9 interface: +void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations) + */ +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* offset to next interlace block */ + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_do_read_interlace (stock C version)\n"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0xf); + int j; + + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + png_memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sp -= pixel_bytes; + } + break; + } + } + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + } +#if !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (&transformations == NULL) /* silence compiler warning */ + return; +#endif +} +#endif /* !PNG_HAVE_ASSEMBLER_READ_INTERLACE */ +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +#ifndef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, + png_bytep prev_row, int filter) +{ + png_debug(1, "in png_read_filter_row\n"); + png_debug2(2,"row = %lu, filter = %d\n", png_ptr->row_number, filter); + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + case PNG_FILTER_VALUE_SUB: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_UP: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_AVG: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *lp++) / 2 ) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_PAETH: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + break; + } + default: + png_warning(png_ptr, "Ignoring bad adaptive filter type"); + *row=0; + break; + } +} +#endif /* !PNG_HAVE_ASSEMBLER_READ_FILTER_ROW */ + +void /* PRIVATE */ +png_read_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + const int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + const int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + const int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_debug(1, "in png_read_finish_row\n"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (!(png_ptr->num_rows)) + continue; + } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; + } while (png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } + + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + char extra; + int ret; + + png_ptr->zstream.next_out = (Byte *)&extra; + png_ptr->zstream.avail_out = (uInt)1; + for(;;) + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_warning(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression Error"); + + if (!(png_ptr->zstream.avail_out)) + { + png_warning(png_ptr, "Extra compressed data."); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + + } + png_ptr->zstream.avail_out = 0; + } + + if (png_ptr->idat_size || png_ptr->zstream.avail_in) + png_warning(png_ptr, "Extra compression data"); + + inflateReset(&png_ptr->zstream); + + png_ptr->mode |= PNG_AFTER_IDAT; +} + +void /* PRIVATE */ +png_read_start_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + const int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + const int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + const int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int max_pixel_depth; + png_uint_32 row_bytes; + + png_debug(1, "in png_read_start_row\n"); + png_ptr->zstream.avail_in = 0; + png_init_read_transformations(png_ptr); + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + row_bytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->iwidth) + 1; + + png_ptr->irowbytes = (png_size_t)row_bytes; + if((png_uint_32)png_ptr->irowbytes != row_bytes) + png_error(png_ptr, "Rowbytes overflow in png_read_start_row"); + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + png_ptr->irowbytes = png_ptr->rowbytes + 1; + } + max_pixel_depth = png_ptr->pixel_depth; + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_pixel_depth = 32; + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + else + max_pixel_depth = 32; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + } +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#if defined(PNG_READ_EXPAND_SUPPORTED) + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#if defined(PNG_READ_FILLER_SUPPORTED) + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth=png_ptr->user_transform_depth* + png_ptr->user_transform_channels; + if(user_pixel_depth > max_pixel_depth) + max_pixel_depth=user_pixel_depth; + } +#endif + + /* align the width on the next larger 8 pixels. Mainly used + for interlacing */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* calculate the maximum bytes needed, adding a byte and a pixel + for safety's sake */ + row_bytes = PNG_ROWBYTES(max_pixel_depth,row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64); + png_ptr->row_buf = png_ptr->big_row_buf+32; +#if defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD) + png_ptr->row_buf_size = row_bytes; +#endif + +#ifdef PNG_MAX_MALLOC_64K + if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + if ((png_uint_32)png_ptr->rowbytes > PNG_SIZE_MAX - 1) + png_error(png_ptr, "Row has too many bytes to allocate in memory."); + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( + png_ptr->rowbytes + 1)); + + png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %lu,\n", png_ptr->width); + png_debug1(3, "height = %lu,\n", png_ptr->height); + png_debug1(3, "iwidth = %lu,\n", png_ptr->iwidth); + png_debug1(3, "num_rows = %lu\n", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,\n", png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu,\n", png_ptr->irowbytes); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngset.c b/demo/src/lib/libpng/contrib/pngset.c new file mode 100644 index 000000000..9e499c9bc --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngset.c @@ -0,0 +1,1265 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_bKGD_SUPPORTED) +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function\n", "bKGD"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (white_x < 0.0 || white_y < 0.0 || + red_x < 0.0 || red_y < 0.0 || + green_x < 0.0 || green_y < 0.0 || + blue_x < 0.0 || blue_y < 0.0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } + if (white_x > 21474.83 || white_y > 21474.83 || + red_x > 21474.83 || red_y > 21474.83 || + green_x > 21474.83 || green_y > 21474.83 || + blue_x > 21474.83 || blue_y > 21474.83) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (white_x < 0 || white_y < 0 || + red_x < 0 || red_y < 0 || + green_x < 0 || green_y < 0 || + blue_x < 0 || blue_y < 0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + if (white_x > (double) PNG_UINT_31_MAX || + white_y > (double) PNG_UINT_31_MAX || + red_x > (double) PNG_UINT_31_MAX || + red_y > (double) PNG_UINT_31_MAX || + green_x > (double) PNG_UINT_31_MAX || + green_y > (double) PNG_UINT_31_MAX || + blue_x > (double) PNG_UINT_31_MAX || + blue_y > (double) PNG_UINT_31_MAX) +#else + if (white_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + white_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || + red_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + red_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || + green_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + green_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || + blue_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + blue_y > (png_fixed_point) PNG_UINT_31_MAX/100000L) +#endif + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double gamma; + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=21474.83; + } + else + gamma=file_gamma; + info_ptr->gamma = (float)gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point gamma; + + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + gamma=0; + } + else + gamma=int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function\n", "hIST"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (info_ptr->num_palette <= 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); +#endif + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in version + 1.2.1 */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof (png_uint_16))); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data."); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_HIST; +#else + png_ptr->flags |= PNG_FLAG_FREE_HIST; +#endif +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function\n", "IHDR"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* check for width and height valid values */ + if (width == 0 || height == 0) + png_error(png_ptr, "Image width or height is zero in IHDR"); +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || height > png_ptr->user_height_max) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#else + if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#endif + if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image size in IHDR"); + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth in IHDR"); + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + png_error(png_ptr, "Invalid color type in IHDR"); + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + png_error(png_ptr, "Invalid color type/bit depth combination in IHDR"); + + if (interlace_type >= PNG_INTERLACE_LAST) + png_error(png_ptr, "Unknown interlace method in IHDR"); + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_error(png_ptr, "Unknown compression method in IHDR"); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted) + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); + if(filter_type != PNG_FILTER_TYPE_BASE) + { + if(!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + png_error(png_ptr, "Unknown filter method in IHDR"); + if(png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) + png_warning(png_ptr, "Invalid filter method in IHDR"); + } +#else + if(filter_type != PNG_FILTER_TYPE_BASE) + png_error(png_ptr, "Unknown filter method in IHDR"); +#endif + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type =(png_byte) color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* check for potential overflow */ + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = (png_size_t)0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,width); +} + +#if defined(PNG_oFFs_SUPPORTED) +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "oFFs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_uint_32 length; + int i; + + png_debug1(1, "in %s storage function\n", "pCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)\n", length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose."); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + + png_debug(3, "storing X0, X1, type, and nparams in info\n"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)\n", length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units."); + return; + } + png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params."); + return; + } + + info_ptr->pcal_params[nparams] = NULL; + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)\n", i, length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter."); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + } + + info_ptr->valid |= PNG_INFO_pCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PCAL; +#endif +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_uint_32 length; + + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + png_warning(png_ptr, "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + + info_ptr->valid |= PNG_INFO_sCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SCAL; +#endif +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "pHYs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function\n", "PLTE"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + /* + * It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); +#endif + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + of num_palette entries, + in case of an invalid PNG file that has too-large sample values. */ + png_ptr->palette = (png_colorp)png_malloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); + png_memset(png_ptr->palette, 0, PNG_MAX_PALETTE_LENGTH * + png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof (png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PLTE; +#else + png_ptr->flags |= PNG_FLAG_FREE_PLTE; +#endif + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#if defined(PNG_sBIT_SUPPORTED) +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function\n", "sBIT"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof (png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#if defined(PNG_sRGB_SUPPORTED) +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function\n", "sRGB"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif +#endif + png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FIXED_POINT_SUPPORTED + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, + int_blue_x, int_blue_y); +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; + + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#endif +} +#endif + + +#if defined(PNG_iCCP_SUPPORTED) +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + + png_debug1(1, "in %s storage function\n", "iCCP"); + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, png_strlen(name)+1); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + return; + } + png_strcpy(new_iccp_name, name); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, "Insufficient memory to process iCCP profile."); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types */ + info_ptr->iccp_compression = (png_byte)compression_type; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ICCP; +#endif + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + if (info_ptr->text != NULL) + { + png_textp old_text; + int old_max; + + old_max = info_ptr->max_text; + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + { + png_free(png_ptr, old_text); + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + return(1); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TEXT; +#endif + } + png_debug1(3, "allocated %d entries for info_ptr->text\n", + info_ptr->max_text); + } + for (i = 0; i < num_text; i++) + { + png_size_t text_length,key_len; + png_size_t lang_len,lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if(text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + else +#ifdef PNG_iTXt_SUPPORTED + { + /* set iTXt data */ + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else + { + png_warning(png_ptr, "iTXt chunk not supported."); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if(text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text\n", + (png_uint_32)(key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key, + (png_size_t)(key_len)); + *(textp->key+key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang=textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang+lang_len) = '\0'; + textp->lang_key=textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key+lang_key_len) = '\0'; + textp->text=textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text=textp->key + key_len + 1; + } + if(text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text+text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if(textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->text[info_ptr->num_text]= *textp; + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text); + } + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function\n", "tIME"); + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof (png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans, int num_trans, png_color_16p trans_values) +{ + png_debug1(1, "in %s storage function\n", "tRNS"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans != NULL) + { + /* + * It may not actually be necessary to set png_ptr->trans here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); +#endif + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, + (png_uint_32)PNG_MAX_PALETTE_LENGTH); + if (num_trans <= PNG_MAX_PALETTE_LENGTH) + png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TRNS; +#else + png_ptr->flags |= PNG_FLAG_FREE_TRNS; +#endif + } + + if (trans_values != NULL) + { + png_memcpy(&(info_ptr->trans_values), trans_values, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + info_ptr->num_trans = (png_uint_16)num_trans; + info_ptr->valid |= PNG_INFO_tRNS; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +{ + png_sPLT_tp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes."); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + + to->name = (png_charp)png_malloc(png_ptr, + png_strlen(from->name) + 1); + /* TODO: use png_malloc_warn */ + png_strcpy(to->name, from->name); + to->entries = (png_sPLT_entryp)png_malloc(png_ptr, + from->nentries * png_sizeof(png_sPLT_t)); + /* TODO: use png_malloc_warn */ + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_t)); + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SPLT; +#endif +} +#endif /* PNG_sPLT_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk)); + if (np == NULL) + { + png_warning(png_ptr, "Out of memory while processing unknown chunk."); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks=NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_strncpy((png_charp)to->name, (png_charp)from->name, 5); + to->data = (png_bytep)png_malloc_warn(png_ptr, from->size); + if (to->data == NULL) + { + png_warning(png_ptr, "Out of memory processing unknown chunk."); + } + else + { + png_memcpy(to->data, from->data, from->size); + to->size = from->size; + + /* note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_UNKN; +#endif +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if(png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +void PNGAPI +png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +{ + /* This function is deprecated in favor of png_permit_mng_features() + and will be removed from libpng-1.3.0 */ + png_debug(1, "in png_permit_empty_plte, DEPRECATED.\n"); + if (png_ptr == NULL) + return; + png_ptr->mng_features_permitted = (png_byte) + ((png_ptr->mng_features_permitted & (~(PNG_FLAG_MNG_EMPTY_PLTE))) | + ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); +} +#endif +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features\n"); + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (png_ptr == NULL) + return; + if (num_chunks == 0) + { + if(keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if(keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks=png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_uint_32)(5*(num_chunks+old_num_chunks))); + if(png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list+5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p=new_list+5*old_num_chunks+4, i=0; inum_chunk_list=old_num_chunks+num_chunks; + png_ptr->chunk_list=new_list; +#ifdef PNG_FREE_ME_SUPPORTED + png_ptr->free_me |= PNG_FREE_LIST; +#endif +} +#endif + +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn\n"); + if (png_ptr == NULL) + return; + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function\n", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if(row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size) +{ + if (png_ptr == NULL) + return; + if(png_ptr->zbuf) + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = (png_size_t)size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} +#endif + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~(mask); +} + + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* this function was added to libpng 1.2.0 and should always exist by default */ +void PNGAPI +png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags) +{ + png_uint_32 settable_asm_flags; + png_uint_32 settable_mmx_flags; + + if (png_ptr == NULL) + return; + + settable_mmx_flags = +#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | +#endif +#ifdef PNG_HAVE_ASSEMBLER_READ_INTERLACE + PNG_ASM_FLAG_MMX_READ_INTERLACE | +#endif +#ifdef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH | +#endif + 0; + + /* could be some non-MMX ones in the future, but not currently: */ + settable_asm_flags = settable_mmx_flags; + + if (!(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED) || + !(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU)) + { + /* clear all MMX flags if MMX isn't supported */ + settable_asm_flags &= ~settable_mmx_flags; + png_ptr->asm_flags &= ~settable_mmx_flags; + } + + /* we're replacing the settable bits with those passed in by the user, + * so first zero them out of the master copy, then logical-OR in the + * allowed subset that was requested */ + + png_ptr->asm_flags &= ~settable_asm_flags; /* zero them */ + png_ptr->asm_flags |= (asm_flags & settable_asm_flags); /* set them */ +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* this function was added to libpng 1.2.0 */ +void PNGAPI +png_set_mmx_thresholds (png_structp png_ptr, + png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold) +{ + if (png_ptr == NULL) + return; + png_ptr->mmx_bitdepth_threshold = mmx_bitdepth_threshold; + png_ptr->mmx_rowbytes_threshold = mmx_rowbytes_threshold; +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* this function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* ?PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngtrans.c b/demo/src/lib/libpng/contrib/pngtrans.c new file mode 100644 index 000000000..9c4d67cd0 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngtrans.c @@ -0,0 +1,652 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr\n"); + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap\n"); + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing\n"); + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap\n"); + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift\n"); + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling\n"); + if (png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler\n"); + png_ptr->transformations |= PNG_FILLER; + png_ptr->filler = (png_byte)filler; + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#if !defined(PNG_1_0_X) +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha\n"); + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha\n"); + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha\n"); + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono\n"); + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert\n"); + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = (png_bytep)onebppswaptable; + else if (row_info->bit_depth == 2) + table = (png_bytep)twobppswaptable; + else if (row_info->bit_depth == 4) + table = (png_bytep)fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info\n"); +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if(user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + return ((png_voidp)png_ptr->user_transform_ptr); +#else + if(png_ptr) + return (NULL); + return (NULL); +#endif +} +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngwio.c b/demo/src/lib/libpng/contrib/pngwio.c new file mode 100644 index 000000000..a9c1dc53d --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngwio.c @@ -0,0 +1,228 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.2.3 - May 21, 2002 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2002 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + writes to a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered writes. This should never be asked + to write more than 64K on a 16 bit machine. */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + +#if defined(_WIN32_WCE) + if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); +#endif + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(near_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) + err = 0; +#else + err = fwrite(buf, 1, written, io_ptr); +#endif + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + to disk). After png_flush is called, there should be no data pending + writing in any buffers. */ +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#if !defined(PNG_NO_STDIO) +void PNGAPI +png_default_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif +#endif + +/* This function allows the application to supply new output functions for + libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png output data structure + io_ptr - pointer to user supplied structure containing info about + the output functions. May be NULL. + write_data_fn - pointer to a new output function that takes as its + arguments a pointer to a png_struct, a pointer to + data to be written, and a 32-bit unsigned int that is + the number of bytes to be written. The new write + function should call png_error(png_ptr, "Error msg") + to exit and output any fatal error messages. + flush_data_fn - pointer to a new flush function that takes as its + arguments a pointer to a png_struct. After a call to + the flush function, there should be no data in any buffers + or pending transmission. If the output method doesn't do + any buffering of ouput, a function prototype must still be + supplied although it doesn't have to do anything. If + PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + time, output_flush_fn will be ignored, although it must be + supplied for compatibility. */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL."); + } +} + +#if defined(USE_FAR_KEYWORD) +#if defined(_MSC_VER) +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(far_ptr != ptr) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngwrite.c b/demo/src/lib/libpng/contrib/pngwrite.c new file mode 100644 index 000000000..95984f6e5 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngwrite.c @@ -0,0 +1,1513 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* get internal access to png.h */ +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + png_write_sig(png_ptr); /* write PNG signature */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted)) + { + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted=0; + } +#endif + /* write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + info_ptr->interlace_type); +#else + 0); +#endif + /* the rest of these check to see if the valid field has the appropriate + flag set, and if it does, writes the chunk. */ +#if defined(PNG_WRITE_gAMA_SUPPORTED) + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_sRGB_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#if defined(PNG_WRITE_iCCP_SUPPORTED) + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#if defined(PNG_WRITE_sBIT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_cHRM_SUPPORTED) + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info\n"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#if defined(PNG_WRITE_tRNS_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tRNS) + { +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j=0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#if defined(PNG_WRITE_bKGD_SUPPORTED) + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_hIST_SUPPORTED) + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#if defined(PNG_WRITE_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif +#if defined(PNG_WRITE_sCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sCAL) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#else + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written."); +#endif +#endif +#endif +#if defined(PNG_WRITE_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif +#if defined(PNG_WRITE_sPLT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end\n"); + if (png_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* see if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#if defined(PNG_WRITE_TEXT_SUPPORTED) + int i; /* local index variable */ +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + /* check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* write end of PNG file */ + png_write_IEND(png_ptr); +#if 0 +/* This flush, added in libpng-1.0.8, causes some applications to crash + because they do not set png_ptr->output_flush_fn */ + png_flush(png_ptr); +#endif +} + +#if defined(PNG_WRITE_tIME_SUPPORTED) +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm\n"); + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t\n"); + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + png_debug(1, "in png_create_write_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + +#if !defined(PNG_1_0_X) +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_init_mmx_flags(png_ptr); /* 1.2.0 addition */ +#endif +#endif /* PNG_1_0_X */ + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; + png_destroy_struct(png_ptr); + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize png_ptr structure, and allocate any memory needed */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Deprecated. */ +#undef png_write_init +void PNGAPI +png_write_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for writing is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by the application for writing is too small."); + } + png_write_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + + +void PNGAPI +png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ + png_structp png_ptr=*ptr_ptr; +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i = 0; + + if (png_ptr == NULL) + return; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_write_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_write_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + *ptr_ptr = png_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#if !defined(PNG_1_0_X) +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_init_mmx_flags(png_ptr); /* 1.2.0 addition */ +#endif +#endif /* PNG_1_0_X */ + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif +} + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows\n"); + + if (png_ptr == NULL) + return; + + /* loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image\n"); +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* intialize interlace handling. If image is not interlaced, + this will set pass to 1 */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr == NULL) + return; + png_debug2(1, "in png_write_row (row %ld, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + + /* initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row."); + + /* check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); +#endif + + png_write_start_row(png_ptr); + } + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* if interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row, + png_ptr->row_info.rowbytes); + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* this should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush\n"); + if (png_ptr == NULL) + return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush\n"); + if (png_ptr == NULL) + return; + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct\n"); + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy\n"); + /* free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter\n"); + if (png_ptr == NULL) + return; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); + case PNG_FILTER_VALUE_NONE: png_ptr->do_filter=PNG_FILTER_NONE; break; + case PNG_FILTER_VALUE_SUB: png_ptr->do_filter=PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: png_ptr->do_filter=PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: png_ptr->do_filter=PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break; + default: png_ptr->do_filter = (png_byte)filters; break; + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics\n"); + if (png_ptr == NULL) + return; + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits=9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method\n"); + if (png_ptr == NULL) + return; + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; +} + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn\n"); + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#if defined(PNG_WRITE_INVERT_SUPPORTED) + /* invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) + /* pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + /* swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) + /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into + * RGB (4 channels -> 3 channels). The second parameter is not used. + */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#if defined(PNG_WRITE_BGR_SUPPORTED) + /* flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_SUPPORTED) + /* swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + /* swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + if(transforms == 0 || params == NULL) + /* quiet compiler warnings */ return; +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngwtran.c b/demo/src/lib/libpng/contrib/pngwtran.c new file mode 100644 index 000000000..0372fe656 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngwtran.c @@ -0,0 +1,572 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations\n"); + + if (png_ptr == NULL) + return; + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if(png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* user write transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#if defined(PNG_WRITE_PACK_SUPPORTED) +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack\n"); + if (row_info->bit_depth == 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* with low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0-s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/contrib/pngwutil.c b/demo/src/lib/libpng/contrib/pngwutil.c new file mode 100644 index 000000000..4a9c52b93 --- /dev/null +++ b/demo/src/lib/libpng/contrib/pngwutil.c @@ -0,0 +1,2750 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.2.11 June 4, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[4]; + png_debug2(0, "Writing %s chunk (%lu bytes)\n", chunk_name, length); + + /* write the length */ + png_save_uint_32(buf, length); + png_write_data(png_ptr, buf, (png_size_t)4); + + /* write the chunk name */ + png_write_data(png_ptr, chunk_name, (png_size_t)4); + /* reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* write the data, and run the CRC over it */ + if (data != NULL && length > 0) + { + png_calculate_crc(png_ptr, data, length); + png_write_data(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + /* write the crc */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void /* PRIVATE */ +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + /* write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)8 - png_ptr->sig_bytes); + if(png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* + * This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* the uncompressed input data */ + int input_len; /* its length */ + int num_output_ptr; /* number of output pointers used */ + int max_output_ptr; /* size of output_ptr */ + png_charpp output_ptr; /* array of pointers to output */ +} compression_state; + +/* compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = 0; + comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + comp->input_len = 0; + + /* we may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + sprintf(msg, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* this is the same compression loop as in png_write_row() */ + do + { + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save the data */ + comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* finish the compression */ + do + { + /* tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save off the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* we got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr,(png_bytep)comp->output_ptr[i], + png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + comp->output_ptr[i]=NULL; + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + comp->output_ptr=NULL; + /* write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + + /* reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; +#endif + png_byte buf[13]; /* buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR\n"); + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr,"Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* save off the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* write the chunk */ + png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13); + + /* initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_PLTE; +#endif + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE\n"); + if (( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d\n", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, num_pal * 3); +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this instead */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + png_debug(1, "in png_write_IDAT\n"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. */ + /* The conditions below are practically always satisfied; + however, they still must be checked. */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IEND; +#endif + png_debug(1, "in png_write_IEND\n"); + png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +/* write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +/* write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sRGB; +#endif + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB\n"); + if(srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1); +} +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +/* write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iCCP; +#endif + png_size_t name_len; + png_charp new_name; + compression_state comp; + + png_debug(1, "in png_write_iCCP\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if (name == NULL || (name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + { + png_warning(png_ptr, "Empty keyword in iCCP chunk"); + return; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len, + PNG_COMPRESSION_TYPE_BASE, &comp); + + /* make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, (png_bytep)png_iCCP, + (png_uint_32)name_len+profile_len+2); + new_name[name_len+1]=0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +/* write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sPLT; +#endif + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + int entry_size = (spalette->depth == 8 ? 6 : 10); + int palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifdef PNG_NO_POINTER_INDEXING + int i; +#endif + + png_debug(1, "in png_write_sPLT\n"); + if (spalette->name == NULL || (name_len = png_check_keyword(png_ptr, + spalette->name, &new_name))==0) + { + png_warning(png_ptr, "Empty keyword in sPLT chunk"); + return; + } + + /* make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, (png_bytep)png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 1); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, 1); + + /* loop through each palette entry, writing appropriately */ +#ifndef PNG_NO_POINTER_INDEXING + for (ep = spalette->entries; epentries+spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +/* write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sBIT; +#endif + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT\n"); + /* make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size); +} +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +/* write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + png_uint_32 itemp; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 || + white_x + white_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%f, white_y=%f\n",white_x, white_y); +#endif + return; + } + itemp = (png_uint_32)(white_x * 100000.0 + 0.5); + png_save_uint_32(buf, itemp); + itemp = (png_uint_32)(white_y * 100000.0 + 0.5); + png_save_uint_32(buf + 4, itemp); + + if (red_x < 0 || red_y < 0 || red_x + red_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM red point specified"); + return; + } + itemp = (png_uint_32)(red_x * 100000.0 + 0.5); + png_save_uint_32(buf + 8, itemp); + itemp = (png_uint_32)(red_y * 100000.0 + 0.5); + png_save_uint_32(buf + 12, itemp); + + if (green_x < 0 || green_y < 0 || green_x + green_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM green point specified"); + return; + } + itemp = (png_uint_32)(green_x * 100000.0 + 0.5); + png_save_uint_32(buf + 16, itemp); + itemp = (png_uint_32)(green_y * 100000.0 + 0.5); + png_save_uint_32(buf + 20, itemp); + + if (blue_x < 0 || blue_y < 0 || blue_x + blue_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM blue point specified"); + return; + } + itemp = (png_uint_32)(blue_x * 100000.0 + 0.5); + png_save_uint_32(buf + 24, itemp); + itemp = (png_uint_32)(blue_y * 100000.0 + 0.5); + png_save_uint_32(buf + 28, itemp); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x > 80000L || white_y > 80000L || white_x + white_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%ld, white_y=%ld\n",white_x, white_y); +#endif + return; + } + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + if (red_x + red_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM fixed red point specified"); + return; + } + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + if (green_x + green_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM green point specified"); + return; + } + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + if (blue_x + blue_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM blue point specified"); + return; + } + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); +} +#endif +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +/* write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, + int num_trans, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tRNS; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr,"Invalid number of transparent colors specified"); + return; + } + /* write the chunk out as it is */ + png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans, (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* one 16 bit value */ + if(tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +/* write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_bKGD; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index > png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6); + } + else + { + if(back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +/* write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_hIST; +#endif + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST\n"); + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, (png_bytep)png_hIST, (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword\n"); + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'\n", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if (*kp < 0x20 || (*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[40]; + + sprintf(msg, "invalid keyword character 0x%02X", *kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if(kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + *new_key=NULL; + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + new_key[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +/* write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tEXt; +#endif + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt\n"); + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in tEXt chunk"); + return; + } + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, (png_uint_32)key_len+text_len+1); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +/* write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_zTXt; +#endif + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in zTXt chunk"); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + png_free(png_ptr, new_key); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* write start of chunk */ + png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, (png_uint_32) + (key_len+text_len+2)); + /* write key */ + png_write_chunk_data(png_ptr, (png_bytep)key, key_len + 1); + buf[0] = (png_byte)compression; + /* write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +/* write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iTXt; +#endif + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang, new_key; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in iTXt chunk"); + return; + } + if (lang == NULL || (lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + + /* make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, (png_bytep)png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + + /* set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, 2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), lang_len + 1); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), lang_key_len + 1); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + if (new_lang) + png_free(png_ptr, new_lang); +} +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +/* write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_oFFs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs\n"); + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9); +} +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +/* write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pCAL; +#endif + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams); + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d\n", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d\n", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu\n", i, params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d\n", (int)total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +/* write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + char buf[64]; + png_size_t total_len; + + png_debug(1, "in png_write_sCAL\n"); + + buf[0] = (char)unit; +#if defined(_WIN32_WCE) +/* sprintf() function is not supported on WindowsCE */ + { + wchar_t wc_buf[32]; + size_t wc_len; + swprintf(wc_buf, TEXT("%12.12e"), width); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, NULL); + total_len = wc_len + 2; + swprintf(wc_buf, TEXT("%12.12e"), height); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len, + NULL, NULL); + total_len += wc_len; + } +#else + sprintf(buf + 1, "%12.12e", width); + total_len = 1 + png_strlen(buf + 1) + 1; + sprintf(buf + total_len, "%12.12e", height); + total_len += png_strlen(buf + total_len); +#endif + + png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, (png_bytep)buf, total_len); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s\n"); + + wlen = png_strlen(width); + hlen = png_strlen(height); + total_len = wlen + hlen + 2; + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + png_memcpy(buf + 1, width, wlen + 1); /* append the '\0' here */ + png_memcpy(buf + wlen + 2, height, hlen); /* do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, buf, total_len); +} +#endif +#endif +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +/* write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pHYs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs\n"); + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9); +} +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tIME; +#endif + png_byte buf[7]; + + png_debug(1, "in png_write_tIME\n"); + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7); +} +#endif + +/* initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row\n"); + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth,png_ptr->width)+1); + + /* set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + + /* set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_memset(png_ptr->prev_row, 0, buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep )png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep )png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int ret; + + png_debug(1, "in png_write_finish_row\n"); + /* next row */ + png_ptr->row_number++; + + /* see if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth,png_ptr->width))+1); + return; + } + } +#endif + + /* if we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* check for an error */ + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1, "in png_do_write_interlace\n"); + /* we don't have to do anything on the last pass (6) */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && pass < 6) +#else + if (pass < 6) +#endif + { + /* each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* start at the beginning */ + dp = row; + /* find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep prev_row, best_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter\n"); + /* find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; + best_row = row_buf = png_ptr->row_buf; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* it's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } + + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +} + + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row\n"); + png_debug1(2, "filter = %d\n", filtered_row[0]); + /* set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* repeat until we have compressed all the data */ + do + { + int ret; /* return of zlib */ + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* see if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/demo/src/lib/libpng/main.cc b/demo/src/lib/libpng/main.cc new file mode 100644 index 000000000..736fe2c23 --- /dev/null +++ b/demo/src/lib/libpng/main.cc @@ -0,0 +1,63 @@ +#include + +extern "C" { +#include +} + +#include + +using namespace Genode; + +static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t len) +{ +// read((char *)data, len); +} + +int main(int argc, char **argv) +{ + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) return 1; + + png_set_read_fn(png_ptr, 0, user_read_data); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); + return 2; + } + + png_read_info(png_ptr, info_ptr); + + /* get image data chunk */ + int bit_depth, color_type, interlace_type; + png_uint_32 w, h; + png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + int _min_w = w; + int _min_h = h; + printf("png is %d x %d, depth=%d\n", _min_w, _min_h, bit_depth); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_gray_1_2_4_to_8(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + if (bit_depth < 8) png_set_packing(png_ptr); + if (bit_depth == 16) png_set_strip_16(png_ptr); + + /* allocate buffer for decoding */ + png_byte **row_ptrs = (png_byte **)malloc(_min_h * sizeof(png_byte*)); + + int needed_row_size = png_get_rowbytes(png_ptr, info_ptr)*8; + for (int i = 0; i < _min_h; ++i ) + row_ptrs[i] = (png_byte *)malloc(needed_row_size); + + /* fill texture */ + png_read_image(png_ptr, row_ptrs); + + return 0; +} diff --git a/demo/src/lib/libpng/stdio.h b/demo/src/lib/libpng/stdio.h new file mode 100644 index 000000000..e69de29bb diff --git a/demo/src/lib/libpng/target.mk b/demo/src/lib/libpng/target.mk new file mode 100644 index 000000000..317818db7 --- /dev/null +++ b/demo/src/lib/libpng/target.mk @@ -0,0 +1,7 @@ +TARGET = test-libpng_static +SRC_CC = main.cc +INC_DIR = $(REP_DIR)/include/libpng \ + $(REP_DIR)/include/mini_c \ + $(REP_DIR)/include/libz +CC_OPT += -DPNG_USER_CONFIG +LIBS = cxx env libpng_static libz_static mini_c diff --git a/demo/src/lib/libz/contrib/adler32.c b/demo/src/lib/libz/contrib/adler32.c new file mode 100644 index 000000000..007ba2627 --- /dev/null +++ b/demo/src/lib/libz/contrib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/demo/src/lib/libz/contrib/compress.c b/demo/src/lib/libz/contrib/compress.c new file mode 100644 index 000000000..df04f0148 --- /dev/null +++ b/demo/src/lib/libz/contrib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/demo/src/lib/libz/contrib/crc32.c b/demo/src/lib/libz/contrib/crc32.c new file mode 100644 index 000000000..f658a9ef5 --- /dev/null +++ b/demo/src/lib/libz/contrib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/demo/src/lib/libz/contrib/crc32.h b/demo/src/lib/libz/contrib/crc32.h new file mode 100644 index 000000000..8053b6117 --- /dev/null +++ b/demo/src/lib/libz/contrib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/demo/src/lib/libz/contrib/deflate.c b/demo/src/lib/libz/contrib/deflate.c new file mode 100644 index 000000000..29ce1f64a --- /dev/null +++ b/demo/src/lib/libz/contrib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/demo/src/lib/libz/contrib/deflate.h b/demo/src/lib/libz/contrib/deflate.h new file mode 100644 index 000000000..05a5ab3a2 --- /dev/null +++ b/demo/src/lib/libz/contrib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/demo/src/lib/libz/contrib/gzio.c b/demo/src/lib/libz/contrib/gzio.c new file mode 100644 index 000000000..7e90f4928 --- /dev/null +++ b/demo/src/lib/libz/contrib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/demo/src/lib/libz/contrib/infback.c b/demo/src/lib/libz/contrib/infback.c new file mode 100644 index 000000000..455dbc9ee --- /dev/null +++ b/demo/src/lib/libz/contrib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/demo/src/lib/libz/contrib/inffast.c b/demo/src/lib/libz/contrib/inffast.c new file mode 100644 index 000000000..bbee92ed1 --- /dev/null +++ b/demo/src/lib/libz/contrib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/demo/src/lib/libz/contrib/inffast.h b/demo/src/lib/libz/contrib/inffast.h new file mode 100644 index 000000000..1e88d2d97 --- /dev/null +++ b/demo/src/lib/libz/contrib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/demo/src/lib/libz/contrib/inffixed.h b/demo/src/lib/libz/contrib/inffixed.h new file mode 100644 index 000000000..75ed4b597 --- /dev/null +++ b/demo/src/lib/libz/contrib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/demo/src/lib/libz/contrib/inflate.c b/demo/src/lib/libz/contrib/inflate.c new file mode 100644 index 000000000..792fdee8e --- /dev/null +++ b/demo/src/lib/libz/contrib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/demo/src/lib/libz/contrib/inflate.h b/demo/src/lib/libz/contrib/inflate.h new file mode 100644 index 000000000..07bd3e78a --- /dev/null +++ b/demo/src/lib/libz/contrib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/demo/src/lib/libz/contrib/inftrees.c b/demo/src/lib/libz/contrib/inftrees.c new file mode 100644 index 000000000..8a9c13ff0 --- /dev/null +++ b/demo/src/lib/libz/contrib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/demo/src/lib/libz/contrib/inftrees.h b/demo/src/lib/libz/contrib/inftrees.h new file mode 100644 index 000000000..b1104c87e --- /dev/null +++ b/demo/src/lib/libz/contrib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/demo/src/lib/libz/contrib/trees.c b/demo/src/lib/libz/contrib/trees.c new file mode 100644 index 000000000..395e4e168 --- /dev/null +++ b/demo/src/lib/libz/contrib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/demo/src/lib/libz/contrib/trees.h b/demo/src/lib/libz/contrib/trees.h new file mode 100644 index 000000000..72facf900 --- /dev/null +++ b/demo/src/lib/libz/contrib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/demo/src/lib/libz/contrib/uncompr.c b/demo/src/lib/libz/contrib/uncompr.c new file mode 100644 index 000000000..b59e3d0de --- /dev/null +++ b/demo/src/lib/libz/contrib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/demo/src/lib/libz/contrib/zutil.c b/demo/src/lib/libz/contrib/zutil.c new file mode 100644 index 000000000..d55f5948a --- /dev/null +++ b/demo/src/lib/libz/contrib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/demo/src/lib/libz/contrib/zutil.h b/demo/src/lib/libz/contrib/zutil.h new file mode 100644 index 000000000..b7d5eff81 --- /dev/null +++ b/demo/src/lib/libz/contrib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/demo/src/lib/mini_c/README b/demo/src/lib/mini_c/README new file mode 100644 index 000000000..ef2ffd579 --- /dev/null +++ b/demo/src/lib/mini_c/README @@ -0,0 +1,10 @@ +Mini C library + +This library only provides the libc support for libz, libpng, +and DOpE. Most functions are not implemented. The implemented +functions might be slightly incompatible to a real libc. +Please use this library with caution! + +If you require libc support that goes beyond simple string +functions for your application, please consider porting a +real libc to Genode instead of enhancing mini_c. diff --git a/demo/src/lib/mini_c/abort.cc b/demo/src/lib/mini_c/abort.cc new file mode 100644 index 000000000..ea586c269 --- /dev/null +++ b/demo/src/lib/mini_c/abort.cc @@ -0,0 +1,22 @@ +/* + * \brief Mini C abort() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +extern "C" void *abort(void) +{ + PDBG("abort called"); + Genode::sleep_forever(); + return 0; +} diff --git a/demo/src/lib/mini_c/atol.cc b/demo/src/lib/mini_c/atol.cc new file mode 100644 index 000000000..d4c88c123 --- /dev/null +++ b/demo/src/lib/mini_c/atol.cc @@ -0,0 +1,21 @@ +/* + * \brief Mini C atol() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" long atol(const char *nptr) +{ + long result = 0; + Genode::ascii_to(nptr, &result); + return result; +} diff --git a/demo/src/lib/mini_c/malloc_free.cc b/demo/src/lib/mini_c/malloc_free.cc new file mode 100644 index 000000000..c5a2316d6 --- /dev/null +++ b/demo/src/lib/mini_c/malloc_free.cc @@ -0,0 +1,50 @@ +/* + * \brief Mini C malloc() and free() + * \author Norman Feske + * \date 2006-07-21 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using namespace Genode; + + +extern "C" void *malloc(unsigned size) +{ + /* + * We store the size of the allocation at the very + * beginning of the allocated block and return + * the subsequent address. This way, we can retrieve + * the size information when freeing the block. + */ + unsigned long real_size = size + sizeof(unsigned long); + void *addr = 0; + if (!env()->heap()->alloc(real_size, &addr)) + return 0; + + *(unsigned long *)addr = real_size; + return (unsigned long *)addr + 1; +} + + +extern "C" void *calloc(unsigned nmemb, unsigned size) +{ + void *addr = malloc(nmemb*size); + memset(addr, 0, nmemb*size); + return addr; +} + + +extern "C" void free(void *ptr) +{ + unsigned long *addr = ((unsigned long *)ptr) - 1; + env()->heap()->free(addr, *addr); +} diff --git a/demo/src/lib/mini_c/memcmp.cc b/demo/src/lib/mini_c/memcmp.cc new file mode 100644 index 000000000..8a4bde7c4 --- /dev/null +++ b/demo/src/lib/mini_c/memcmp.cc @@ -0,0 +1,19 @@ +/* + * \brief Mini C memcmp() + * \author Christian Prochaska + * \date 2009-08-11 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" int memcmp(const void *s1, const void *s2, Genode::size_t n) +{ + return Genode::memcmp(s1, s2, n); +} diff --git a/demo/src/lib/mini_c/memset.cc b/demo/src/lib/mini_c/memset.cc new file mode 100644 index 000000000..6564942ce --- /dev/null +++ b/demo/src/lib/mini_c/memset.cc @@ -0,0 +1,19 @@ +/* + * \brief Mini C memset() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" void *memset(void *s, int c, Genode::size_t n) +{ + return Genode::memset(s, c, n); +} diff --git a/demo/src/lib/mini_c/mini_c.c b/demo/src/lib/mini_c/mini_c.c new file mode 100644 index 000000000..9f621a179 --- /dev/null +++ b/demo/src/lib/mini_c/mini_c.c @@ -0,0 +1,84 @@ +/* + * \brief Mini C dummy functions + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +#ifdef GENODE_RELEASE +#define printf(...) +#endif /* GENODE_RELEASE */ + +int sprintf(char *str, const char *format, ...) + { printf("%s: not implemented\n", __func__); return 0; } +FILE *fopen(const char *path, const char *mode) + { printf("%s: not implemented\n", __func__); return 0; } +FILE *fdopen(int fildes, const char *mode) + { printf("%s: not implemented\n", __func__); return 0; } +int fclose(FILE *fp) + { printf("%s: not implemented\n", __func__); return 0; } +int fprintf(FILE *stream, const char *format, ...) + { printf("%s: not implemented\n", __func__); return 0; } +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) + { printf("%s: not implemented\n", __func__); return 0; } +unsigned fread(void *ptr, unsigned size, unsigned nmemb, FILE *stream) + { printf("%s: not implemented\n", __func__); return 0; } +int fputc(int c, FILE *stream) + { printf("%s: not implemented\n", __func__); return 0; } +int fflush(FILE *stream) + { printf("%s: not implemented\n", __func__); return 0; } +int fseek(FILE *stream, long offset, int whence) + { printf("%s: not implemented\n", __func__); return 0; } +long ftell(FILE *stream) + { printf("%s: not implemented\n", __func__); return 0; } +void clearerr(FILE *stream) + { printf("%s: not implemented\n", __func__); } +int ferror(FILE *stream) + { printf("%s: not implemented\n", __func__); return 0; } +int puts(const char *s) + { printf("%s", s); return 1; } +int putchar(int c) + { printf("%c", c); return c; } + +#include + +int abs(int j) +{ + return j < 0 ? -j : j; +} + +/* in alloc_env_backend.cc +void *calloc(unsigned nmemb, unsigned size) + { printf("%s: not implemented\n", __func__); return 0; } +*/ + +#include + +/* in base/cxx +void *memcpy(void *dest, const void *src, unsigned n); +*/ +char *strcpy(char *dest, const char *src) + { printf("%s: not implemented\n", __func__); return 0; } +char *strcat(char *dest, const char *src) + { printf("%s: not implemented\n", __func__); return 0; } + +inline size_t min(size_t v1, size_t v2) { return v1 < v2 ? v1 : v2; } + +char *strncpy(char *dst, const char *src, size_t n) +{ + n = min(n, strlen(src) + 1); + memcpy(dst, src, n); + if (n > 0) dst[n - 1] = 0; + return dst; +} + +char *strerror(int errnum) + { printf("%s: not implemented\n", __func__); return 0; } diff --git a/demo/src/lib/mini_c/printf.cc b/demo/src/lib/mini_c/printf.cc new file mode 100644 index 000000000..07d287595 --- /dev/null +++ b/demo/src/lib/mini_c/printf.cc @@ -0,0 +1,24 @@ +/* + * \brief Mini C printf() + * \author Norman Feske + * \date 2008-10-23 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" void printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + + Genode::vprintf(format, list); + + va_end(list); +} diff --git a/demo/src/lib/mini_c/snprintf.cc b/demo/src/lib/mini_c/snprintf.cc new file mode 100644 index 000000000..7a3cee6d8 --- /dev/null +++ b/demo/src/lib/mini_c/snprintf.cc @@ -0,0 +1,27 @@ +/* + * \brief Mini C snprintf() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +extern "C" int snprintf(char *dst, Genode::size_t dst_len, const char *format, ...) +{ + va_list list; + va_start(list, format); + + Genode::String_console sc(dst, dst_len); + sc.vprintf(format, list); + + va_end(list); + return sc.len(); +} diff --git a/demo/src/lib/mini_c/strlen.cc b/demo/src/lib/mini_c/strlen.cc new file mode 100644 index 000000000..72c644cc1 --- /dev/null +++ b/demo/src/lib/mini_c/strlen.cc @@ -0,0 +1,19 @@ +/* + * \brief Mini C strlen() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" Genode::size_t strlen(const char *s) +{ + return Genode::strlen(s); +} diff --git a/demo/src/lib/mini_c/strtod.cc b/demo/src/lib/mini_c/strtod.cc new file mode 100644 index 000000000..337496458 --- /dev/null +++ b/demo/src/lib/mini_c/strtod.cc @@ -0,0 +1,27 @@ +/* + * \brief Mini C strtod() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" double strtod(const char *nptr, char **endptr) +{ + double value = 0; + + int num_chars = Genode::ascii_to(nptr, &value); + + if (endptr) + *endptr = (char *)(nptr + num_chars); + + return value; +} + diff --git a/demo/src/lib/mini_c/strtol.cc b/demo/src/lib/mini_c/strtol.cc new file mode 100644 index 000000000..3f5beccca --- /dev/null +++ b/demo/src/lib/mini_c/strtol.cc @@ -0,0 +1,31 @@ +/* + * \brief Mini C strtol() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +using namespace Genode; + +extern "C" long int strtol(const char *nptr, char **endptr, int base) +{ + long num_chars, result = 0; + + if (base == 0) + num_chars = ascii_to(nptr, &result); + else + num_chars = ascii_to(nptr, &result, base); + + if (endptr) + *endptr = (char *)(nptr + num_chars); + + return result; +} diff --git a/demo/src/lib/mini_c/vsnprintf.cc b/demo/src/lib/mini_c/vsnprintf.cc new file mode 100644 index 000000000..f9537ec74 --- /dev/null +++ b/demo/src/lib/mini_c/vsnprintf.cc @@ -0,0 +1,21 @@ +/* + * \brief Mini C vsnprintf() + * \author Christian Helmuth + * \date 2008-07-24 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" int vsnprintf(char *dst, Genode::size_t dst_len, const char *format, va_list list) +{ + Genode::String_console sc(dst, dst_len); + sc.vprintf(format, list); + return sc.len(); +} diff --git a/demo/src/server/liquid_framebuffer/README b/demo/src/server/liquid_framebuffer/README new file mode 100644 index 000000000..43b620287 --- /dev/null +++ b/demo/src/server/liquid_framebuffer/README @@ -0,0 +1,31 @@ +Liquid frame buffer is an implementation of the frame buffer interface +running as a client of the Nitpicker GUI server. It supports the +following configuration options. The example shows the default +values. + +! +! +! +! on +! +! +! 400 +! 270 +! 500 +! 400 +! +! +! Liquid Framebuffer +! +! + +Because Liquid frame buffer creates the virtual frame-buffer window at +start time, not at session-creation time, sufficient memory resources must +be provided when starting the program. Consequently, the client does not +need to donate memory for the frame buffer backing store. + +Liquid frame buffer supports only one client. If multiple +virtual frame buffers are needed, multiple instances of the +program should be used. diff --git a/demo/src/server/liquid_framebuffer/framebuffer_window.h b/demo/src/server/liquid_framebuffer/framebuffer_window.h new file mode 100644 index 000000000..b9abb8088 --- /dev/null +++ b/demo/src/server/liquid_framebuffer/framebuffer_window.h @@ -0,0 +1,108 @@ +/* + * \brief Window with holding a fixed-size content element + * \author Norman Feske + * \date 2006-09-21 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _FRAMEBUFFER_WINDOW_H_ +#define _FRAMEBUFFER_WINDOW_H_ + +#include "window.h" +#include "titlebar.h" +#include "sky_texture.h" + +#define TITLEBAR_RGBA _binary_titlebar_rgba_start + +extern unsigned char TITLEBAR_RGBA[]; + + +template +class Framebuffer_window : public Window +{ + private: + + /** + * Constants + */ + enum { _TH = 32 }; /* height of title bar */ + + /** + * Widgets + */ + Titlebar _titlebar; + Sky_texture _bg_texture; + int _bg_offset; + Element *_content; + + public: + + /** + * Constructor + */ + Framebuffer_window(Platform *pf, + Redraw_manager *redraw, + Element *content, + const char *name) + : + Window(pf, redraw, content->min_w() + 2, content->min_h() + 1 + _TH), + _bg_offset(0), _content(content) + { + /* titlebar */ + _titlebar.rgba(TITLEBAR_RGBA); + _titlebar.text(name); + _titlebar.event_handler(new Mover_event_handler(this)); + + append(&_titlebar); + append(_content); + + _min_w = max_w(); + _min_h = max_h(); + } + + /** + * Window interface + */ + void format(int w, int h) + { + _w = w; + _h = h; + + Parent_element::_format_children(1, w); + + pf()->view_geometry(pf()->vx(), pf()->vy(), _w, _h); + redraw()->size(_w, _h); + refresh(); + } + + /** + * Configure background texture offset (for background animation) + */ + void bg_offset(int bg_offset) { _bg_offset = bg_offset; } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y) + { + _bg_texture.draw(c, 0, - _bg_offset); + + ::Parent_element::draw(c, x, y); + + /* border */ + Color col(0, 0, 0); + c->draw_box(0, 0, _w, 1, col); + c->draw_box(0, _TH, _w, 1, col); + c->draw_box(0, _h - 1, _w, 1, col); + c->draw_box(0, 1, 1, _h - 2, col); + c->draw_box(_w - 1, 1, 1, _h - 2, col); + }; +}; + +#endif diff --git a/demo/src/server/liquid_framebuffer/main.cc b/demo/src/server/liquid_framebuffer/main.cc new file mode 100644 index 000000000..8705e66ae --- /dev/null +++ b/demo/src/server/liquid_framebuffer/main.cc @@ -0,0 +1,214 @@ +/* + * \brief Nitpicker-based virtual framebuffer + * \author Norman Feske + * \date 2006-09-21 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +#include "framebuffer_window.h" +#include "canvas_rgb565.h" +#include "user_state.h" +#include "services.h" + +/** + * Runtime configuration + */ +namespace Config +{ + int iconbar_detail = 1; + int background_detail = 1; + int mouse_cursor = 1; + int browser_attr = 0; +} + + +void Launcher::launch() { } + +extern int native_startup(int, char **); + + +class Background_animator : public Tick +{ + private: + + Framebuffer_window *_fb_win; + int _bg_offset; + + public: + + /** + * Constructor + */ + Background_animator(Framebuffer_window *fb_win): + _fb_win(fb_win), _bg_offset(0) { + schedule(20); } + + /** + * Tick interface + */ + int on_tick() + { + _fb_win->bg_offset(_bg_offset); + _bg_offset += 2; + _fb_win->refresh(); + + /* schedule next tick */ + return 1; + } +}; + + +/** + * Animated background + */ +static bool config_animate = true; +static bool config_alpha = true; + +/** + * Size and position of virtual frame buffer + */ +static long config_fb_width = 500; +static long config_fb_height = 400; +static long config_fb_x = 400; +static long config_fb_y = 260; + +/** + * Window title + */ +static const char *config_title = "Liquid Framebuffer"; + + +/** + * Parse configuration + */ +static void read_config() +{ + using namespace Genode; + + Xml_node config_node = config()->xml_node(); + + try { + char buf[16]; + config_node.sub_node("animate").value(buf, sizeof(buf)); + + if (!strcmp("off", buf)) config_animate = false; + else if (!strcmp("on", buf)) config_animate = true; + else + Genode::printf("Warning: invalid value for animate declaration,\n" + " valid values are 'on', 'off.\n'"); + } catch (Xml_node::Nonexistent_sub_node) { } + + config_alpha = config_animate; + + try { config_node.sub_node("x").value(&config_fb_x); } + catch (Xml_node::Nonexistent_sub_node) { } + + try { config_node.sub_node("y").value(&config_fb_y); } + catch (Xml_node::Nonexistent_sub_node) { } + + try { config_node.sub_node("width").value(&config_fb_width); } + catch (Xml_node::Nonexistent_sub_node) { } + + try { config_node.sub_node("height").value(&config_fb_height); } + catch (Xml_node::Nonexistent_sub_node) { } + + try { + static char buf[64]; + config_node.sub_node("title").value(buf, sizeof(buf)); + config_title = buf; + } catch (Xml_node::Nonexistent_sub_node) { } +} + + +/** + * Main program + */ +int main(int argc, char **argv) +{ + if (native_startup(argc, argv)) return -1; + + try { read_config(); } catch (...) { } + + /* heuristic for allocating the double-buffer backing store */ + enum { WINBORDER_WIDTH = 10, WINBORDER_HEIGHT = 40 }; + + /* init platform */ + static Platform pf(config_fb_x, config_fb_y, + config_fb_width + WINBORDER_WIDTH, + config_fb_height + WINBORDER_HEIGHT, + config_fb_width + WINBORDER_WIDTH, + config_fb_height + WINBORDER_HEIGHT); + + /* initialize our services and window content */ + init_services(config_fb_width, config_fb_height, config_alpha); + + /* init canvas */ + static Chunky_canvas canvas; + canvas.init(static_cast(pf.buf_adr()), + pf.scr_w()*pf.scr_h()); + canvas.set_size(pf.scr_w(), pf.scr_h()); + canvas.clip(0, 0, pf.scr_w(), pf.scr_h()); + + /* init redraw manager */ + static Redraw_manager redraw(&canvas, &pf, pf.vw(), pf.vh()); + + /* create instance of browser window */ + static Framebuffer_window + fb_win(&pf, &redraw, window_content(), config_title); + + if (config_animate) { + static Background_animator fb_win_bg_anim(&fb_win); + } + + /* create user state manager */ + static User_state user_state(&fb_win, &fb_win, pf.vx(), pf.vy()); + + /* assign framebuffer window as root element to redraw manager */ + redraw.root(&fb_win); + + fb_win.parent(&user_state); + fb_win.format(fb_win.min_w(), fb_win.min_h()); + + /* enter main loop */ + Event ev; + unsigned long curr_time, old_time; + curr_time = old_time = pf.timer_ticks(); + do { + pf.get_event(&ev); + + if (ev.type != Event::WHEEL) { + ev.mx -= user_state.vx(); + ev.my -= user_state.vy(); + } + + /* direct all keyboard events to the window content */ + if ((ev.type == Event::PRESS || ev.type == Event::RELEASE) + && (ev.code != Event::BTN_LEFT)) + window_content()->handle_event(ev); + else + user_state.handle_event(ev); + + if (ev.type == Event::REFRESH) + pf.scr_update(0, 0, pf.scr_w(), pf.scr_h()); + + if (ev.type == Event::TIMER) + Tick::handle(pf.timer_ticks()); + + /* perform periodic redraw */ + curr_time = pf.timer_ticks(); + if (!pf.event_pending() && ((curr_time - old_time > 20) || (curr_time < old_time))) { + old_time = curr_time; + redraw.process(); + } + } while (ev.type != Event::QUIT); + + return 0; +} diff --git a/demo/src/server/liquid_framebuffer/services.cc b/demo/src/server/liquid_framebuffer/services.cc new file mode 100644 index 000000000..3edf54934 --- /dev/null +++ b/demo/src/server/liquid_framebuffer/services.cc @@ -0,0 +1,256 @@ +/* + * \brief Implementation of Framebuffer and Input services + * \author Norman Feske + * \date 2006-09-22 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +#include "canvas_rgb565.h" +#include "services.h" + + +/***************** + ** Event queue ** + *****************/ + +class Event_queue +{ + private: + + enum { QUEUE_SIZE = 1024 }; + + Input::Event _queue[QUEUE_SIZE]; + int _head; + int _tail; + Genode::Semaphore _sem; + + public: + + /** + * Constructor + */ + Event_queue(): _head(0), _tail(0) + { + memset(_queue, 0, sizeof(_queue)); + } + + void post(Input::Event ev) + { + if ((_head + 1)%QUEUE_SIZE != _tail) { + _queue[_head] = ev; + _head = (_head + 1)%QUEUE_SIZE; + _sem.up(); + } + } + + Input::Event get() + { + _sem.down(); + Input::Event dst_ev = _queue[_tail]; + _tail = (_tail + 1)%QUEUE_SIZE; + return dst_ev; + } + + int pending() { return _head != _tail; } + +} _ev_queue; + + +/*************************** + ** Input service backend ** + ***************************/ + +namespace Input { + + void event_handling(bool enable) { } + bool event_pending() { return _ev_queue.pending(); } + Event get_event() { return _ev_queue.get(); } + +} + + +class Window_content : public Element +{ + private: + + class Content_event_handler : public Event_handler + { + private: + + Event_queue *_ev_queue; + int _omx, _omy; + Element *_element; + + public: + + Content_event_handler(Event_queue *ev_queue, Element *element): + _ev_queue(ev_queue), _element(element) { } + + void handle(Event &ev) + { + int mx = ev.mx - _element->abs_x(); + int my = ev.my - _element->abs_y(); + + int code = 0; + + if (ev.type == Event::PRESS || ev.type == Event::RELEASE) + code = ev.code; + + Input::Event::Type type; + + type = (ev.type == Event::MOTION) ? Input::Event::MOTION + : (ev.type == Event::PRESS) ? Input::Event::PRESS + : (ev.type == Event::RELEASE) ? Input::Event::RELEASE + : Input::Event::INVALID; + + if (type != Input::Event::INVALID) + _ev_queue->post(Input::Event(type, code, mx, my, mx - _omx, my - _omy)); + + _omx = mx; + _omy = my; + } + }; + + unsigned _fb_w, _fb_h; + Genode::Attached_ram_dataspace _fb_ds; + Pixel_rgb565 *_pixel; + unsigned char *_alpha; + Texture_rgb565 _fb_texture; + Content_event_handler _ev_handler; + + public: + + Window_content(unsigned fb_w, unsigned fb_h, Event_queue *ev_queue, + bool config_alpha) + : + _fb_w(fb_w), _fb_h(fb_h), + _fb_ds(Genode::env()->ram_session(), _fb_w*_fb_h*sizeof(Pixel_rgb565)), + _pixel(_fb_ds.local_addr()), + _alpha((unsigned char *)Genode::env()->heap()->alloc(_fb_w*_fb_h)), + _fb_texture(_pixel, _alpha, _fb_w, _fb_h), + _ev_handler(ev_queue, this) + { + _min_w = _fb_w; + _min_h = _fb_h; + + int alpha_min = config_alpha ? 0 : 255; + + /* init alpha channel */ + for (unsigned y = 0; y < _fb_h; y++) + for (unsigned x = 0; x < _fb_w; x++) { + + int v = (x * y + (_fb_w*_fb_h)/4) / _fb_w; + v = v + (x + y)/2; + int a = v & 0xff; + if (v & 0x100) + a = 255 - a; + + a += (dither_matrix[y % dither_size][x % dither_size] - 127) >> 4; + + _alpha[y*_fb_w + x] = Genode::max(alpha_min, Genode::min(a, 255)); + } + + event_handler(&_ev_handler); + } + + /** + * Accessors + */ + Genode::Dataspace_capability fb_ds_cap() { return _fb_ds.cap(); } + unsigned fb_w() { return _fb_w; } + unsigned fb_h() { return _fb_h; } + + /** + * Element interface + */ + void draw(Canvas *c, int x, int y) { + c->draw_texture(&_fb_texture, _x + x, _y + y); } +}; + + +static Window_content *_window_content; + +Element *window_content() { return _window_content; } + + +/*********************************************** + ** Implementation of the framebuffer service ** + ***********************************************/ + +namespace Framebuffer +{ + class Session_component : public Genode::Rpc_object + { + public: + + Genode::Dataspace_capability dataspace() { return _window_content->fb_ds_cap(); } + + void info(int *out_w, int *out_h, Mode *out_mode) + { + *out_w = _window_content->fb_w(); + *out_h = _window_content->fb_h(); + *out_mode = RGB565; + } + + void refresh(int x, int y, int w, int h) { + window_content()->redraw_area(x, y, w, h); } + }; + + + class Root : public Genode::Root_component + { + protected: + + Session_component *_create_session(const char *args) { + PDBG("creating framebuffer session"); + return new (md_alloc()) Session_component(); } + + public: + + Root(Genode::Rpc_entrypoint *session_ep, + Genode::Allocator *md_alloc) + : Genode::Root_component(session_ep, md_alloc) { } + }; +} + + +void init_services(unsigned fb_w, unsigned fb_h, bool config_alpha) +{ + using namespace Genode; + + static Window_content content(fb_w, fb_h, &_ev_queue, config_alpha); + _window_content = &content; + + /* + * Initialize server entry point + */ + enum { STACK_SIZE = 4096 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "liquid_fb_ep"); + + /* + * Let the entry point serve the framebuffer and input root interfaces + */ + static Framebuffer::Root fb_root(&ep, env()->heap()); + static Input::Root input_root(&ep, env()->heap()); + + /* + * Now, the root interfaces are ready to accept requests. + * This is the right time to tell mummy about our services. + */ + env()->parent()->announce(ep.manage(&fb_root)); + env()->parent()->announce(ep.manage(&input_root)); +} diff --git a/demo/src/server/liquid_framebuffer/services.h b/demo/src/server/liquid_framebuffer/services.h new file mode 100644 index 000000000..388b90dab --- /dev/null +++ b/demo/src/server/liquid_framebuffer/services.h @@ -0,0 +1,23 @@ +/* + * \brief Fb_nit-internal service interface + * \author Norman Feske + * \date 2006-09-22 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SERVICES_H_ +#define _SERVICES_H_ + +#include "canvas.h" +#include "elements.h" + +extern Element *window_content(); +extern void init_services(unsigned fb_w, unsigned fb_h, bool config_alpha); + +#endif diff --git a/demo/src/server/liquid_framebuffer/target.mk b/demo/src/server/liquid_framebuffer/target.mk new file mode 100644 index 000000000..e31904642 --- /dev/null +++ b/demo/src/server/liquid_framebuffer/target.mk @@ -0,0 +1,9 @@ +TARGET = liquid_fb +LIBS = scout_widgets +SRC_CC = main.cc services.cc +INC_DIR += $(REP_DIR)/src/app/scout/include \ + $(REP_DIR)/src/app/scout/include/genode \ + $(REP_DIR)/src/server/framebuffer/sdl + +# suppress non-critical but weird compiler warning, probably a bug in gcc-4.6.1 +CC_OPT_main += -Wno-uninitialized diff --git a/demo/src/server/nitlog/main.cc b/demo/src/server/nitlog/main.cc new file mode 100644 index 000000000..2b4ccf3f5 --- /dev/null +++ b/demo/src/server/nitlog/main.cc @@ -0,0 +1,412 @@ +/* + * \brief Nitpicker-based logging service + * \author Norman Feske + * \date 2006-09-18 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Nitpicker's graphics backend + */ +#include +#include +#include + + +enum { LOG_W = 80 }; /* number of visible characters per line */ +enum { LOG_H = 25 }; /* number of lines of log window */ + + +/* + * Font initialization + */ +extern char _binary_mono_tff_start; +Font default_font(&_binary_mono_tff_start); + + +class Log_entry +{ + private: + + char _label[64]; + char _text[LOG_W]; + char _attr[LOG_W]; + Color _color; + int _label_len; + int _text_len; + int _id; + + public: + + /** + * Constructors + * + * The default constructor is used to build an array of log entries. + */ + Log_entry() { } + + Log_entry(Color color, const char *label, const char *log_text, const char *log_attr, int id): + _color(color), _id(id) + { + Genode::strncpy(_label, label, sizeof(_label)); + Genode::strncpy(_text, log_text, sizeof(_text)); + + _label_len = Genode::strlen(_label); + _text_len = Genode::strlen(_text); + + /* replace line feed at the end of the text with a blank */ + if (_text_len > 0 && _text[_text_len - 1] == '\n') + _text[_text_len - 1] = ' '; + + Genode::memcpy(_attr, log_attr, _text_len); + } + + /** + * Draw entry + * + * An entry consists of a label and text. The argument 'new_section' + * marks a transition of output from one session to another. This + * information is used to separate sessions visually. + */ + void draw(Canvas *canvas, int y, int new_section = false) + { + Color label_fgcol = Color(min(255, _color.r + 200), + min(255, _color.g + 200), + min(255, _color.b + 200)); + Color label_bgcol = Color(_color.r, _color.g, _color.b); + Color text_fgcol = Color(180, 180, 180); + Color text_bgcol = Color(_color.r / 2, _color.g / 2, _color.b / 2); + + /* calculate label dimensions */ + int label_w = default_font.str_w(_label); + int label_h = default_font.str_h(_label); + + if (new_section) { + canvas->draw_box(Rect(Point(1, y), Area(label_w + 2, label_h - 1)), label_bgcol); + canvas->draw_string(Point(1, y - 1), &default_font, label_fgcol, _label); + canvas->draw_box(Rect(Point(1, y + label_h - 1), Area(label_w + 2, 1)), Color(0, 0, 0)); + canvas->draw_box(Rect(Point(label_w + 2, y), Area(1, label_h - 1)), _color); + canvas->draw_box(Rect(Point(label_w + 3, y), Area(1, label_h - 1)), Color(0, 0, 0)); + canvas->draw_box(Rect(Point(label_w + 4, y), Area(1000, label_h)), text_bgcol); + canvas->draw_box(Rect(Point(label_w + 4, y), Area(1000, 1)), Color(0, 0, 0)); + } else + canvas->draw_box(Rect(Point(1, y), Area(1000, label_h)), text_bgcol); + + /* draw log text */ + canvas->draw_string(Point(label_w + 6, y), &default_font, text_fgcol, _text); + } + + /** + * Accessors + */ + int label_len() { return _label_len; } + int id() { return _id; } +}; + + +class Log_window +{ + private: + + Canvas *_canvas; /* graphics backend */ + Log_entry _entries[LOG_H]; /* log entries */ + int _dst_entry; /* destination entry for next write */ + int _view_pos; /* current view port on the entry array */ + bool _scroll; /* scroll mode (when text hits bottom) */ + char _attr[LOG_W]; /* character attribute buffer */ + bool _dirty; /* schedules the log window for a redraw */ + Genode::Lock _dirty_lock; + + public: + + /** + * Constructor + */ + Log_window(Canvas *canvas): + _canvas(canvas), _dst_entry(0), _view_pos(0), _dirty(true) { } + + /** + * Write log entry + * + * \param color base color for highlighting the session. + * \param sid unique ID of the log session. This ID is used to + * determine section transitions in the log output. + */ + void write(Color color, const char *label, const char *log_text, int sid) + { + _entries[_dst_entry] = Log_entry(color, label, log_text, _attr, sid); + + if (_scroll) + _view_pos++; + + /* cycle through log entries */ + _dst_entry = (_dst_entry + 1) % LOG_H; + + /* start scrolling when the dst entry wraps for the first time */ + if (_dst_entry == 0) + _scroll = true; + + /* schedule log window for redraw */ + Genode::Lock::Guard lock_guard(_dirty_lock); + _dirty |= 1; + } + + /** + * Draw log window + * + * \retval true drawing operations had been performed + */ + bool draw() + { + { + Genode::Lock::Guard lock_guard(_dirty_lock); + if (!_dirty) return false; + _dirty = false; + } + + int line_h = default_font.str_h(" "); + int curr_session_id = -1; + + for (int i = 0, y = 0; i < LOG_H; i++, y += line_h) { + Log_entry *le = &_entries[(i + _view_pos) % LOG_H]; + le->draw(_canvas, y, curr_session_id != le->id()); + curr_session_id = le->id(); + } + + return true; + } +}; + + +class Log_session_component : public Genode::Rpc_object +{ + public: + + enum { LABEL_LEN = 64 }; + + private: + + Color _color; + Log_window *_log_window; + char _label[LABEL_LEN]; + int _id; + + static int _bit(int v, int bit_num) { return (v >> bit_num) & 1; } + + public: + + /** + * Constructor + */ + Log_session_component(const char *label, Log_window *log_window) + : _color(0, 0, 0), _log_window(log_window) + { + static int cnt; + + _id = cnt++; + + const int scale = 32; + const int offset = 64; + + /* compute session color */ + int r = (_bit(_id, 3) + 2*_bit(_id, 0))*scale + offset; + int g = (_bit(_id, 4) + 2*_bit(_id, 1))*scale + offset; + int b = (_bit(_id, 5) + 2*_bit(_id, 2))*scale + offset; + + _color = Color(r, g, b); + + Genode::strncpy(_label, label, sizeof(_label)); + } + + + /*************************** + ** Log session interface ** + ***************************/ + + Genode::size_t write(String const &log_text) + { + if (!log_text.is_valid_string()) { + PERR("corrupted string"); + return 0; + } + + _log_window->write(_color, _label, log_text.string(), _id); + return Genode::strlen(log_text.string()); + } +}; + + +class Log_root_component : public Genode::Root_component +{ + private: + + Log_window *_log_window; + + protected: + + Log_session_component *_create_session(const char *args) + { + PINF("create log session (%s)", args); + char label_buf[Log_session_component::LABEL_LEN]; + + Genode::Arg label_arg = Genode::Arg_string::find_arg(args, "label"); + label_arg.string(label_buf, sizeof(label_buf), ""); + + return new (md_alloc()) Log_session_component(label_buf, _log_window); + } + + public: + + /** + * Constructor + */ + Log_root_component(Genode::Rpc_entrypoint *ep, + Genode::Allocator *md_alloc, + Log_window *log_window) + : + Genode::Root_component(ep, md_alloc), + _log_window(log_window) { } +}; + + +class Log_view +{ + private: + + Nitpicker::View_capability _cap; + + int _x, _y, _w, _h; + + public: + + Log_view(Nitpicker::Session *nitpicker, + int x, int y, int w, int h) + : + _x(x), _y(y), _w(w), _h(h) + { + using namespace Nitpicker; + + _cap = nitpicker->create_view(); + View_client(_cap).viewport(_x, _y, _w, _h, 0, 0, true); + View_client(_cap).stack(Nitpicker::View_capability(), true, true); + } + + void top() + { + Nitpicker::View_client(_cap).stack(Nitpicker::View_capability(), true, true); + } + + void move(int x, int y) + { + _x = x, _y = y; + Nitpicker::View_client(_cap).viewport(_x, _y, _w, _h, 0, 0, true); + } + + /** + * Accessors + */ + int x() { return _x; } + int y() { return _y; } +}; + + +int main(int argc, char **argv) +{ + using namespace Genode; + + /* make sure that we connect to LOG before providing this service by ourself */ + printf("--- nitlog ---\n"); + + /* calculate size of log view in pixels */ + int log_win_w = default_font.str_w(" ") * LOG_W + 2; + int log_win_h = default_font.str_h(" ") * LOG_H + 2; + + /* init sessions to the required external services */ + static Nitpicker::Connection nitpicker(log_win_w, log_win_h); + static Timer::Connection timer; + + /* initialize entry point that serves the root interface */ + enum { STACK_SIZE = 4096 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "nitlog_ep"); + + /* + * Use sliced heap to allocate each session component at a separate + * dataspace. + */ + static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session()); + + /* create log window */ + void *addr = env()->rm_session()->attach(nitpicker.framebuffer()->dataspace()); + static Chunky_canvas canvas((Pixel_rgb565 *)addr, + Area(log_win_w, log_win_h)); + static Log_window log_window(&canvas); + + /* + * We clip a border of one pixel off the canvas. This way, the + * border remains unaffected by the drawing operations and + * acts as an outline for the log window. + */ + canvas.clip(Rect(Point(1, 1), Area(log_win_w - 2, log_win_h - 2))); + + /* create view for log window */ + Log_view log_view(&nitpicker, 20, 20, log_win_w, log_win_h); + + /* create root interface for service */ + static Log_root_component log_root(&ep, &sliced_heap, &log_window); + + /* announce service at our parent */ + env()->parent()->announce(ep.manage(&log_root)); + + /* handle input events */ + Input::Event *ev_buf = env()->rm_session()->attach(nitpicker.input()->dataspace()); + int omx = 0, omy = 0, key_cnt = 0; + while (1) { + + while (!nitpicker.input()->is_pending()) { + if (log_window.draw()) + nitpicker.framebuffer()->refresh(0, 0, log_win_w, log_win_h); + timer.msleep(20); + } + + for (int i = 0, num_ev = nitpicker.input()->flush(); i < num_ev; i++) { + + Input::Event *ev = &ev_buf[i]; + + if (ev->type() == Input::Event::PRESS) key_cnt++; + if (ev->type() == Input::Event::RELEASE) key_cnt--; + + /* move view */ + if (ev->type() == Input::Event::MOTION && key_cnt > 0) + log_view.move(log_view.x() + ev->ax() - omx, + log_view.y() + ev->ay() - omy); + + /* find selected view and bring it to front */ + if (ev->type() == Input::Event::PRESS && key_cnt == 1) + log_view.top(); + + omx = ev->ax(); omy = ev->ay(); + } + } + return 0; +} diff --git a/demo/src/server/nitlog/mono.tff b/demo/src/server/nitlog/mono.tff new file mode 100644 index 0000000000000000000000000000000000000000..0a58f395b29b3d0bed27ced853af51bf57205bfd GIT binary patch literal 20488 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 htPn $@ + @echo "#############################################" >> $@ + @echo >> $@ + @$(MAKE) --no-print-directory .. >> $@ + +DIRECTORIES = $(shell find .. -type d) + +.PHONY: $(DIRECTORIES) + +$(DIRECTORIES): + @if test -r $@/README; then \ + dir=$@; dir="$${dir:3}"; \ + if test -n "$$dir"; then \ + dir="'$$dir'"; \ + echo "$$dir"; \ + echo "$${dir//?/=}"; \ + echo; \ + fi; \ + cat $@/README; \ + echo; \ + echo; \ + fi; \ + recursion="$(shell find $@/* -mindepth 1 -name README -printf '%H\n')"; \ + if test "$$recursion"; then $(MAKE) --no-print-directory -s $$recursion; fi + +clean cleanall: + rm -f directories.txt diff --git a/doc/build_system.txt b/doc/build_system.txt new file mode 100644 index 000000000..5ef6e8e3e --- /dev/null +++ b/doc/build_system.txt @@ -0,0 +1,466 @@ + + + ======================= + The Genode build system + ======================= + + + Norman Feske + +Abstract +######## + +The Genode OS Framework comes with a custom build system that is designed for +the creation of highly modular and portable systems software. Understanding +its basic concepts is pivotal for using the full potential of the framework. +This document introduces those concepts and the best practises of putting them +to good use. Beside building software components from source code, common +and repetitive development tasks are the testing of individual components +and the integration of those components into complex system scenarios. To +streamline such tasks, the build system is accompanied with special tooling +support. This document introduces those tools. + + +Build directories and repositories +################################## + +The build system is supposed to never touch the source tree. The procedure of +building components and integrating them into system scenarios is done at +a distinct build directory. One build directory targets a specific platform, +i.e., a kernel and hardware architecture. Because the source tree is decoupled +from the build directory, one source tree can have many different build +directories associated, each targeted at another platform. + +The recommended way for creating a build directory is the use of the +'create_builddir' tool located at '/tool/builddir/'. By starting +the tool without arguments, its usage information will be printed. For creating +a new build directory, one of the listed target platforms must be specified. +Furthermore, the location of the new build directory has to be specified via +the 'BUILD_DIR=' argument. For example: + +! cd +! ./tool/create_builddir linux_x86 BUILD_DIR=/tmp/build.linux_x86 + +This command will create a new build directory for the Linux/x86 platform +at '/build.linux_x86/'. + + +Build-directory configuration via 'build.conf' +============================================== + +The fresh build directory will contain a 'Makefile', which is a symlink to +'tool/builddir/build.mk'. This 'Makefile' is the front end of the build system +and not supposed to be edited. Beside the 'Makefile', there is a 'etc/' +subdirectory that contains the build-directory configuration. For most +platforms, there is only a single 'build.conf' file, which defines the parts of +the Genode source tree incorporated in the build process. Those parts are +called _repositories_. + +The repository concept allows for keeping the source code well separated for +different concerns. For example, the platform-specific code for each target +platform is located in a dedicated 'base-' repository. Also, different +abstraction levels and features of the system are residing in different +repositories. The 'etc/build.conf' file defines the set of repositories to +consider in the build process. At build time, the build system overlays the +directory structures of all repositories specified via the 'REPOSITORIES' +declaration to form a single logical source tree. By changing the list of +'REPOSITORIES', the view of the build system on the source tree can be altered. +The 'etc/build.conf' as found in a fresh created build directory will list the +'base-' repository of the platform selected at the 'create_builddir' +command line as well as the 'base', 'os', and 'demo' repositories needed for +compiling Genode's default demonstration scenario. Furthermore, there are a +number of commented-out lines that can be uncommented for enabling additional +repositories. + +Note that the order of the repositories listed in the 'REPOSITORIES' declaration +is important. Front-most repositories shadow subsequent repositories. This +makes the repository mechanism a powerful tool for tweaking existing repositories: +By adding a custom repository in front of another one, customized versions of +single files (e.g., header files or target description files) can be supplied to +the build system without changing the original repository. + + +Building targets +================ + +To build all targets contained in the list of 'REPOSITORIES' as defined in +'etc/build.conf', simply issue 'make'. This way, all components that are +compatible with the build directory's base platform will be built. In practice, +however, only some of those components may be of interest. Hence, the build +can be tailored to those components which are of actual interest by specifying +source-code subtrees. For example, using the following command +! make core server/nitpicker +the build system builds all targets found in the 'core' and 'server/nitpicker' +source directories. You may specify any number of subtrees to the build +system. As indicated by the build output, the build system revisits +each library that is used by each target found in the specified subtrees. +This is very handy for developing libraries because instead of re-building +your library and then your library-using program, you just build your program +and that's it. This concept even works recursively, which means that libraries +may depend on other libraries. + +In practice, you won't ever need to build the _whole tree_ but only the +targets that you are interested in. + + +Cleaning the build directory +============================ + +To remove all but kernel-related generated files, use +! make clean + +To remove all generated files, use +! make cleanall + +Both 'clean' and 'cleanall' won't remove any files from the 'bin/' +subdirectory. This makes the 'bin/' a safe place for files that are +unrelated to the build process, yet required for the integration stage, e.g., +binary data. + + +Controlling the verbosity of the build process +============================================== + +To understand the inner workings of the build process in more detail, you can +tell the build system to display each directory change by specifying + +! make VERBOSE_DIR= + +If you are interested in the arguments that are passed to each invocation of +'make', you can make them visible via + +! make VERBOSE_MK= + +Furthermore, you can observe each single shell-command invocation by specifying + +! make VERBOSE= + +Of course, you can combine these verboseness toggles for maximizing the noise. + + +Enabling parallel builds +======================== + +To utilize multiple CPU codes during the build process, you may invoke 'make' +with the '-j' argument. If manually specifying this argument becomes an +inconvenience, you may add the following line to your 'etc/build.conf' file: + +! MAKE += -j + +This way, the build system will always use '' CPUs for building. + + +Caching inter-library dependencies +================================== + +The build system allows to repeat the last build without performing any +library-dependency checks by using: + +! make again + +The use of this feature can significantly improve the work flow during +development because in contrast to source-codes, library dependencies rarely +change. So the time needed for re-creating inter-library dependencies at each +build can be saved. + + +Repository directory layout +########################### + +Each Genode repository has the following layout: + + Directory | Description + ------------------------------------------------------------ + 'doc/' | Documentation, specific for the repository + ------------------------------------------------------------ + 'etc/' | Default configuration of the build process + ------------------------------------------------------------ + 'mk/' | The build system + ------------------------------------------------------------ + 'include/' | Globally visible header files + ------------------------------------------------------------ + 'src/' | Source codes and target build descriptions + ------------------------------------------------------------ + 'lib/mk/' | Library build descriptions + + +For each custom source-code repository supplied to the build system, the +following subdirectories are mandatory: + +! lib/mk/ +! src/ +! include/ + + +Creating targets and libraries +############################## + +Target descriptions +=================== + +A good starting point is to look at the init target. The source code of init is +located at 'os/src/init/'. In this directory, you will find a target description +file named 'target.mk'. This file contains the building instructions and it is +usually is very simple. The build process is controlled by defining the +following variables. + + +Build variables to be defined by you +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:'TARGET': is the name of the binary to be created. This is the + only *mandatory variable* to be defined in a 'target.mk' file. + +:'REQUIRES': expresses the requirements that must be satisfied in order to + build the target. You find more details about the underlying mechanism in + Section [Specializations]. + +:'LIBS': is the list of libraries that are used by the target. + +:'SRC_CC': contains the list of '.cc' source files. The default search location + for source codes is the directory, where the 'target.mk' file resides. + +:'SRC_C': contains the list of '.c' source files. + +:'SRC_S': contains the list of assembly '.s' source files. + +:'SRC_BIN': contains binary data files to be linked to the target. + +:'INC_DIR': is the list of include search locations. Directories should + always be appended by using +=. Never use an assignment! + +:'EXT_OBJECTS': is a list of Genode-external objects or libraries. This + variable is mostly used for interfacing Genode with legacy software + components. + + +Rarely used variables +--------------------- + +:'CC_OPT': contains additional compiler options to be used for '.c' as + well as for '.cc' files. + +:'CC_CXX_OPT': contains additional compiler options to be used for the + C++ compiler only. + +:'CC_C_OPT': contains additional compiler options to be used for the + C compiler only. + + +Specifying search locations +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When specifying search locations for header files via the 'INC_DIR' variable or +for source files via 'vpath', relative pathnames are illegal to use. Instead, +you can use the following variables to reference locations within the +source-code repository, where your target lives: + +:'REP_DIR': is the base directory of the current source-code repository. + Normally, specifying locations relative to the base of the repository is + never used by 'target.mk' files but needed by library descriptions. + +:'PRG_DIR': is the directory, where your 'target.mk' file resides. This + variable is always to be used when specifying a relative path. + + +Library descriptions +==================== + +In contrast to target descriptions that are scattered across the whole source +tree, library descriptions are located at the central place 'lib/mk'. Each +library corresponds to a '.mk' file. The base of the description file +is the name of the library. Therefore, no 'TARGET' variable needs to be set. +The source-code locations are expressed as '$(REP_DIR)'-relative 'vpath' +commands. + +Library-description files support the following additional declarations: + +:'SHARED_LIB = yes': declares that the library should be built as a shared + object rather than a static library. The resulting object will be called + '.lib.so'. + + +Specializations +=============== + +Building components for different platforms likely implicates portions of code +that are tied to certain aspects of the target platform. For example, a target +platform may be characterized by + +* A kernel API such as L4v2, Linux, L4.sec, +* A hardware architecture such as x86, ARM, Coldfire, +* A certain hardware facility such as a custom device, or +* Other properties such as software license requirements. + +Each of these attributes express a specialization of the build process. The +build system provides a generic mechanism to handle such specializations. + +The _programmer_ of a software component knows the properties on which his +software relies and thus, specifies these requirements in his build description +file. + +The _user/customer/builder_ decides to build software for a specific platform +and defines the platform specifics via the 'SPECS' variable per build +directory in 'etc/specs.conf'. In addition to an (optional) 'etc/specs.conf' +file within the build directory, the build system incorporates the first +'etc/specs.conf' file found in the repositories as configured for the +build directory. For example, for a 'linux_x86' build directory, the +'base-linux/etc/specs.conf' file is used by default. The build directory's +'specs.conf' file can still be used to extend the 'SPECS' declarations, for +example to enable special features. + +Each '' in the 'SPECS' variable instructs the build system to + +* Include the 'make'-rules of a corresponding 'base/mk/spec-.mk' + file. This enables the customization of the build process for each platform. + +* Search for '.mk' files in the 'lib/mk//' subdirectory. + This way, we can provide alternative implementations of one and the same + library interface for different platforms. + +Before a target or library gets built, the build system checks if the 'REQUIRES' +entries of the build description file are satisfied by entries of the 'SPECS' +variable. The compilation is executed only if each entry in the 'REQUIRES' +variable is present in the 'SPECS' variable as supplied by the build directory +configuration. + + +Automated integration and testing +################################# + +Genode's cross-kernel portability is one of the prime features of the +framework. However, each kernel takes a different route when it comes to +configuring, integrating, and booting the system. Hence, for using a particular +kernel, profound knowledge about the boot concept and the kernel-specific tools +is required. To streamline the testing of Genode-based systems across the many +different supported kernels, the framework comes equipped with tools that +relieve you from these peculiarities. + +Run scripts +=========== + +Using so-called run scripts, complete Genode systems can be described in a +concise and kernel-independent way. Once created, a run script can be used +to integrate and test-drive a system scenario directly from the build directory. +The best way to get acquainted with the concept is reviewing the run script +for the 'hello_tutorial' located at 'hello_tutorial/run/hello.run'. +Let's revisit each step expressed in the 'hello.run' script: + +* Building the components needed for the system using the 'build' command. + This command instructs the build system to compile the targets listed in + the brace block. It has the same effect as manually invoking 'make' with + the specified argument from within the build directory. + +* Creating a new boot directory using the 'create_boot_directory' command. + The integration of the scenario is performed in a dedicated directory at + '/var/run//'. When the run script is finished, + this directory will contain all components of the final system. In the + following, we will refer to this directory as run directory. + +* Installing the Genode 'config' file into the run directory using the + 'install_config' command. The argument to this command will be written + to a file called 'config' at the run directory picked up by + Genode's init process. + +* Creating a bootable system image using the 'build_boot_image' command. + This command copies the specified list of files from the '/bin/' + directory to the run directory and executes the platform-specific steps + needed to transform the content of the run directory into a bootable + form. This form depends on the actual base platform and may be an ISO + image or a bootable ELF image. + +* Executing the system image using the 'run_genode_until' command. Depending + on the base platform, the system image will be executed using an emulator. + For most platforms, Qemu is the tool of choice used by default. On Linux, + the scenario is executed by starting 'core' directly from the run + directory. The 'run_genode_until' command takes a regular expression + as argument. If the log output of the scenario matches the specified + pattern, the 'run_genode_until' command returns. If specifying 'forever' + as argument (as done in 'hello.run'), this command will never return. + If a regular expression is specified, an additional argument determines + a timeout in seconds. If the regular expression does not match until + the timeout is reached, the run script will abort. + +Please note that the 'hello.run' script does not contain kernel-specific +information. Therefore it can be executed from the build directory of any base +platform by using: + +! make run/hello + +When invoking 'make' with an argument of the form 'run/*', the build system +will look in all repositories for a run script with the specified name. The run +script must be located in one of the repositories 'run/' subdirectories and +have the file extension '.run'. + +For a more comprehensive run script, 'os/run/demo.run' serves as a good +example. This run script describes Genode's default demo scenario. As seen in +'demo.run', parts of init's configuration can be made dependent on the +platform's properties expressed as spec values. For example, the PCI driver +gets included in init's configuration only on platforms with a PCI bus. For +appending conditional snippets to the 'config' file, there exists the 'append_if' +command, which takes a condition as first and the snippet as second argument. +To test for a SPEC value, the command '[have_spec ]' is used as +condition. Analogously to how 'append_if' appends strings, there exists +'lappend_if' to append list items. The latter command is used to conditionally +include binaries to the list of boot modules passed to the 'build_boot_image' +command. + + +The run mechanism explained +=========================== + +Under the hood, run scripts are executed by an expect interpreter. When the +user invokes a run script via 'make run/', the build system invokes +the run tool at '/tool/run' with the run script as argument. The +run tool is an expect script that has no other purpose than defining several +commands used by run scripts, including a platform-specific script snippet +called run environment ('env'), and finally including the actual run script. +Whereas 'tool/run' provides the implementations of generic and largely +platform-independent commands, the 'env' snippet included from the platform's +respective 'base-/run/env' file contains all platform-specific +commands. For reference, the most simplistic run environment is the one at +'base-linux/run/env', which implements the 'create_boot_directory', +'install_config', 'build_boot_image', and 'run_genode_until' commands for Linux +as base platform. For the other platforms, the run environments are far more +elaborative and document precisely how the integration and boot concept works +on each platform. Hence, the 'base-/run/env' files are not only +necessary parts of Genode's tooling support but serve as resource for +peculiarities of using each kernel. + + +Using run script to implement test cases +======================================== + +Because run scripts are actually expect scripts, the whole arsenal of +language features of the Tcl scripting language is available to them. This +turns run scripts into powerful tools for the automated execution of test +cases. A good example is the run script at 'libports/run/lwip.run', which tests +the lwIP stack by running a simple Genode-based HTTP server on Qemu. It fetches +and validates a HTML page from this server. The run script makes use of a +regular expression as argument to the 'run_genode_until' command to detect the +state when the web server becomes ready, subsequently executes the 'lynx' shell +command to fetch the web site, and employs Tcl's support for regular +expressions to validate the result. The run script works across base platforms +that use Qemu as execution environment. + +To get the most out of the run mechanism, a basic understanding of the Tcl +scripting language is required. Furthermore the functions provided by +'tool/run' and 'base-/run/env' should be studied. + + +Automated testing across base platforms +======================================= + +To execute one or multiple test cases on more than one base platform, there +exists a dedicated tool at 'tool/autopilot'. Its primary purpose is the +nightly execution of test cases. The tool takes a list of platforms and of +run scripts as arguments and executes each run script on each platform. The +build directory for each platform is created at +'/tmp/autopilot./' and the output of each run script is +written to a file called '..log'. On stderr, autopilot +prints the statistics about whether or not each run script executed +successfully on each platform. If at least one run script failed, autopilot +returns a non-zero exit code, which makes it straight forward to include +autopilot into an automated build-and-test environment. + + diff --git a/doc/coding_style.txt b/doc/coding_style.txt new file mode 100644 index 000000000..647f77bec --- /dev/null +++ b/doc/coding_style.txt @@ -0,0 +1,267 @@ +Coding style guidelines for Genode +################################## + +Things to avoid +=============== + +Please avoid using pre-processor macros. C++ provides language +features for almost any case, for which a C programmer uses +macros. + +:Defining constants: + + Use 'enum' instead of '#define' + ! enum { MAX_COLORS = 3 }; + ! enum { + ! COLOR_RED = 1, + ! COLOR_BLUE = 2, + ! COLOR_GREEN = 3 + ! }; + +:Meta-programming: + + Use templates instead of pre-processor macros. + In contrast to macros, templates are type-safe + and fit well with the implementation syntax. + +:Conditional-code inclusion: + + Please avoid C-hacker style '#ifdef CONFIG_PLATFROM' - '#endif' + constructs but instead, factor-out the encapsulated code into a + separate file and introduce a proper function interface. + The build process should then be used to select the appropriate + platform-specific files at compile time. Keep platform dependent + code as small as possible. Never pollute existing generic code + with platform-specific code. + + +Header of each file +=================== + +! /* +! * \brief Short description of the file +! * \author Original author +! * \date Creation date +! * +! * Some more detailed description. This is optional. +! */ + + +Identifiers +=========== + +* First character of class names uppercase, any other characters lowercase +* Function and variable names lower case +* 'Multi_word_identifiers' via underline +* 'CONSTANTS' upper case +* Private and protected members of a class begin with an '_'-character +* Accessor functions are named after their corresponding attributes: + + ! /** + ! * Request private member variable + ! */ + ! int value() { return _value; } + ! + ! /** + ! * Set the private member variable + ! */ + ! void value(int value) { _value = value; } + + +Indentation +=========== + +* Use one tab per indentation step. *Do not mix tabs and spaces!* +* Use no tabs except at the beginning of a line. +* Use spaces for alignment + +See [http://web.archive.org/web/20050311153439/http://electroly.com/mt/archives/000002.html] +for a more detailed description. + +This way, everyone can set his preferred tabsize in his editor +and the source code always looks good. + + +Switch statements +~~~~~~~~~~~~~~~~~ + +Switch-statement blocks should be indented as follows: + +! switch (color) { +! +! case BLUE: +! break; +! +! case GREEN: +! { +! int declaration_required; +! ... +! } +! +! default: +! } + +Please note that the case labels have the same indentation +level as the switch statement. This avoids a two-level +indentation-change at the end of the switch block that +would occur otherwise. + + +Vertical whitespaces +==================== + +In header files: + +* Leave two empty lines between classes. +* Leave one empty line between member functions. + +In implementation files: + +* Leave two empty lines between functions. + + +Braces +====== + +* Braces after class, struct and function names are placed at a new line: + ! class Foo + ! { + ! public: + ! + ! void function(void) + ! { + ! ... + ! } + ! }; + + except for single-line functions. + +* All other occurrences of open braces (for 'if', 'while', 'do', 'for', + 'namespace', 'enum' etc.) are at the end of a line: + + ! if (flag) { + ! .. + ! } else { + ! .. + ! } + +* Surprisingly, one-line functions should be written on one line. + Typically, this applies for accessor functions. + If slightly more space than one line is needed, indent as follows: + + ! int heavy_computation(int a, int lot, int of, int args) { + ! return a + lot + of + args; } + + +Comments +======== + +Function header +~~~~~~~~~~~~~~~ + +Each public or protected (but no private) function in a header-file should be +prepended by a header as follows: + +! /** +! * Short description +! * +! * \param a meaning of parameter a +! * \param b meaning of parameter b +! * \param c,d meaning of parameters c and d +! * +! * \return meaning of return value +! * \retval 0 meaning of the return value 0 +! * +! * More detailed information about the function. This is optional. +! */ + +Descriptions of parameters and return values should be lower-case and brief. +More elaborative descriptions can be documented in the text area below. + +In implementation files, only local and private functions should feature +function headers. + + +Single-line comments +~~~~~~~~~~~~~~~~~~~~ + +! /* use this syntax for single line comments */ + +A single-line comment should be prepended by an empty line. +Single-line comments should be short - no complete sentences. Use lower-case. + +C++-style comments ('//') should only be used for temporarily commenting-out +code. Such commented-out garbage is easy to 'grep' and there are handy +'vim'-macros available for creating and removing such comments. + + +Variable descriptions +~~~~~~~~~~~~~~~~~~~~~ + +Use the same syntax as for single-line comments. Insert two or more +spaces before your comment starts. + +! int size; /* in kilobytes */ + + +Multi-line comments +~~~~~~~~~~~~~~~~~~~ + +Multi-line comments are more detailed descriptions in the form of +sentences. +A multi-line comment should be enclosed by empty lines. + +! /* +! * This is some tricky +! * algorithm that works +! * as follows: +! * ... +! */ + +The first and last line of a multi-line comment contain no words. + + +Source-code blocks +~~~~~~~~~~~~~~~~~~ + +For structuring your source code, you can entitle the different +parts of a file like this: + +! <- two empty lines +! +! /******************** +! ** Event handlers ** +! ********************/ +! <- one empty line + +Note the two stars at the left and right. There are two of them to +make the visible width of the border match its height (typically, +characters are ca. twice as high as wide). + +A source-code block header represents a headline for the following +code. To couple this headline with the following code closer than +with previous code, leave two empty lines above and one empty line +below the source-code block header. + + +Order of public, protected, and private blocks +============================================== + +For consistency reasons, use the following class layout: + +! class Sandstein +! { +! private: +! ... +! protected: +! ... +! public: +! }; + +Typically, the private section contains member variables that are used +by public accessor functions below. In this common case, we only reference +symbols that are defined above as it is done when programming plain C. + +Leave one empty line (or a line that contains only a brace) above and below +a 'private', 'protected', or 'public' label. This also applies when the +label is followed by a source-code block header. diff --git a/doc/components.txt b/doc/components.txt new file mode 100644 index 000000000..03307e5e5 --- /dev/null +++ b/doc/components.txt @@ -0,0 +1,357 @@ + + + ========================== + Genode components overview + ========================== + + Norman Feske + + +Abstract +######## + +Genode comes with a growing number of components apparently scattered across +various repositories. This document provides an overview of these components +and outlines the systematics behind them. + + +Categorization of components +############################ + +Genode components usually fall into one of four categories, namely device +drivers, resource multiplexers, protocol stacks, and applications. Each +of them is briefly characterized as follows: + +:Device drivers: translate hardware resources into device-independent + session interfaces. Naturally, a device driver is specific to a + particular hardware platform. The hardware resources are accessed + via core's IO_MEM, IO_PORT, and IRQ services. The functionality of + the driver is made available to other system components by announcing + one of Genode's device-independent session interfaces, which are + 'pci_session', 'framebuffer_session', 'input_session', 'block_session', + 'audio_out_session', 'log_session', 'nic_session', and 'timer_session' + (see 'os/include/' for the interface definitions). Those interfaces are + uniform across hardware platforms and kernel base platforms. Usually, + each device driver can accommodate only one client at a time. + +:Resource multiplexers: provide mechanisms to multiplex device resources + to multiple clients. A typical resource multiplexer requests one + of Genode's device-independent session interface (usually connected + to a device driver) and, in turn, announces a service of the same kind. + However, in contrast to a device driver, a resource multiplexer is able + to serve more than one client at the same time. + +:Protocol stacks: translate low-level interfaces to higher-level + interfaces (or sometimes vice versa). Typically, a protocol stack comes + in the form of a library, which uses a device-independent session + interface as back end and provides a high-level library interface as + front end. However, protocol stacks also exist in the form of + distinct components that implement translations between different + session interfaces. + +:Applications: implement functionality using APIs as provided by + protocol stacks. + +:Runtime environments: enable existing 3rd-party software to be executed + as a Genode sub systems. + + +Device drivers +############## + +Device drivers usually reside in the 'src/drivers' subdirectory of source-code +repositories. The most predominant repositories hosting device drivers are +'os', 'linux_drivers', 'dde_ipxe'. + + +Platform devices +================ + +:'os/src/drivers/pci': + Implements the PCI-session interface using the PCI controller as found on + x86 PC hardware. Using this interface, a client can probe for a particular + device and request information about physical device resources (using the + 'pci_device' interface). These information are subsequently used to request + respective IO_MEM, IRQ, and IO_PORT sessions at core. + + +UART devices +============ + +The UART device drivers implement the terminal-session interface. + +:'os/src/drivers/uart/pl011': + Driver for the PL011 UART as found on many ARM-based platforms. + +:'os/src/drivers/uart/i8250': + Driver for the i8250 UART as found on PC hardware. + + +Framebuffer and input drivers +============================= + +Framebuffer and input drivers implement the framebuffer-session interface and +input-session interfaces respectively. + +:'os/src/drivers/input/dummy': + Pseudo input driver without accessing any hardware. This component is useful + to resolve a dependency from an input session for scenarios where no user + input is required. + +:'os/src/drivers/input/fiasco_ux': + Driver for the virtual hardware provided by the user-mode version of the + Fiasco kernel. + +:'os/src/drivers/input/ps2/x86': + Driver for the 'i8042' PS/2 controller as found in x86 PCs. It supports both + mouse (including ImPS/2, ExPS/2) and keyboard. + +:'os/src/drivers/input/ps2/pl050': + Driver for the PL050 PS/2 controller as found on ARM platforms such as + VersatilePB. The physical base address used by the driver is obtained at + compile time from a header file called 'pl050_defs.h'. The version of the + VersatilePB platform can be found at 'os/include/platform/vpb926/' and + is made available to the driver via the SPECS machinery of the Genode build + system. + +:'os/src/drivers/framebuffer/vesa': + Driver using VESA mode setting on x86 PCs. For more information, please refer + to the README file in the driver directory. + +:'os/src/drivers/framebuffer/pl11x': + Driver for the PL110/PL111 LCD display. + +:'os/src/drivers/framebuffer/sdl': + Serves as both framebuffer and input driver on Linux using libSDL. This + driver is only usable on the Linux base platform. + +:'os/src/drivers/framebuffer/fiasco_ux': + Driver for the virtual framebuffer device provided by the user-mode Fiasco + kernel. + +:'linux_drivers/src/drivers/usb': + USB driver that makes USB HID devices available as input sessions. + For an example of using this driver, refer to the run script at + 'linux_drivers/run/usb_hid'. + + +Timer drivers +============= + +All timer drivers implement the timer-session interface. Technically, a timer +driver is both a device driver (accessing a timer device) and a resource +multiplexer (supporting multiple timer-session clients at the same time). The +timer implementations differ in their use of different time sources and the +mode of internal operation of the timer sessions. Time sources are either +hardware timers, a time source provided by the kernel, or a pseudo time source +(busy). + +The internal operation of the timer session depends on the kernel. On kernels +with support for out-of-order RPCs, all timer sessions are handled by a single +thread. Otherwise, each timer session uses a dedicated thread. + +:'timer/nova': PIT as time source, multi-threaded +:'timer/codezero': busy time source, single-threaded +:'timer/okl4_arm': busy time source, single-threaded +:'timer/okl4_x86': PIT as time source, single-threaded +:'timer/foc': IPC timeout as time source, multi-threaded +:'timer/fiasco': IPC timeout as time source, single-threaded +:'timer/pistachio': IPC timeout as time source, single-threaded +:'timer/linux': nanosleep as time source, single-threaded + + +Audio output drivers +==================== + +All audio-output drivers implement the audio session interface defined at +'os/include/audio_out_session/'. + +:'os/src/drivers/audio_out/linux': + Uses ALSA as back-end on the Linux base platform. + +:'linux_drivers/src/drivers/audio_out': + Sound drivers for the most common PC sound hardware, ported from the Linux + kernel. + + +Block drivers +============= + +All block drivers implement the block-session interface defined at +'os/include/block_session/'. + +:'os/src/drivers/atapi': + Driver for ATAPI CD-ROM devices on x86 PCs. + +:'os/src/drivers/sd_card': + Driver for SD-cards connected via the PL180 device as found on the PBX-A9 + platform. + +:'linux_drivers/src/drivers/usb': + USB driver that makes USB storage devices available as block sessions. + For an example of using this driver, refer to the run script at + 'linux_drivers/run/usb_storage'. + +:'os/src/drivers/ahci': + Driver for SATA disks on x86 PCs. + + +Network interface drivers +========================= + +All network interface drivers implement the NIC session interface +defined at 'os/include/nic_session'. + +:'os/src/drivers/nic/linux': + Driver that uses a Linux tap device as back end. It is only useful on the + Linux base platform. + +:'os/src/drivers/nic/lan9118': + Native device driver for the LAN9118 network adaptor as featured on the + PBX-A9 platform. + +:'dde_ipxe/src/drivers/nic': + Device drivers ported from the iPXE project. Supported devices are Intel + E1000 and pcnet32. + +:'linux_drivers/src/drivers/madwifi': + The MadWifi wireless stack ported from the Linux kernel. + + +Resource multiplexers +##################### + +By convention, resource multiplexers are located at the 'src/server' +subdirectory of a source repository. + +:Framebuffer and input: The framebuffer and input session interfaces can be + multiplexed using the Nitpicker GUI server, which allows multiple clients to + create and manage rectangular areas on screen. Nitpicker uses one input + session and one framebuffer session as back end and, in turn, provides + so-called nitpicker sessions to one or multiple clients. Each nitpicker + session contains a virtual framebuffer and a virtual input session. Nitpicker + (including a README file) is located at 'os/src/server/nitpicker'. + +:Audio output: The audio mixer located at 'os/src/server/mixer' enables + multiple clients to use the audio-out interface. The mixing is done by simply + adding and clamping the signals of all present clients. + +:Networking: The NIC bridge located at 'os/src/server/nic_bridge' multiplexes + one NIC session to multiple virtual NIC sessions using a proxy-ARP + implementation. Each client has to obtain a dedicated IP address visible to + the physical network. DHCP requests originating from the virtual NIC sessions + are delegated to the physical network. + +:Block: The block-device partition server at 'os/src/server/part_blk' reads + the partition table of a block session and exports each partition found as + separate block session. For using this server, please refer to the run + script at 'os/run/part_blk'. + + +Protocol stacks +############### + +Protocol stacks come either in the form of separate components that translate +one session interface to another, or in the form of libraries. + +Separate components: + +:'os/src/server/nit_fb': + Translates a nitpicker session to a pair of framebuffer and input sessions. + Each 'nit_fb' instance is visible as a rectangular area on screen presenting + a virtual frame buffer. The area is statically positioned. For more + information, please refer to 'os/src/server/nit_fb/README'. + +:'demo/src/server/liquid_framebuffer': + Implements the same translation as 'nit_fb' but by presenting an interactive + window rather than a statically positioned screen area. + +:'os/src/server/tar_rom': + Provides each file contained in a tar file obtained via Genode's ROM session + as separate ROM session. + +:'os/src/server/iso9660': + Provides each file of an ISO9660 file system accessed via a block session as + separate ROM session. + +:'os/src/server/rom_loopdev': + Provides the content of a ROM file as a block session, similar to the + loop-mount mechanism on Linux + +:'demo/src/server/nitlog': + Provides a LOG session, printing log output on screen via a nitpicker + session. + +Libraries: + +:'libports/lib/mk/libc_log': + Redirects the standard output of the libc to Genode's LOG session interface. + +:'libports/lib/mk/libc_lwip_nic_dhcp': + Translates the BSD socket API to a NIC session using the lwIP stack. + +:'libports/lib/mk/gallium' + 'linux_drivers/lib/mk/gpu_i915_drv': + Translates the OpenGL API to a framebuffer session using the MESA OpenGL + stack and the Intel GEM GPU driver. + +:'libports/lib/mk/sdl': + Translates the libSDL API to framebuffer and input sessions. + +:'qt4': + Qt4 framework, using nitpicker session and NIC session as back end. + + +Applications +############ + +Applications are Genode components that use other component's services but +usually do not provide services. They are typically located in the 'src/app/' +subdirectory of a repository. Most applications come with README files +located in their respective directory. + +:'demo/src/app/backdrop': + Nitpicker client application that sets a PNG image as desktop background. + +:'demo/src/app/launchpad': + Graphical application for interactively starting and killing subsystems. + +:'demo/src/app/scout': + Graphical hypertext browser used for Genode's default demonstration scenario. + +:'libports/src/app/eglgears': + Example program for using OpenGL via the Gallium3D graphics stack. + +:'ports/src/app/arora': + Arora is a Qt4-based web browser using the Webkit engine. + +:'ports/src/app/gdb_monitor': + Application that allows the debugging of a process via GDB over a remote + connection. + +:'qt4/src/app/qt_launchpad': + Graphical application starter implemented using Qt4. + +:'qt4/src/app/examples/': + Several example applications that come with Qt4. + +:'os/src/app/xvfb': + Is a proxy application that enables the integration of a virtual X server + into a Nitpicker session on the Linux base platform. + + +Runtime environments +#################### + +:'ports/src/noux': Noux is an experimental implementation of a UNIX-like API + that enables the use of unmodified command-line based GNU software. For using + noux, refer to the run script 'ports/run/noux.run'. + +:'ports-okl4/src/oklinux': OKLinux is a paravirtualized Linux kernel that + enables the use of Linux-based OSes as subsystems on the OKL4 kernel. For + using OKLinux, refer to the run script 'ports-okl4/run/lx_block.run'. + +:'ports-foc/src/l4linux': L4Linux is a paravirtualized Linux kernel that + enables the use of Linux-based OSes as subsystems on the Fiasco.OC kernel. + For using L4Linux, refer to the run script 'ports-foc/run/l4linux.run'. + + diff --git a/doc/conventions.txt b/doc/conventions.txt new file mode 100644 index 000000000..3963c74e6 --- /dev/null +++ b/doc/conventions.txt @@ -0,0 +1,78 @@ + + Conventions for the Genode development + + + Norman Feske + + +Documentation +############# + +We use the GOSH syntax +[http://os.inf.tu-dresden.de/~nf2/files/GOSH/current/gosh.txt] +for documentation and README files. + + +README files +############ + +Each directory should contain a file called 'README' that briefly explains +what the directory is about. In 'doc/Makefile' is a rule for +generating a directory overview from the 'README' files automatically. + +You can structure your 'README' file by using the GOSH style for subsections: +! Subsection +! ~~~~~~~~~~ +Do not use chapters or sections in your 'README' files. + + +Filenames +######### + +All normal filenames are lowercase. Filenames should be chosen to be +expressive. Someone who explores your files for the first time might not +understand what 'mbi.cc' means but 'multiboot_info.cc' would ring a bell. If a +filename contains multiple words, use the '_' to separate them (instead of +'miscmath.h', use 'misc_math.h'). + + +Coding style +############ + +A common coding style helps a lot to ease collaboration. The official coding +style of the Genode base components is described in 'doc/coding_style.txt'. +If you consider working closely together with the Genode main developers, +your adherence to this style is greatly appreciated. + + +Include files and RPC interfaces +################################ + +Never place include files directly into the '/include/' directory +but use a meaningful subdirectory that corresponds to the component that +provides the interfaces. + +Each RPC interface is represented by a separate include subdirectory. For +an example, see 'base/include/ram_session/'. The headerfile that defines +the RPC function interface has the same base name as the directory. The RPC +stubs are called 'client.h' and 'server.h'. If your interface uses a custom +capability type, it is defined in 'capability.h'. Furthermore, if your +interface is a session interface of a service, it is good practice to +provide a connection class in a 'connection.h' file for managing session- +construction arguments and the creation and destruction of sessions. + +Specialization-dependent include directories are placed in 'include//'. + + +Service Names +############# + +Service names as announced via the 'parent()->announce()' function follow +the following convention: + +Core's services, which are the most fundamental base services are written +completely upper case. Each developer should be aware of the meaning of the +used acronyms such as RAM, RM, ROM. All other service names should be +descriptive names rather than acronyms. Service names should contain only +letters, numbers, and underline characters. The first character must +always be an uppercase letter, all other characters are lowercase. diff --git a/doc/future_optimizations.txt b/doc/future_optimizations.txt new file mode 100644 index 000000000..86c74cdee --- /dev/null +++ b/doc/future_optimizations.txt @@ -0,0 +1,68 @@ + + Future optimizations of Genode + + Norman Feske + +Abstract +######## + +This document outlines possible optimizations for Genode. +In the first place, Genode was meant as a feasibility study. +Therefore, optimizing performance and memory-consumption was +not a primary goal of the experiment. However, there exist +several ideas to improve these properties. These ideas are +definitely not to be regarded as ToDo items. Each idea should +only be conducted if it fixes a _real_ hotspot in the system +that was found by quantitative analysis. + + +Memory consumption +################## + +Currently, we use an AVL-tree-based best-fit memory allocator +('Allocator_avl') as Heap. Despite, this allocator was +intended to be used only for the root allocator for physical +memory inside Core, we use it as the basis for the current +heap implementation. This way, we can reuse the code, +reducing the overall code complexity. + +The AVL-tree-based allocator uses a meta-data entry of 32 +bytes per free or used memory block. This means that each +memory allocation from the heap introduces a meta-data +overhead of 32 up to 64 bytes. When allocating a lot of +small objects, this overhead is very high. + +:Question:: Is this issue a real bottleneck? + +Possible improvements are: + +* Using slab allocators for known allocation sizes. + Slab entries have a much smaller footprint. +* Creating a list-based allocator implementation for + the heap. This comes at the cost of additional + code complexity. + + +RPC Performance +############### + +We use C++ streams for RPC communication and therefore, +introduced run-time overhead for the dynamic marshaling. +Using an IDL compiler would improve the RPC performance. +Is the RPC performance a real performance problem? What +is the profile of RPC usage? (Number of RPCs per second, +Percentage of CPU time spent on RPCs, Secondary effects +for RPCs such as cache and TLB pollution). + + +Locking +####### + +On L4v2-Genode, locking is implemented via yielding spin locks. +We may consider a L4env-like lock implementation. + + +Misc +#### + +Take a look at include/util/string.h and judge by yourself :-) diff --git a/doc/getting_started.txt b/doc/getting_started.txt new file mode 100644 index 000000000..ff367df7b --- /dev/null +++ b/doc/getting_started.txt @@ -0,0 +1,116 @@ + + ============================= + How to start exploring Genode + ============================= + + Norman Feske + + +Abstract +######## + +This guide is meant to provide you a painless start with using the Genode OS +Framework. It explains the steps needed to get a simple demo system running +on Linux first, followed by the instructions on how to run the same scenario +on a microkernel. + + +Quick start to build Genode for Linux +##################################### + +The best starting point for exploring Genode is to run it on Linux. Make sure +that your system satisfies the following requirements: + +* GNU 'Make' version 3.81 or newer +* 'libSDL-dev' +* 'tclsh' and 'expect' +* 'byacc' (only needed for the L4/Fiasco kernel) +* 'qemu' and 'genisoimage' (for testing non-Linux platforms via Qemu) + +Furthermore, you will need to install the official Genode toolchain, which +you can download at [http://genode.org/download/tool-chain]. + +The Genode build system never touches the source tree but generates object +files, libraries, and programs in a dedicated build directory. We do not have a +build directory yet. For a quick start, let us create one for the Linux base +platform: + +! cd +! ./tool/create_builddir linux_x86 BUILD_DIR=build.lx + +The new build directory is called 'build.lx' and configured for the 'linux_x86' +platform. To give Genode a try, build and execute a simple demo scenario via: + +! cd build.lx +! make run/demo + +By invoking 'make' with the 'run/demo' argument, all components needed by the +demo scenario are built and the demo is executed. If you are interested in +looking behind the scenes of the demo scenario, please refer to +'doc/build_system.txt' and the run script at 'os/run/demo.run'. + + +Using platforms other than Linux +================================ + +Running Genode on Linux is the most convenient way to get acquainted with the +framework. However, the point where Genode starts to shine is when used as the +user land executed on a microkernel. The framework supports a variety of +different kernels such as L4/Fiasco, L4ka::Pistachio, OKL4, and NOVA. Those +kernels largely differ in terms of feature sets, build systems, tools, and boot +concepts. To relieve you from dealing with those peculiarities, Genode provides +you with an unified way of using them. For each kernel platform, there exists +a dedicated directory called 'base-'. Within this directory, you will +find a 'Makefile', which automates the task of downloading the source codes of +the kernel and interfacing the kernel with Genode. Just change to the +respective 'base-' directory and issue: + +! make prepare + +Note that each 'base-' directory comes with a 'README' file, which +you should revisit first when exploring the base platform. Additionally, most +'base-' directories provide more in-depth information within their +respective 'doc/' subdirectories. + +Now that the base platform is prepared, the 'create_builddir' tool can be used +to create a build directory for your platform of choice by giving the platform +as argument. To see the list of available platforms, execute 'create_builddir' +with no arguments. + +For example, to give the demo scenario a spin on the OKL4 kernel, the following +steps are required: + +# Download the kernel: + ! cd + ! make -C base-okl4 prepare +# Create a build directory + ! ./tool/create_builddir okl4_x86 BUILD_DIR=build.okl4 +# Build and execute the demo using Qemu + ! make -C build.okl4 run/demo + +The procedure works analogously for the other base platforms. + + +How to proceed with exploring Genode +#################################### + +Now that you have taken the first steps into using Genode, you may seek to +get more in-depth knowledge and practical experience. The foundation for doing +so is a basic understanding of the build system. The documentation at +'build_system.txt' provides you with the information about the layout of the +source tree, how new components are integrated, and how complete system +scenarios can be expressed. Equipped with this knowledge, it is time to get +hands-on experience with creating custom Genode components. A good start is the +'hello_tutorial', which shows you how to implement a simple client-server +scenario. To compose complex scenarios out of many small components, the +documentation of the Genode's configuration concept at 'os/doc/init.txt' is an +essential reference. + +Certainly, you will have further questions on your way with exploring Genode. +The best place to get these questions answered is the Genode mailing list. +Please feel welcome to ask your questions and to join the discussions: + +:Genode Mailing Lists: + + [http://genode.org/community/mailing-lists] + diff --git a/doc/release_notes-08-11.txt b/doc/release_notes-08-11.txt new file mode 100644 index 000000000..0e78089b8 --- /dev/null +++ b/doc/release_notes-08-11.txt @@ -0,0 +1,830 @@ + + + ============================================== + Release notes for the Genode OS Framework 8.11 + ============================================== + + Genode Labs + +Summary +####### + +This document presents the new features and major changes introduced +in version 8.11 of the Genode OS Framework. It is geared towards +people interested in closely following the progress of the Genode +project and to developers who want to adopt their software to our +mainline development. The document aggregates important fragments +of the updated documentation such that you won't need to scan existing +documents for the new bits. Furthermore, it attempts to provide our +rationale behind the taken design decisions. + +The general theme for the release 8.11 is enabling the use of the +Genode OS framework for real-world applications. Because we regard +the presence of device drivers and a way to reuse existing library +code as fundamental prerequisites for achieving this goal, the major +new additions are an API for device drivers written in C, an API for +handling asynchronous notifications, and a C runtime. Other noteworthy +improvements are the typification of capabilities at the C++-language +level, a way for receiving and handling application faults, the +introduction of managed dataspaces, and a new API for scheduling +timed events. + + +Base framework +############## + +This section documents the new features and changes affecting the +'base' repository, in particular the base API. + + +New features +============ + +Connection handling +~~~~~~~~~~~~~~~~~~~ + +The interaction of a client with a server involves the definition of +session-construction arguments, the request of the session creation via +its parent, the initialization of the matching RPC-client stub code +with the received session capability, the actual use of the session +interface, and the closure of the session. A typical procedure of +using a service looks like this: + +!#include +!... +! +!/* construct session-argument string and create session */ +!char *args = "filename=config, ram_quota=4K"); +!Capability session_cap = env()->parent()->session("ROM", args); +! +!/* initialize RPC stub code */ +!Rom_session_client rsc(session_cap); +! +!/* invoke remote procedures, 'dataspace' is a RPC function */ +!Capability ds_csp = rsc.dataspace(); +!... +! +!/* call parent to close the session */ +!env()->parent()->close(session_cap); + +Even though this procedure does not seem to be overly complicated, +is has raised the following questions and criticism: + +* The quota-donation argument is specific for each server. Most services + use client-donated RAM quota only for holding little meta data and, + thus, are happy with a donation of 4KB. Other services maintain larger + client-specific state and require higher RAM-quota donations. The + developer of a client has to be aware about the quota requirements for + each service used by his application. + +* There exists no formalism for documenting session arguments. + +* Because session arguments are passed to the 'session'-call as a plain + string, there are no syntax checks for the assembled string performed + at compile time. For example, a missing comma would go undetected until + a runtime test is performed. + +* There are multiple lines of client code needed to open a session to + a service and the session capability must be maintained manually for + closing the session later on. + +The new 'Connection' template provides a way to greatly simplify the +handling of session arguments, session creation, and destruction on the +client side. By implementing a service-specific connection class +inherited from 'Connection', session arguments become plain constructor +arguments, session functions can be called directly on the 'Connection' +object, and the session gets properly closed when destructing the +'Connection'. By convention, the 'Connection' class corresponding to a +service resides in a file called 'connection.h' in the directory of the +service's RPC interface. For each service, a corresponding 'Connection' +class becomes the natural place where session arguments and quota +donations are documented. With this new mechanism in place, the example +above becomes as simple as: + +!#include +!... +! +!/* create connection to the ROM service */ +!Rom_connection rom("config"); +! +!/* invoke remote procedure */ +!Capability ds_csp = rom.dataspace(); + +[http://genode.org/documentation/api/base_index#Connecting_to_services - See the API documentation for the connection template...] + + +Typed capabilities +~~~~~~~~~~~~~~~~~~ + +A plain 'Capability' is an untyped reference to a remote object of any +type. For example, a capability can reference a thread object or a +session to a service. It is loosely similar to a C void pointer, for which +the programmer maintains the knowledge about which data type is actually +referenced. To facilitate the type-safe use of RPC interfaces at the C++ +language level, we introduced a template for creating specialized +capability types ('Typed_capability' in 'base/typed_capability.h') and +the convention that each RPC interface declares a dedicated capability +type. Note that type-safety is not maintained across RPC interfaces. As +illustrated in Figure [img/layered_ipc], typification is done at the +object-framework level on the server side and via in the 'Connection' +classes at the client side. + +[image img/layered_ipc] + +From the application-developer's perspective, working with capabilities +has now become type-safe, making the produced code more readable and robust. + +[http://genode.org/documentation/api/base_index#Capability_representation - See the updated API documentation for the capability representation...] + + +Fifo data structure +~~~~~~~~~~~~~~~~~~~ + +Because the 'List' data type inserts new list elements at the list head, +it cannot be used for implementing wait queues requiring first-in +first-out semantics. For such use cases, we introduced a dedicated +'Fifo' template. The main motivation for introducing 'Fifo' into the +base API is the new semaphore described below. + +[http://genode.org/documentation/api/base_index#Structured_data_types - See the new API documentation for the fifo template...] + + +Semaphore +~~~~~~~~~ + +Alongside lock-based mutual exclusion of entering critical sections, +organizing threads in a producer-consumer relationship via a semaphore +is a common design pattern for thread synchronization. Prior versions +of Genode provided a preliminary semaphore implementation as part of +the 'os' repository. This implementation, however, supported only one +consumer thread (caller of the semaphore's 'down' function). We have +now enhanced our implementation to support multiple consumer threads +and added the semaphore to Genode's official base API. We have made +the wake-up policy in the presence of multiple consumers configurable +via a template argument. The default policy is first-in-first-out. + +[http://genode.org/documentation/api/base_index#Synchronization - See the new API documentation for the semaphore...] + +Thanks to Christian Prochaska for his valuable contributions to the new +semaphore design. + + +Asynchronous notifications +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Inter-process communication via remote procedure calls requires both +communication partners to operate in a synchronous fashion. The caller +of an RPC blocks as long as the RPC is not answered by the called +server. In order to receive the call, the server has to explicitly +wait for incoming messages. There are a number of situations where +synchronous communication is not suited. + +For example, a GUI server wants to deliver a notification to one of its +clients about new input events being available. It does not want to +block on a RPC to one specific client because it has work to do for +other clients. Instead, the GUI server wants to deliver this +_notification_ with _fire-and-forget_ semantics and continue with +its operation immediately, regardless of whether the client received +the notification or not. The client, in turn, does not want to poll +for new input events at the GUI server but it wants to be _waken_up_ +when something interesting happens. Another example is a block-device +driver that accepts many requests for read/write operations at once. +The operations may be processed out of order and may take a long time. +When having only synchronous communication available, the client and +the block device driver would have to employ one distinct thread for +each request, which is complicated and a waste of resources. Instead, +the block device driver just wants to acknowledge the completeness of +an operation _asynchronously_. + +Because there are many more use cases for asynchronous inter-process +communication, we introduced a new signalling framework that complements +the existing synchronous RPC mode of communication with an interface for +issuing and receiving asynchronous notifications. It defines interfaces +for signal transmitters and signal receivers. A signal receiver can +receive signals from multiple sources, whereas the sources of incoming +signals are clearly distinguishable. One or multiple threads can either +poll or block for incoming signals. Each signal receiver is addressable +via a capability. The signal transmitter provides fire-and-forget +semantics for submitting signals to exactly one signal receiver. Signals +are communicated in a reliable fashion, which means that the exact number +of signals submitted to a signal transmitter is communicated to the +corresponding signal receiver. If notifications are generated at a higher +rate than as they can be processed at the receiver, the transmitter +counts the notifications and delivers the total amount with the next +signal transmission. This way, the total number of notifications gets +properly communicated to the receiver even if the receiver is not highly +responsive. Notifications do not carry any payload because this payload +would have to be queued at the transmitter. + +[image img/signals] + +Image [img/signals] illustrates the roles of signaller thread, +transmitter, receiver, and signal-handler thread. + +[http://genode.org/documentation/api/base_index#Asynchronous_notifications - See the new API documentation for asynchronous notifications...] + +The current generic implementation of the signalling API employs one +thread at each transmitter and one thread at each receiver. Because +the used threads are pretty heavy weight with regard to resource usage, +ports of Genode should replace this implementation with platform- +specific variants, for example by using inter-process semaphores or +native kernel support for signals. + + +Region-manager faults +~~~~~~~~~~~~~~~~~~~~~ + +In Genode, region-manager (RM) sessions are used to manage the +address-space layout for processes. A RM session is an address-space +layout that can be populated by attaching (portions of) dataspaces to +(regions of) the RM session. Normally, the RM session of a process is +first configured by the parent when decoding the process' ELF binary. +During the lifetime of the process, the process itself may attach +further dataspaces to its RM session to access the dataspace's content. +Core as the provider of the RM service uses this information for +resolving page faults raised by the process. In prior versions of +Genode, core ignored unresolvable page faults, printed a debug message +and halted the page-faulted thread. However, this condition may be of +interest, in particular to the process' parent for reacting on the +condition of a crashed child process. Therefore, we enhanced the RM +interface by a fault-handling mechanism. For each RM session, a fault +handler can be installed by registering a signal receiver capability. +If an unresolvable page fault occurs, core delivers a signal to the +registered fault handler. The fault handler, in turn, can request the +actual state of the RM session (page-fault address) and react upon +the fault. One possible reaction is attaching a new dataspace at the +fault address and thereby implicitly resolving the fault. If core +detects that a fault is resolved this way, it resumes the operation +of the faulted thread. + +This mechanism works analogously to how page faults are handled by +CPUs, but on a more abstract level. A (n-level) page table corresponds +to a RM session, a page-table entry corresponds to a dataspace- +attachment, the RM-fault handler corresponds to a page-fault +exception handler, and the resolution of page-faults (RM fault) +follows the same basic scheme: + +# Application accesses memory address with no valid page-table-entry + (RM fault) +# CPU generates page-fault exception (core delivers signal to fault + handler) +# Kernel reads exception-stack frame or special register to determine + fault address (RM-fault handler reads RM state) +# Kernel adds a valid page-table entry and returns from exception + (RM-fault handler attaches dataspace to RM session, core resumes + faulted thread) + +The RM-fault mechanism is not only useful for detecting crashing child +processes but it enables a straight-forward implementation of growing +stacks and heap transparently for a child process. An example for +using RM-faults is provided at 'base/src/test/rm_fault'. + +Note that this mechanism is only available on platforms on which core +resolves page faults. This is the case for kernels of the L4 family. +On Linux however, the Linux kernel resolves page faults and suspends +processes performing unresolvable memory accesses (segmentation fault). + + +Managed dataspaces (experimental) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The RM-fault mechanism clears the way for an exciting new feature +of Genode 8.11 called managed dataspaces. In prior versions of Genode, +each dataspace referred to a contiguous area of physical memory (or +memory-mapped I/O) obtained by one of core's RAM, ROM, or IO_MEM +services, hence we call them physical dataspaces. We have now added +a second type of dataspaces called managed dataspaces. In contrast +to a physical dataspace, a managed dataspace is backed by the content +described by an RM session. In fact, each RM session can be used as +dataspace and can thereby be attached to other RM sessions. + +Combined with the RM fault mechanism described above, managed +dataspaces enable a new realm of applications such as dataspaces +entirely managed by user-level services, copy-on-write dataspaces, +non-contiguous large memory dataspaces that are immune to physical +memory fragmentation, process-local RM fault handlers (e.g., managing +the own thread-stack area as a sub-RM-session), and sparsely populated +dataspaces. + +Current limitations +------------------- + +Currently, managed dataspaces still have two major limitations. First, +this mechanism allows for creating cycles of RM sessions. Core must +detect such cycles during page-fault resolution. Although, a design for +an appropriate algorithm exists, cycle-detection is not yet implemented. +The missing cycle detection would enable a malicious process to force +core into an infinite loop. Second, RM faults are implemented using the +new signalling framework. With the current generic implementation, RM +sessions are far more resource-demanding than they should be. Once the +signalling framework is optimized for L4, RM sessions and thereby +managed dataspaces will become cheap. Until then, we do not recommend +to put this mechanism to heavy use. + +Because of these current limitations, managed dataspaces are marked as +an experimental feature. When building Genode, experimental features are +disabled by default. To enable them, add a file called 'specs.conf' +with the following content to the 'etc/' subdirectory of your build +directory: + +! SPECS += experimental + +For an example of how to use the new mechanism to manage a part of a +process' own address space by itself, you may take a look at +'base/src/test/rm_nested'. + + +Changes +======= + +Besides the addition of the new features described above, the following +parts of the base framework underwent changes worth describing. + + +Consistent use of typed capabilities and connection classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We applied capability typification to all interfaces of Genode including +the base API and the interfaces defined in the 'os' repository. Figure +[img/base_cap_types] provides an overview about the capability types +provided by the base API. + +[image img/base_cap_types] + Overview about the capability types provided by the base API + +Furthermore, we have complemented all session interfaces with +appropriate 'Connection' classes taking service-specific session +arguments into account. + +For session-interface classes, we introduced the convention to declare +the service name as part of the session-interface via a static member +function: +! static const char *service_name(); + + +Allocator refinements +~~~~~~~~~~~~~~~~~~~~~ + +Throughout Genode, allocators are not only used for allocating memory +but also for managing address-space layouts and ranges of physical +resources such as I/O-port ranges or IRQ ranges. In these cases, the +address '0' may be a valid value. Consequently, this value cannot be +used to signal allocation errors as done in prior versions of Genode. +Furthermore, because managed dataspaces use the RM session interface to +define the dataspace layout, the address-'0' problem applies here as +well. We have now refined our allocator interfaces and the RM-session +interface to make them fit better for problems other than managing +virtual memory. + + +Misc changes +~~~~~~~~~~~~ + +We revised all interfaces to consistently use _exceptions_ to signal +error conditions rather than delivering error codes as return values. +This way, error codes become exception types that have a meaningful +name and, in contrast to global 'errno' definitions, an error exception +type can be defined local to the interface it applies to. Furthermore, +the use of exceptions allows for creating much cleaner looking interfaces. + +Traditionally, we have provided our custom _printf_ implementation as C +symbol to make this function available from both C and C++ code. However, +we observed that we never called this function from C code and that the +'printf' symbol conflicts with the libc. Hence, we turned 'printf' +into a C++ symbol residing in the 'Genode' namespace. + + +Operating-system services and libraries +####################################### + +This section documents the new features and changes affecting +the 'os' repository. + +New Features +============ + +Device-driver framework for C device drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Genode's base API features everything needed to create user-level device +drivers. For example, the 'os' repository's PS/2 input driver and the +PCI bus driver are using Genode's C++ base API directly. However, most of +today's device drivers are written in C. To ease the reuse of existing +drivers on Genode, we have introduced a C API for device drivers into +Genode's 'os' repository. The API is called DDE kit (DDE is an acronym +for device-driver environment) and it is located at 'os/include/dde_kit'. + +The DDE kit API is the result of long-year experiences with porting device +drivers from Linux and FreeBSD to custom OS environments. The following +references are the most significant contributions to the development of +the API. +; +Christian Helmuth created the initial version of the Linux device-driver +environment for L4. He describes his effort of reusing unmodified sound +drivers on the L4 platform in his thesis +[http://os.inf.tu-dresden.de/papers_ps/helmuth-diplom.pdf - Generische Portierung von Linux-Gerätetreibern auf die DROPS-Architektur]. +; +Gerd Griessbach approached the problem of re-using Linux USB drivers +by following the DDE approach in his diploma thesis +[http://os.inf.tu-dresden.de/papers_ps/griessbach-diplom.pdf - USB for DROPS]. +; +Marek Menzer adapted Linux DDE to Linux 2.6 and explored the DDE +approach for block-device drivers in his student research project +[http://os.inf.tu-dresden.de/papers_ps/menzer-beleg.pdf - Portierung des DROPS Device Driver Environment (DDE) für Linux 2.6 am Beispiel des IDE-Treibers ] +and his diploma thesis +[http://os.inf.tu-dresden.de/papers_ps/menzer-diplom.pdf - Entwicklung eines Blockgeräte-Frameworks für DROPS]. +; +Thomas Friebel generalized the DDE approach and introduced the DDE kit +API to enable the re-use of device driver from other platforms than +Linux. In particular, he experimented with the block-device drivers of +FreeBSD in his diploma thesis +[http://os.inf.tu-dresden.de/papers_ps/friebel-diplom.pdf - Übertragung des Device-Driver-Environment-Ansatzes auf Subsysteme des BSD-Betriebssystemkerns]. +; +Dirk Vogt successfully re-approached the port of USB device drivers +from the Linux kernel to L4 in his student research project +[http://os.inf.tu-dresden.de/papers_ps/vogt-beleg.pdf - USB for the L4 Environment]. + +The current incarnation of the DDE kit API provides the following +features: + +* General infrastructure such as init calls, assertions, debug output +* Interrupt handling (attach, detach, disable, enable) +* Locks, semaphores +* Memory management (slabs, malloc) +* PCI access (find device, access device config space) +* Virtual page tables (translation between physical and virtual + addresses) +* Memory-mapped I/O, port I/O +* Multi-threading (create, exit, thread-local storage, sleep) +* Timers, jiffies + +For Genode, we have created a complete reimplementation of the DDE kit +API from scratch by fully utilizing the existing Genode infrastructure +such as the available structured data types, core's I/O services, +the synchronization primitives, and the thread API. + +[image img/dde_kit] + +Figure [img/signals] illustrates the role of DDE kit when re-using an +unmodified device driver taken from the Linux kernel. DDE kit translates +Genode's C++ base API to the DDE kit C API. The DDE kit API, in turn, is +used as back end by the Linux driver environment, which translates Linux +kernel interfaces to calls into DDE kit. With this translation in place, +an unmodified Linux device driver can be embedded into the Linux driver +environment. The device API is specific for a class of devices such as +NICs, block devices, or input devices. It can either be used directly as +a function interface by an application that is using the device driver +as a library, or it can be made accessible to external processes via an +RPC interface. + + +Limitations +----------- + +The PCI sub system is not completely implemented, yet. + + +Alarm API providing a timed event scheduler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The scheduling of timed events is a recurring pattern found in device +drivers, application frameworks such as Qt4 ('qeventdispatcher'), and +applications. Therefore, we have added a timed event scheduler to the +'os' repository. The new alarm API ('os/include/os/alarm.h') allows +for the scheduling of both one-shot alarms and periodic alarms. + + +Changes +======= + +PS/2 input driver +~~~~~~~~~~~~~~~~~ + +The original PS/2 driver tried to switch the PS/2 keyboard to +scan-code set 2 and assumed that all modern keyboards support this +mode of operation. However, this assumption was wrong. We observed +that the legacy PS/2 support of some USB keyboards covers only the +emulated (xlate) scan-code set 1 mode. This is also case for the PS/2 +emulation in VirtualBox. Therefore, we changed our PS/2 driver to +never touch the keyboard mode but to only detect the current mode +of operation. The driver has now to support both, scan-code set 1 and +scan-code set 2. This change comes along with a slightly more complex +state machine in the driver. Hence, we moved the state machine from +the IRQ handler to a distinct class and changed the control flow of +the driver to fetch only one value from the i8042 PS/2 controller +per received interrupt. + + +PCI bus driver +~~~~~~~~~~~~~~ + +Until now, Genode's PCI bus driver was only used for experimentation +purposes. With the forthcoming driver framework however, the PCI bus +driver will play a central role in the system. Therefore, we adapted +the interface of the PCI driver to these requirements. Specifically, +the scanning of the PCI bus can now be performed without constraining +the results by a specific vendor ID. + + +Nitpicker GUI server +~~~~~~~~~~~~~~~~~~~~ + +We improved the _output_latency_ of the Nitpicker GUI server by flushing +pixels eagerly and deferring the next periodically scheduled flush. +This change has a positive effect on the responsiveness of the GUI to +user input. + + +Misc changes +~~~~~~~~~~~~ + +Prior versions of the 'os' repository came with a custom 'os/include/base' +directory with interfaces extending the base API. To avoid confusion +between the 'base' repository and the 'os' repository, 'os'-local API +extensions are now located at 'os/include/os'. This way, the folder +prefix of include statements indicates well from which repository the +included header files comes from. + + +C runtime +######### + +Most of existing libraries rely on the presence of a C library. For +making the reuse of this software on Genode possible, we have now +made a complete C library available for Genode. It comes as a separate +source-code repository called 'libc' and is based on the code of FreeBSD. +The original code is available at the official FreeBSD website. + +:FreeBSD website: + [http://www.freebsd.org/developers/cvs.html] + +Our libc port comprises the libraries 'gdtoa', 'gen', 'locale', 'stdio', +'stdlib', 'stdtime', 'string', and 'msun'. Currently, it supports the +x86 architecture. Support for other architectures is planned as future +addition. At the current stage, our back end is very basic and most of +its functions are dummy stubs. We used Christian Prochaska's forthcoming +Genode port of Qt4 as test case and successfully used the new libc as +foundation for building graphical Qt4 applications. We will further +extend the back end in correspondence to the growing feature set of the +Genode OS framework. + +:Usage: + +To use the libc in your application, just add 'libc' to the 'LIBS' +declaration in your build-description file. This declaration will make +the libc headers available for the include path of your target and link +the C library. When building, make sure that the 'libc' repository is +included in your build configuration ('etc/build.conf'). + +:Limitations: + +The current version of the C library is not thread-safe. For most +string and math functions, this is not a problem (as these functions +do not modify global state) but be careful with using more complex +functions such as 'malloc' from multiple threads. Also, 'errno' may +become meaningless when calling libc functions from multiple threads. + +We have left out the following files from the Genode port of the +FreeBSD libc: gdtoa 'strtodnrp.c' (gdtoa), 'getosreldate.c' (gen), +'strcoll.c', 'strxfrm.c', 'wcscoll.c', 'wcsxfrm.c' (string), +'s_exp2l.c' ('msun'). + +The current back end is quite simplistic and it may help you to revisit +the current state of the implementation in the 'libc/src/lib/libc' +directory. If one of the functions in 'dummies.c' is called, you will +see the debug message: +! " called, not yet implemented!" +However, some of the back-end function implemented in the other files +have dummy semantics but have to remain quiet because they are called +from low-level libc code. + + +Build infrastructure +#################### + +Build-directory creation tool +============================= + +Because we think that each Genode developer benefits from knowing the +basics about the functioning of the build system, the manual creation of +build directories is described in Genode's getting-started document. +However, for regular developers, creating build directories becomes a +repetitive task. Hence, it should be automated. We have now added a +simple build-directory creation tool that creates pre-configured build +directories for some supported platforms. The tool is located at +'tool/builddir/create_builddir'. To print its usage information, just +execute the tool without arguments. + + +Improved linking of binary files +================================ + +For linking binary data, binary file have to be converted to object +files. Over the time, we have used different mechanisms for this +purpose. Originally, we used 'ld -r -b binary'. Unfortunately, these +linker options are not portable. Therefore, the mechanism was changed +to a 'hexdump' and 'sed' magic that generated a C array from binary data. +This solution however, is complicated and slow. Now, we have adopted +an idea of Ludwig Hähne to use the 'incbin' directive of the GNU +assembler, which is a very clean, flexible, and fast solution. + + +Lib-import mechanism +==================== + +Libraries often require specific include files to be available at the +default include search location. For example, users of a C library +expect 'stdio.h' to be available at the root of the include search +location. Placing the library's include files in the root of the +default search location would pollute the include name space for +all applications, regardless if they use the library or not. To +keep library-include files well separated from each other, we have +enhanced our build system by a new mechanism called lib-import. +For each library specified in the 'LIBS' declaration of a build +description file, the build system incorporates a corresponding +'import-.mk' file into the build process. Such as file +defines library-specific compiler options, in particular additional +include-search locations. The build system searches for lib-import +files in the 'lib/import/' subdirectories of all used repositories. + + +Using 'ar' for creating libraries +================================= + +The previous versions of Genode relied on incremental linking ('ld -r') +for building libraries. This approach is convenient because the linker +resolves all cross-dependencies between libraries regardless of the +order of how libraries are specified at the linker's command line. +However, incremental linking prevents the linker from effectively +detecting dead code. In contrast, when linking '.a' files, the linker +detects unneeded object files. Traditionally, we have only linked our +own framework containing no dead code. This changed with the new 'libc' +support. When linking the 'libc', the presence of dead code becomes +the normal case rather than the exception. Consequently, our old +incremental-linking approach produced exceedingly large binaries +including all functions that come with the 'libc'. We have now adopted +the classic 'ar' mechanism for assembling libraries and use the linker's +'start-group' 'end-group' feature to resolve inter-library-dependencies. +This way, dead code gets eliminated at the granularity of object files. +In the future, we will possible look into the '-ffunction-sections' and +'-gc-sections' features of the GNU tool chain to further improve the +granularity to function level. + +If your build-description files rely on custom rules referring to +'lib.o' files, these rules must be adapted to refer to 'lib.a' files +instead. + + +Misc changes +============ + +* Added sanity check for build-description files overriding 'INC_DIR' + instead of extending it. + +* Restrict inclusion of dependency files to those that actually matter + when building libraries within 'var/libcache'. This change significantly + speeds up the build process in the presence of large libraries such as + Qt4 and libc. + +* Added rule for building 'cpp' files analogously to the 'cc' rule. + Within Genode, we name all C++ implementation files with the 'cc' + suffix. However, Qt4 uses 'cpp' as file extension so we have to + support both. + +* Build-description files do no longer need the declaration + 'REQUIRES = genode'. Genode's include search locations are now + incorporated into the build process by default. + + +Applications +############ + +This section refers to the example applications contained in Genode's +'demo' repository. + +We have enhanced the _Scout_widgets_ as used by the launchpad and the +Scout tutorial browser to perform all graphical output double-buffered, +which effectively eliminates drawing artifacts that could occur when +exposing intermediate drawing states via direct (unbuffered) output. +Furthermore, we have added a way to constrain the maximum size of +windows to perform pixel-buffer allocations on realistic window sizes. + +Both launchpad and Scout can now start child applications. In Scout +this functionality is realized by special "execute" links. We have +generalized the underlying application logic for creating and +maintaining child processes between both applications and placed +the unification into a separate 'launchpad' library. + +We have replaced the default document presented in Scout with an +_interactive_walk-through_guide_ explaining the basic features of Genode. +The document uses the new "execute" link facility to let the user start +a launchpad instance by clicking on a link. + + +Platform-specific changes +######################### + +Genode used to define _fixed-width_integer_types_ in a file 'stdint.h' +placed in a directory corresponding to bit-width of the platform, for +example 'include/32bit/stdint.h'. When building for a 32bit platform, +the build system included the appropriate directory into the +include-search path and thereby made 'stdint.h' available at the root +of the include location. Unfortunately, this clashes with the 'stdint.h' +file that comes with the C library. To avoid conflict with libc header +files, we moved the definition of fixed-width integer types to +'32bit/base/fixed_stdint.h'. + +For the L4/Fiasco version of Genode, there existed some x86-specific +header files that did not specifically depend on L4/Fiasco, for example +atomic operations. Because these files are not L4/Fiasco-specific and +may become handy for other platforms as well, we moved them to the +generic 'base' repository. + + +Linux 32bit +=========== + +:Dissolving Genode's dependency from the glibc: + +The port of the C runtime to Genode posed an interesting challenge to +the Linux version of Genode. This version used to rely on certain +functions provided by the underlying glibc: + +* For creating and destroying threads, we used to rely on POSIX threads + as provided by the 'pthread' library + +* The lock implementation was based on the POSIX semaphore functions + 'sem_init', 'sem_wait', and 'sem_post' + +* Shared memory was realized by using files ('open', 'close', + 'ftruncate') and the 'mmap' interface + +* Starting and killing processes was implemented using 'fork' and 'kill' + +* Inter-process communication used the glibc's socket functions + +For our custom C runtime, we want to override the glibc functionality +with our own implementation. For example, we want to provide the 'mmap' +interface to a Genode application by implementing 'mmap' with +functions of our base API. On Linux, however, this base API, in turn, +used to rely on 'mmap'. This is just an example. The problem applies +also for the other categories mentioned above. We realized that we cannot +rely on the glibc on one hand but at the same time replace it by a custom +C runtime (in fact, we believe that such a thing is possible by using +awkward linker magic but we desire a clean solution). Consequently, we +have to remove the dependency of Genode from the glibc on Linux. Step +by step, we replaced the used glibc functions by custom Linux system-call +bindings. Each binding function has a prefix 'lx_' such that the symbol +won't collide with 'libc' symbols. The new bindings are located at the file +'base-linux/src/platform/linux_syscalls.h'. It consist of 20 functions, +most of them resembling the original interface ('socket', 'connect', +'bind', 'getsockname', 'recvfrom', 'write', 'close', 'open', 'fork', +'execve', 'mmap', 'ftruncate', 'unlink', 'tkill', 'nanosleep'). +For other functions, we simplified the semantics for our use case +('sigaction', 'sigpending', 'sigsetmask', 'create_thread'). The most +noteworthy changes are the creation and destruction of threads by +directly using the 'clone' and 'tkill' system calls, and the lock +implementation. Because we cannot anymore rely on the convenience of +using futexes indirectly through the POSIX semaphore interface, we +have adopted the simple locking approach that we already use for the +L4/Fiasco version. This lock implementation is a simple sleeping +spinlock. + + +:Compromises: + +The introduction of custom Linux system-call bindings for Genode has +several pros and cons. With this change, The Linux version of Genode is +not anymore easy to port to other POSIX platforms such as the Darwin +kernel. For each POSIX kernel used as Genode platform, a custom +implementation of our system-call bindings must be created. The +original POSIX variant could still be reanimated, but this version +would inherently lack support for Genode's C runtime, and thus would +have limited value. A positive side effect of this solution, however, +is that 'linux_syscalls.h' documents well the subset of the Linux' +kernel interface that we are actually using. + +The replacement of POSIX semaphores with sleeping spinlocks decreases +locking performance quite significantly. In the contention case, the +wakeup from sleeping introduces a high latency of up to one millisecond. +Furthermore, fairness is not guaranteed and the spinning produces a bit +of system load. If this approach turns out to become a serious performance +bottleneck, we will consider creating custom bindings for Linux' futexes. + + +L4/Fiasco +========= + +The concepts of _RM_faults_ and _managed_dataspaces_ as described in +Section [Base framework], had been implemented into the L4/Fiasco +version of core. Although the introduction of these concepts involved +only minimal changes at the API level, the required core-internal +changes had been quite invasive, affecting major parts of the pager +and RM-session implementations. + +Prior versions of the L4/Fiasco version of core did not implement +the _cancel-blocking_mechanism_ as specified by the CPU-session API. +The missing implementation resulted in lock-ups when destructing a +thread that blocks for lock. With the new implementation based on +L4/Fiasco's inter-task ex-regs system call, such threads can now +be gracefully destructed. diff --git a/doc/release_notes-09-02.txt b/doc/release_notes-09-02.txt new file mode 100644 index 000000000..508a5a69e --- /dev/null +++ b/doc/release_notes-09-02.txt @@ -0,0 +1,460 @@ + + ============================================== + Release notes for the Genode OS Framework 9.02 + ============================================== + + Genode Labs + +Summary +####### + +Whereas the focus of the previous release 8.11 was the refinement of +Genode's base API and the creation of the infrastructure needed to build +real-world applications, the release 9.02 is focused on functional +enhancements in two directions. The first direction is broadening the +number of possible base platforms for the framework. At present, most +microkernels bring along a custom user land, which is closely tied to the +particular kernel. Our vision is to establish Genode as a common ground for +developing applications, protocol stacks, and device drivers in such a way +that the software becomes easily portable among different kernels. This +release makes Genode available on the L4ka::Pistachio kernel. Hence, +software developed with the Genode API can now run unmodified on +Linux/x86, L4/Fiasco, and L4ka::Pistachio. In the second direction, we +are steadily advancing the functionality available on top of Genode. With +this release, we introduce a basic networking facility and support for +native Qt4 applications as major new features. Thanks to Genode's +portability, these features become automatically available on all +supported base platforms. + +Our original plan for the release 9.02 also comprised the support of a +Linux-on-Genode (para-)virtualization solution. Initially, we intended to +make [http://os.inf.tu-dresden.de/L4/LinuxOnL4/ - L4Linux] available on +the L4/Fiasco version of Genode. However, we identified several downsides +with this approach. Apparently, the development of the officially available +version of L4/Fiasco has become slow and long-known issues remain unfixed. +L4Linux, however, is closely tied to L4/Fiasco and the L4 environment. For +us at Genode Labs, maintaining both a custom port of L4Linux for Genode +and L4/Fiasco by ourself in addition to developing Genode is unfeasible. +In contrast, the Pistachio kernel features more advanced options for +virtualization ([http://l4ka.org/projects/virtualization/afterburn/ - Afterburner] +and VT support) that we want to explore. Furthermore, there exists another +version of L4Linux called OKLinux for the OKL4 kernel developed at +[http://ok-labs.com - OK-Labs], which is very interesting as well. +Therefore, we decided against an ad-hoc solution and deferred this feature +to the next release. [http:/about/road-map - See our updated road map...] + + +Major new Features +################## + +Genode on L4ka::Pistachio +========================= + +From the very beginning, the base API of the Genode OS Framework was +designed for portability. We put a lot of effort into finding API +abstractions that are both implementable on a wide range of kernels and as +close to the hardware as possible to keep the abstraction overhead low. For +this reason, we developed the framework in parallel for the Linux kernel and +the L4/Fiasco kernel. To validate our claim that Genode is highly portable, +Julian Stecklina ported the framework to another member of the L4 family, +namely the [http://l4ka.org/projects/pistachio/ - L4ka::Pistachio kernel]. +This high-performance kernel implements the latest official L4 API called +L4.x2 and has a number of advanced features such as multi-processor support +and virtualization support. + +After Julian successfully created the first Pistachio version of Genode, +we successively refined his work and conducted further unifications among +the platform-dependent code for the different kernels. The result of this +effort is included in this release and comes in the form of the +'base-pistachio' source-code repository. + +;Interesting technical notes: + +* The IRQ handling on Pistachio is slightly different from L4/Fiasco. + On L4/Fiasco, an IRQ becomes unmasked only when the user-level IRQ + handler thread blocks for an IRQ by issuing an IPC call to the + kernel's corresponding IRQ thread. In contrast, Pistachio unmasks an + IRQ as soon as the user-level IRQ handler associates itself with an + IRQ. Thus, an IRQ message may occur not only when the user-level IRQ + handler blocks for any IRQ but anytime. In particular, IRQ messages + may interfere with the IRQ handler's IPC communication with other + threads. To ensure that IRQ messages do only occur when expecting + them, we lazily associate the IRQ handler thread to the IRQ the + first time we wait for an IRQ and issue an unmasking IPC call + subsequent times. + +* Genode provides a mechanism for gracefully destructing threads that + are in a blocking state, for example waiting for an IPC message. + Such a thread may hold locks or other resources that would not + get properly freed when just killing the thread by force. Therefore, + Genode provides a way to issue the cancellation of a blocking + operation by another thread (e.g., the thread calling the destructor). + Once, a blocking operation got canceled, a C++ exception + ('Blocking_canceled') is raised such the thread can fall back into + a defined state and then be destructed. On L4ka::Pistachio, we use + Pistachio's pager-exchange-registers feature in combination with + the user-defined UTCB handle for cancelling blocking operations and + detecting cancellations. The interesting code bits can be found in + 'src/base/ipc/ipc.cc', 'src/base/lock/lock.cc', + 'src/core/platform_thread.cc', and in the Pistachio-specific + timer-service implementation. + +* During the refinement of the Pistachio version, we were able to further + generalize code that was previously specific for L4/Fiasco and + L4ka::Pistachio respectively. Now, the platform-specific code comprises + less than 3,000 lines of code (LOC) for L4/Pistachio, circa 2,000 LOC + for L4/Fiasco, and circa 1,000 LOC for Linux. Hence, we expect that + porting the framework to further kernels is possible at reasonable + engineering costs. + +:Current limitations: + +* The current version does not use superpages (4M mappings) because we + experienced problems with mapping 4K pages out of 4M pages. This is an + issue that we like to investigate further because using 4M mappings + would improve the boot time and reduce the kernel-memory usage. + +* Currently, we use a simple sleeping spinlock for synchronization, which + is not optimal for several reasons. There are no fairness guarantees, + the spinning consumes CPU time, and threads that got blocked in the + contention case are woken up at the coarse granularity of the kernel's + timer tick, which is typically one millisecond. + +* Nested RM sessions as introduced as an experimental feature in the + Genode release 8.11 are not yet supported. + +:Further details: + +You can find further technical details and usage instructions at our +dedicated [http://genode.org/community/wiki/GenodeOnL4kaPistachio - Wiki page]. + + +Qt4 on Genode +============= + +The minimalism of the Genode OS Framework with regard to its code +complexity raised the question of whether this framework is feasible +for hosting real-world applications and widely used runtime environments. +Christian Prochaska took the challenge to port Trolltech's Qt4 application +framework, which serves as the basis for the popular KDE desktop, to Genode. + +Because Christian started his work more than a year ago at a time when no +C library was available on Genode, several intermediate steps were needed. +The first step was the integration of the Qt4 tools such as the meta-object +compiler (moc) and resource compiler properly into the our build systion. +With the tools in place, the Linux version of Genode came to an advantage. +In this environment, a Genode application is able to use glibc functionality. +So the problem of a missing C library could be deferred and Christian was +able to focus on interfacing Qt with the existing Genode services such as +the Nitpicker GUI sever. Next, the glibc dependencies were successively +replaced by custom implementations or simple dummy stubs. Thereby, all +needed functionalities such as timed semaphores and thread-local storage +had to be mapped to existing Genode API calls. Once, all glibc dependencies +had been dissolved, Qt could be compiled for the L4/Fiasco version. + +Since a C library has become available in Genode 8.11, we were able to +replace Christian's intermediate stub codes with a real C library. We also +utilize recently added features of Genode such as its alarm framework to +simplify the Qt4 port. Furthermore, we were able to remove all +platform-specific bits such that the Qt4 port has now become completely +generic with regard to the underlying kernel. Qt4 can be executed on Linux, +L4/Fiasco, and L4ka::Pistachio without any changes. Figure [img/qt4_screenshot] +shows a screenshot of Qt's Tetrix example running side-by-side with native +Genode applications. + +[image img/qt4_screenshot] + +:Current state: + +* The Qt4 port comes in the form of a source-code repository, which contains + all Qt source codes, and some example programs such as Tetrix. You can + download the Qt4 repository as a separate archive at the download page of + the Genode release 9.2. For the next release, we plan to separate the + Genode-specific parts from Qt original code and make the Genode-specific + parts a regular component of the Genode main line. + +* The Qt4 port consists of Qt's Core library, GUI library, Script library, + XML library, and the UI tools library. Other libraries such as Webkit + are not ported yet. + +* This first version of Qt4 on Genode is not to be considered as stable. + There are several known issues yet to be addressed. In particular, + the 'QEventDispatcher' is still work in progress and not fully stabilized. + +* Because, we use to statically link programs, the binaries of Qt + applications are exceedingly large. For example the Tetrix binary is + 100MB including debug information and 11MB in the stripped form. For + employing Qt on Genode at a larger scale, Genode should be enhanced with + [http://genode.org/community/wiki/SharedLibrarySupport - shared-library support]. + + +Networking +========== + +With Genode 8.11, we introduced the Device-Driver-Environment Kit (DDE Kit) +API, which is a C API specifically designed for implementing and porting +device drivers written in plain C. We have now complemented DDE Kit with an +environment for executing Linux device drivers natively on Genode. This +library is called 'dde_linux26' and contained in our new 'linux_drivers' +source-code repository. The environment consists of several parts, which +correspond to the different sub systems of the Linux kernel 2.6, such as +'arch', 'drivers', 'kernel'. + +The first class of device-drivers supported by DDE Linux 2.6 is networking. +At the current stage, the DDE Linux network library comprises general +network-device infrastructure as well as an exemplary driver for the PCnet32 +network device. + +Based on this library, we have created a basic TCP/IP test utilizing the +uIP stack, which uses the DDE Linux network library as back end. The test +program implements a basic web server displaying uIP packet statistics. +When executed on Qemu, you can use your host's web browser to connect to +the web server running on Genode: + +For booting Genode on L4/Fiasco with the web-server demo, use a GRUB +entry in your 'menu.lst' file as follows. + +! title Genode: DDE Linux 2.6 NET on L4/Fiasco +! kernel /fiasco/bootstrap -maxmem=64 -modaddr=0x02000000 +! module /fiasco/fiasco -nokd -serial -serial_esc +! module /fiasco/sigma0 +! module /genode/core +! module /genode/init +! module /config +! module /genode/timer +! module /genode/pci_drv +! module /genode/test-dde_linux26_net + +The first four lines are L4/Fiasco specific. When using L4ka::Pistachio, +the 'menu.lst' entry looks like this: + +! title Genode: DDE Linux 2.6 NET on L4/Pistachio +! kernel /pistachio/kickstart +! module /pistachio/x86-kernel +! module /pistachio/sigma0 +! module /genode/core +! module /genode/init +! module /config +! module /genode/timer +! module /genode/pci_drv +! module /genode/test-dde_linux26_net + +The web-server test requires the PCI bus driver and the timer service. +Therefore, the 'config' file for Genode's init should have following +content: +! +! +! timer +! 512K +! +! +! pci_drv +! 512K +! +! +! test-dde_linux26_net +! 16M +! +! + +Now, its time to create an ISO image from all files specified in +the GRUB configuration. For this, the new utility 'tool/create_iso' +becomes handy. The ISO image can then be booted on Qemu using the +following arguments: +! qemu -m 64 -serial stdio -no-kqemu -cdrom \ +! -net nic,model=pcnet -net user -redir tcp:5555::80 + +The '-redir' argument tells qemu to redirect TCP connections with +localhost:5555 to the guest OS at port 80. After having booted +up Genode on Qemu, you can use your host's web browser to access +the web server: +! firefox http://localhost:5555 + +:Notes about using the TAP version: + +* Preparations + * You must be permitted to sudo and have installed the tunctl + utility. Under Debian/Ubuntu execute + + ! sudo apt-get install uml-utilities + + * Create TAP device + ! TAPDEV=$(sudo tunctl -b -u $USER) + ! sudo /sbin/ifconfig $TAPDEV 10.0.0.1 + + * setup DHCP server on $TAPDEV and 10.0.0.0/8 + +* Run qemu + ! qemu -m 64 -serial stdio -no-kqemu -cdrom dde.iso \ + ! -net nic,model=pcnet \ + ! -net tap,ifname=$TAPDEV,script=no,downscript=no + +* Ping + +* Cleanup + * Stop DHCP server + * Remove TAP device + ! sudo tunctl -d $TAPDEV + + +Operating-system services and libraries +####################################### + +C Runtime +========= + +We have replaced the 'malloc' implementation of the original FreeBSD C +library with a custom implementation, which relies on Genode's 'Heap' as +allocator. The FreeBSD libc reserves a default memory pool of 1MB, which +is no problem on FreeBSD because virtual memory is backed lazily with +physical pages on demand. On Genode however, we immediately account the +allocated memory, which implicates high quota requirements even for +applications that use little memory. In contrast, Genode's heap allocates +and accounts its backing store in relatively small chunks of a few KB. +Therefore, the quota accounting for applications is much more in line with +the actual memory usage. Furthermore, our custom 'malloc' implementation +has the additional benefit of being thread safe. + +* Added i386-specific parts of gen lib, in particular longjmp, setjmp. + + +Device-Driver-Environment Kit +============================= + +* The DDE Kit uses our alarm framework (located in the 'os' repository) now + rather than its own event-scheduler implementation formerly called 'Tick'. + +* We refined the DDE Kit API and reduced the number of custom types. For + example, we removed the custom 'dde_kit_lock_t' and using + 'struct dde_kit_lock' instead, and replaced 'dde_kit_thread_t' with + 'struct dde_kit_thread'. + +Because of the apparent stabilization of the DDE Kit API, we have now added +this API to Genode's official API reference. +[http://genode.org/documentation/api/dde_kit_index - See the documentation of the DDE Kit API...] + + +PS/2 input driver +================= + +We improved the PS/2 keyboard driver by adding missing scan-code translations +for the scan code set 1, in particular the cursor keys. + + +Applications +############ + +Launchpad configuration +======================= + +Launchpad is a graphical application for interactively starting and killing +programs. It is used for the default demonstration of Genode. By default, +launchpad displays a preconfigured list of programs and their respective +default memory quotas. The user can tweak the memory quota for each entry +with mouse and then start a program by clicking on its name. As an +alternative to using the default list, you can now define the list manually +by supplying a configuration to Launchpad. The following example tells +launchpad to display a list of two launcher entries: + +! +! +! sdl_pathfind +! 10M +! +! +! liquid_fb +! 10M +! +! + +To use this configuration for a Launchpad started via init, you can simply +insert the launchpad configuration into the '' node of the launchpad +entry in init's 'config' file. + + +Platform-specific changes +######################### + +L4/Fiasco +========= + +* Raise 'Blocking_canceled' exceptions on canceled IPC calls + +32-bit Linux +============ + +* We continued dissolving the dependency of Genode from the glibc by using + a custom 'getenv' implementation used during process creation. +* By default, we compile now with '-nostdinc' and explicitly specify + '/usr/include' as include search directory only when needed. Previously, + a Genode application, which included a host include file by mistake, has + not raised any compilation error when compiled for the Linux version of + Genode. Now, all Genode platforms behave equally with regard to include + search directories. +* We enforce using the actual compiler's C++ support libraries rather than + the default libraries installed on the host. + + +Tools and build infrastructure +############################## + +Official tool chain +=================== + +At the download section of our website, we used to provide a crosstool-based +tool chain as pre-compiled binaries. Since we got several requests about +how to build such a tool chain from scratch, we created custom utility for +downloading, building, and installing the official Genode tool chain. You +can find the utility at 'tool/tool_chain'. For usage instructions, just +start 'tool_chain' without arguments. Because this utility is a plain script, +you can follow and verify each step that we use for creating the Genode tool +chain. Currently, this official tool chain is based on binutils 2.18 and +gcc 4.2.4. + +As an alternative to installing the tool chain from source, we also +provide pre-compiled binaries at the download section of our website. +[http://genode.org/download/tool-chain - Visit our tool-chain download website...] + +For the Linux version of Genode, we still use the host's default gcc +as tool chain. This way, we spare the hassle of downloading and installing +a custom tool chain for somebody who wants to give Genode a quick try. +With this is mind, we have fixes several small issues with gcc 4.3.2: + +* Fixed dependency generation for gcc-4.3.2. Older version of gcc used to + append a '.o' dependency at the target of '.d'-files. However, gcc-4.3.2 + seems to handle the option '-MT' differently, resulting in a rule that + contains only the '.d' as target. Now, we explicitly specify both the + '.o' file and the '.d' file as target. Consequently, on older gcc + versions, the '.o' file appears twice but that is no problem. + +* Fixed assembler issue with the 'fnstsw' instruction in the C library. + This instruction does not accept eax but only ax as argument. + +Build-directory creation tool +============================= + +We added a rule for creating a pre-configured build directory for the +Pistachio version to our build-directory creation tool +('tool/builddir/create_builddir'). Furthermore, we changed the default +build configuration such that the official Genode tool chain is used for +L4/Fiasco and L4ka::Pistachio. + +Build system +============ + +* Improved clean rule - visit each target directory only once +* Stop the build process on the first error by default, for continuing + the build process depite of an error, you can use the '-i' argument + of make. +* Compiler flags can now be set specific for compiling C and C++ sources. + This is needed because both variants allow different sets of warning + options. The new variables are called 'CC_CXX_OPT' and 'CC_C_OPT'. + +ISO image creation tool +======================= + +We have created a convenient front end for 'genisoimage', which we +use for testing Genode on Qemu. You can find this ISO-image-creation +tool at 'tool/create_iso'. For usage instructions, simply start the +tool without arguments. + diff --git a/doc/release_notes-09-05.txt b/doc/release_notes-09-05.txt new file mode 100644 index 000000000..6fb092878 --- /dev/null +++ b/doc/release_notes-09-05.txt @@ -0,0 +1,585 @@ + + ============================================== + Release notes for the Genode OS Framework 9.05 + ============================================== + + Genode Labs + + +Shortly after including support for the L4ka::Pistachio kernel in the +previous release, the Genode version 9.05 gives a further evidence of +Genode's high portability by making the framework available on top of +the OKL4 kernel. This kernel is a commercial-grade microkernel +developed by [http://ok-labs.com - Open Kernel Labs]. In Section +[Supporting the OKL4 kernel as new base platform], we elaborate on the +new base platform and summarize the experiences made during our porting +work. + +The previous Genode release was accompanied by a source-code archive containing +the initial version of Qt4 for Genode. Our approach is to make the Qt4 +framework available for building Genode applications running natively on the +microkernel rather than within a virtualization environment. As advertised in +our [http://genode.org/about/road-map - road map], we have now seamlessly +integrated the Qt4 framework into our mainline source tree. Furthermore, we +have adapted our port to the Qt4 version 4.5.1. Section [Integration of Qt4 +into the mainline repository] gives a rough overview of the changes and an +introduction on how to use the Qt4 framework with Genode. + +The third major feature of the release is the addition of preliminary USB +support. We have been able to port major parts of Linux' USB infrastructure +to Genode using the DDE Kit introduced in autumn 2008. Section [USB support] +presents an overview about the pursued design and the current state of +implementation. + +Section [OKLinux on Genode] outlines our ongoing efforts of running Linux +as a node in Genode's process tree. + + +Supporting the OKL4 kernel as new base platform +############################################### + +The OKL4 kernel developed by Open Kernel Labs started as a fork of the +L4ka::Pistachio kernel. Whereas L4ka::Pistachio remained true to the L4 +x.2 specification, OKL4 was subject of major API changes geared towards high +performance on the ARM architecture. OKL4 earned much fame for executing a +user-level variant of Linux (OKLinux) on top the microkernel, which turned out +to be faster than executing Linux natively on the ARM9 architecture. Even +though OKL4 is primary targeted at the ARM architecture, we wanted to go for +the x86 variant because of two reasons. First, there exists the just mentioned +user-level port of Linux for OKL4, which looks like an attractive way to execute +Linux on Genode once Genode runs on OKL4. Second, we think that distributing +Genode in the form of ISO images bootable on plain PC hardware is the best +way to reach the OS community. Therefore, we decided to use OKL4 version 2.1 as +the base for our work. In contrast to later releases, this version supports +both x86 and ARM. The following section reviews the unique features of the +OKL4 kernel from our perspective. + + +OKL4 viewed from the angle of a Genode developer +================================================ + +On the kernel-API level, OKL4 has several interesting properties that had been +both welcome and challenging. We want to highlight the following points: + +In contrast to prior L4 kernels, OKL4 has *removed wall-clock timeouts* from +the kernel interface. On L4, timeouts were used as arguments for for blocking +IPC operations serving two purposes. First, specifying IPC timeouts allowed the +IPC caller for regaining control over the blocking thread after the specified +time elapsed. This is considered as important in the case that the called +thread misbehaves and never answers the call. However, the problem of choosing +an appropriate timeout was never properly resolved. When dimensioning the +timeout too small, the called thread may miss the timeout just because it had +no chance to be selected by the scheduler in time. Such timeouts rely on the +presumption that there is low load on the system. On the other hand, when +dimensioning the timeout too high, the system will become sluggish when the +called thread misbehaves. For example, a simple GUI server may want to send +input events to its clients with a timeout to be robust against misbehaving +clients that never wait for events. When choosing a timeout too small, chances +are high that an event will occur at a time when the receiver is handling a +previous event. The timeout would trigger and the event would get lost. When +choosing the timeout too large, say 1 second, any misbehaving client could make +the GUI server freeze for 1 second. Therefore, timeouts for regaining control +over a blocked thread seem to be a bad idea. So we welcome their absence in +OKL4. The second use of timeouts is their use as user-level time source. On L4, +sleep is typically implemented as a blocking IPC with a timeout set to the +sleep value. For this purpose, a system built on top of OKL4 has to employ a +user level device driver accessing a timer device. In Genode, we already have a +timer service for this purpose. So we won't miss timeouts at all. + +Classical L4 kernels provide two variants of *synchronous IPC*. So called long +IPC could copy any amount of memory from the sending to the receiving address +space. This is complicated operation because either communication partner may +specify communication buffers that contain unmapped pages. Hence, page faults +may occur during long-IPC operations. On L4, page faults, in turn, are handled +by the user land. Not until a user-level pager thread resolves the page fault +by establishing a mapping at the faulting address, the kernel can proceed the +IPC operation. This sounds pretty complicated, and it is. The second IPC +variant is called short IPC. It constrains the transferable payload to CPU +registers. Hence, these IPC operations should only be used for messages with a +payload of a maximum of 2 machine words. Because short IPCs are never touching +user-level memory pages, no page faults can occur. +On OKL4, there is only one IPC operation, which copies payload from the +sender's user-level thread-control block (UTCB) to the receiver's UTCB. An +UTCB is an always-mapped memory region. Hence no page faults can occur during +IPC operations. On Genode, the UTCB size of 256 bytes may become a limitation +when RPC messages get large. For example, session requests may include large +session-argument strings specifying session-constructor arguments. Current +services rely only on a few arguments so the size limitation is not an +apparent problem. But that may change for future services. Furthermore, in +contrast to L4 x.2, OKL4 does not allow for transferring payload other than +plain data. In particular, OKL4 does not support the transfer of memory +mappings via IPC. Removing memory mappings from the IPC operation is a very +good idea. On Genode, only roottask (core) establishes mappings and shared +memory is implemented as a user-level protocol (data spaces). There is no need +to allow arbitrary processes to establish memory mapping via IPC. + +The *boot procedure* of OKL4 largely differs from other L4 kernels. This is +attributed to Open Kernel Labs' focus on embedded systems, which mostly rely on +single-image boot loading. OKL4 employs a tool (elfweaver) for creating a +bootable image from a bunch of files and an XML configuration file. Among the +declarations about which processes to be loaded and which policies to enforce, +the configuration file contains platform parameters such as the amount of +physical memory of the machine. This static approach to configure a system is +certainly useful for embedded systems but PC hardware uses to vary a lot. In +this case, evaluating boot-time memory descriptors would be the preferred +solution. + +OKL4 introduces kernel support for *user-level synchronization*. Prior L4 +kernels facilitated user-level synchronization through a combination of +synchronous IPC operations with either priorities or delayed preemption. +OKL4's mutexes can make the life in the user land much easier. However, we have +not looked into OKL4 mutexes yet. + +There does not exist a recursive *map operation* as the source operand of the +map operation is a physical memory descriptor rather than a virtual address in +the mapper's address space. Consequently, this design eliminates the need for +having a recursive unmap operation and thereby, the need to maintain a mapping +data base in the kernel. This is cool because Genode keeps track of the +mappings at the user level anyway (within core). From our perspective, there is +no need to maintain mapping relationships in the kernel. Removing the mapping +database effectively discards a lot of much-discussed problems about how to +manage the mapping database in a clever way. + +There exists *no root memory manager* (sigma0). Because the map operation +takes a physical memory descriptor as argument instead of a virtual address +in the mapper's address space. The mapper does not need to have the mapped +page locally mapped within its own address space. In fact, core (as the only +mapper in a Genode system) does only have very little memory mapped locally. +This design accelerates the boot time because there is no need to map each +physical page in core at startup as performed when running core on the other +L4 kernels. + +These differences of OKL4 compared with the microkernels already supported +by Genode posed a number of interesting challenges and opportunities. We have +thoroughly documented the process in +[http://genode.org/documentation/articles/genode-on-okl4 - Bringing Genode to OKL4]. + + +Usage +===== + +For using Genode with OKL4, please refer to the following dedicated Wiki page: + +:[http://genode.org/community/wiki/GenodeOnOKL4 - Genode on OKL4]: + Wiki page about building and using Genode with the OKL4 kernel. + + +Limitations of the current implementation +========================================= + +The current implementation is able to execute the complete Genode demonstration +scenario on OKL4. This means, we can build and destroy arbitrary trees of +processes, have all the needed infrastructure in place to execute user-level +device drivers such as VESA and PS/2, perform inter-process communication +via RPC and shared memory, and have all basic framework API functions available. +We regard the current state as the first functional version. However, there are +the following points that need improvement and are subject to our future work. + +:Incomplete timer driver: + + On x86, the timer driver should program the PIT to dispatch sleep requests. + However, the I/O ports of the PIT can only by made available to one party in + the system (which naturally would be the timer driver). Unfortunately, there + are some VESA BIOSes around, which try using the PIT directly. The current + version of our VESA driver does not virtualize these accesses. It rather + tries to gain direct access to the I/O ports from core. This would not work + if the timer already uses this device resource. Our plan is to supplement + our VESA driver with a virtual PIT that uses the timer service as back end. + Then we can safely use the PIT by the timer driver. + +:Signalling framework not yet implemented: + + We have not yet implemented Genode's API for asynchronous notifications + in the OKL4 version. In fact, the goal of the initial version of the + OKL4 support was running the default demonstration scenario, which does + not rely on signals. The second and more technical reason is that we + consider exploiting OKL4's event mechanism for implementing the signalling + API but have not finalized the design. The generic implementation as used + on the other platforms cannot be used on OKL4 because this implementation + utilizes one helper thread per signal transmitter. Within core, each RM + session is a potential signal transmitter, which means that we need to + create a helper thread per process. Unfortunately, by default, OKL4 + limits the number of threads within roottask (core) to only 8 threads, + which would impose a severe limit on the number of processes we could + run on OKL4. + +:OKL4's kernel mutexes yet to be used: + + We have not yet explored the use of mutexes provided by the OKL4 kernel + for implementing Genode synchronization APIs but we rather rely on a + yielding spin lock for now. This has a number of drawbacks such as high + wake-up latencies in the contention case (depending on the scheduling + time slice), no support for priorities, and no fairness. Although it + is a simple and robust solution to start with, we plan to facilitate + the OKL4 kernel feature with our upcoming work. + +:Overly simplistic UTCB allocation: + + Right now, we allocate a fixed amount of 32 UTCBs per address space and + thereby limit the maximum number of threads per process. In the future, + this limit should be made configurable. + +:Managed dataspaces not yet supported: + + The support of managed dataspaces relies on the signal API, which is + not yet available for OKL4. + +:Message buffers are limited to 256 bytes: + + Because OKL4 performs message-based inter-process communication by + copying data between the UTCBs of the communicating threads, the + UTCB size constaints the maximum message size. Therefore, message + must not exceed 256 bytes. This is not a huge problem for the currently + available Genode programs but we can imagine session argument-lists + to become larger in the future. + +:Advanced thread functions are incomplete: + + Thread functions such as querying registers of remote threads are not yet + implemented. + + +Integration of Qt4 into the mainline repository +############################################### + +Qt4 is a tool kit for developing platform-independent applications. It +comprises a complete platform-abstraction layer and a rich GUI tool kit +widely used for commercial and open-source applications. It is particularly +known as the technical foundation of the KDE project. The previous Genode +release was accompanied by a snapshot of our initial port of Qt4 to Genode. For +the current release, we have turned this proof-of-concept implementation into a +properly integrated part of the Genode mainline development. This enables Qt4 +applications to be executed natively on the full range of kernels supported by +Genode. + + +Usage +===== + +We complemented Genode's source tree with the new 'qt4' source-code repository, +which contains the Genode-specific parts of the Qt4 framework. The most +portions for the Qt4 framework are used unmodified and thereby have not been +made part of the Genode source tree. Instead, we fetch the original Qt4 source +code from Trolltech's FTP server. This way, our source tree remains tidy and +neat. + +For using Qt4 for your Genode applications, you first need to download and +prepare the original Qt4 source codes and build a few Qt4 tools such as the +meta-object compiler and the resource compiler. The makefile found in the +top-level directory of the 'qt4' repository automates this task: + +! make prepare + +To include the 'qt4' repository into the Genode build process, just add the +'qt4' directory to the 'REPOSITORIES' declaration of the 'etc/build.conf' file +within your build directory. Make sure that the repositories 'demo' and 'libc' +are included as well. The 'qt4' repository comes with a couple of demo applications. +The 'qt_launchpad' is especially interesting because it makes use of both the +Qt4 framework and the Genode framework in one application. + + +Features and limitations +======================== + +The Qt4 port comprises Qt's Core library, GUI library, Script library, XML +library, and the UI tools library. + +For using Qt4 on the Linux version of Genode, we recommend using the Genode +tool chain rather than your host's tool chain. Qt4 makes use of a lot of libc +functionality, supplied by Genode's 'libc' repository. However, on Linux we +still link against your host's libc. This becomes a problem if your host +compiler's C++ support code references libc functionality that is not part of +Genode's libc. Thereby the linker will silently create references to glibc +symbols, making both libraries collide. So if using Qt4, we recommend using the +Genode tool chain: + +:[http://genode.org/download/tool-chain]: + Information about downloading and using the Genode tool chain + + +USB support +########### + +This release introduces the first fragments of USB support to Genode, taking +the USB human-interface device (HID) class as starting point. With this work, +we follow our approach of reusing unmodified Linux device drivers executed +within a device-driver environment called DDE Linux. In the previous release, +we already utilized this approach for realizing basic networking on Genode. +With this release, we complement DDE Linux with support required by USB +drivers. We are grateful for being able to base our implementation on the +excellent foundation laid by Dirk Vogt. He described his work in +[http://os.inf.tu-dresden.de/papers_ps/vogt-beleg.pdf - USB for the L4 environment]. + +For USB HID support, we added the Linux USB and input subsystems to the DDE +Linux 2.6 framework. Besides the 'dde_linux26/net.h' API for network drivers +added in Genode 9.02, the current version also includes APIs for input +('dde_linux26/input.h') and USB ('dde_linux26/usb.h'). We intend these +interfaces to mature towards generic driver-library APIs in the future. For +example, BSD-based drivers shall transparently provide the same functionality +as the current Linux drivers, which permits the simple reuse of driver server +implementations. + +[image img/usb_current] + +Image [img/usb_current] illustrates the current implementation of the USB-based +human-interface device (HID) driver. In this monolithic setup, all parts of the +USB stack and the device API are executed within one address space. These parts +are + +* Input server glue code +* HID driver and input subsystem +* Core functions for management of USB request buffers (URBs), + attached devices, and registered drivers +* Host controller drivers for UHCI, OHCI, and EHCI + +[image img/usb_aspired] + +We regard this as an intermediate step towards our goal to decompose the USB +stack. Image [img/usb_aspired] shows our aspired design. In this design, the +USB server and one or more USB gadget drivers run in dedicated address spaces. +The USB server provides two interfaces called USB session interface and USB +device interface. A USB session interface corresponds to a virtual root hub, +from which USB devices can be queried. The client of the USB session interface +is usually an USB gadget driver that uses the USB device interface. Because +this interface is used for transferring the actual payload at a potentially +high bandwidth, it is based on shared memory and signals. The USB server +consists of the following components: + +* USB server glue code +* Virtual USB device driver managing all attached devices +* Core functions including hardware hub management +* Host controller drivers + +The USB server presents a virtual USB hub to each USB gadget driver. Such +a driver consists of: + +* Device interface, e.g., input server glue code +* Gadget driver, e.g., HID driver and input subsystem +* Core functions +* Virtual host controller +* USB client glue code + +The HID driver uses the USB session API to monitor ports of its virtual root +hub and submit URBs to attached devices. The session interface facilitates the +signalling framework for event notification and a shared-memory dataspace for +URB transmission. + +The 'os' repository already contains the USB session and USB device interfaces. +However, the decomposition is not yet in a functional state. + + +:Current limitations: + +The current monolithic implementation of the USB HID service can already be +used as a replacement of the PS/2 driver. However, both drivers cannot be used +at the same time, yet. To enable the use of both USB HID and PS/2, we plan to +create a further component that merges multiple streams of input events and +passes the result to the GUI server. + + +OKLinux on Genode +################# + +According to our road map, we pursued the goal to run Linux as a node in +Genode's process tree. We explored two approaches: + +:Reanimating the Afterburner project conducted by the [http://l4ka.org - L4Ka group]: + This approach is the result of the L4Ka groups's long-year experience with + manually supporting L4Linux on top of the L4ka::Pistachio kernel. Because of + the high costs of maintaining the paravirtualized Linux kernel, a + semiautomatic paravirtualization technique was created. According to the + impressive results presented in + [http://l4ka.org/publications/paper.php?docid=2025 - Pre-Virtualization: Soft Layering for Virtual Machines], + this approach is able to drastically reduce maintenance costs while retaining + good performance. Furthermore, the approach was applied not only to Linux + running on the L4 kernel but also for using Xen or Linux as underlying + host operating systems. + +:Porting the OKL4-specific version of L4Linux to Genode: + Open Kernel Labs maintain a custom version of L4Linux that runs on OKL4. This + version is mostly referred to as OKLinux aka Wombat. Since Genode can now use OKL4 + as base platform, the reuse of OKLinux in combination with Genode has become + a feasible option. + +Both approaches have pros and cons. Whereas Afterburner is a intriguing +approach, this project seems to be stalled. It relies on a rather old tool +chain, and recent Linux features such as thread-local storage support are not +considered, yet. To pick up this solution for Genode will require us to fully +understand the mechanisms and the code. So we consider this as a mid-term +solution. In short term, running OKLinux on Genode is more feasible. We were +already able to create a prototype version of OKLinux running on Genode. This +version starts up the kernel including all Linux kernel threads, mounts the +boot partition supplied as memory image, and starts the init process. The +engineering costs had been rather low. We replaced the Iguana user land +libraries as originally used by Wombat by a Genode-specific reimplementation to +keep our manual adaptions of the Linux kernel code as small as possible. +Our custom reimplementation of the needed Iguana APIs consists of less than +1,000 lines of code (SLOC). The diff for our changes to the OKLinux kernel code +comprises less than 1,000 lines. We plan to make a snapshot of this prototype +publicly available soon. + + +Operating-system services and libraries +####################################### + +Nitpicker GUI server +==================== + +We optimized the performance of the 'refresh' call, which updates all views of +a session, which display a given buffer portion. The new implementation restricts +the redraw operations to the fragment of each view that displays the specified +buffer portion. The performance improvement becomes most visible when updating +only small parts of a buffer. + + +USB session interface +===================== + +Genode's emerging USB support introduces two new interfaces to the 'os' repository, +which are called USB session and USB device. + +An _USB_session_ is a virtual USB bus with just one root hub with 'MAX_PORTS' +downstream ports. The client of such as session submits USB request blocks +(URBs) and is, in turn, informed about port changes on the root hub as well as +request completion. Connected USB devices can be referenced by USB device +capabilities and are associated with one port at the virtual root hub on +server side. + +An _USB_device_ references a hardware device connected to a virtual USB bus's +root hub. The device capability enables the client to send USB request +blocks to the hardware device. + + +Input interface +=============== + +We updated the key codes of the input interface in response to recent changes +of Linux' 'dev/event' definitions. + + +VESA driver +=========== + +Until now, there existed different processes that tried to access the PCI bus +via I/O ports, in particular the VESA framebuffer driver and the PCI bus +driver. + +Since core enforces that each I/O port can only be assigned exclusively to one +component in the system, multiple processes that need access to the same I/O +ports cannot run at the same time. For our default demonstration scenario, we +had been able to allow the VESA driver to use the PCI I/O ports because nobody +else needed them. However, our growing base of device drivers relies on the +PCI bus driver. To be able to use the VESA driver together with other drivers, +we virtualized the access to the PCI bus from within the VESA driver. + +Our current PCI virtualization code is pretty limited. The VESA driver sees a +virtual PCI bus with only the VGA card attached. For now, we only allow reading +the PCI configuration space of this device, but not writing to it. Apparently, +this simple approach is sufficient to run the VESA BIOS of Qemu. However, other +VESA BIOS implementations may need further access to the PCI device's +configuration space. For example, for querying the size of a PCI resource, +write access to base address registers is required. In such an event, the VESA +driver will print a message about the missing virtualization feature: +! writing data register not supported +If you see such a message, we are very interested to see your log output such +that we can enhance our PCI virtualization code as needed. Please contact us! + + +Base framework +############## + +In the process of bringing Genode to the OKL4 kernel, we have generalized much +of former platform-specific code: + +* The initialization of C++ exception handling has now become part of the + generic 'cxx' library in the 'base' repository. All platforms except + Linux are using this generic library now. + +* The 'server' library used to contain a platform-specific part that + implemented the 'manage' function of a 'Server_entrypoint'. The + generalized version of this library is now being used on all platforms + other than Linux. + +* We unified core-internal interfaces and their implementations such as + 'Dataspace_component', 'Cap_session_component', 'Rm_session_component', + and 'Irq_session_component'. The result has become part of the 'base' + repository. + +* On OKL4, threads need to execute small startup code for querying their + own thread IDs. Therefore, we have extended the 'Thread_base' interface + with a platform-specific hook function called '_thread_bootstrap'. + +* The types defined in 'base/native_types.h' had been complemented by a + new 'Native_thread_id' type. This type is exclusively used by core and the + framework libraries. For using the Genode API, this type is meaningless. + +* For the 64bit support, we slightly refined the interfaces of some utility + template functions in 'util/misc_math.h'. Furthermore, parts of the generic + marshalling code of the IPC framework needed refinement, but no API changes + were needed. + + +Linux-specific changes +###################### + +Adaptation to 64 bit +==================== + +Because most Genode developers tend to work with the Linux version of Genode, +supporting 64-bit Linux becomes increasingly important. With the current release, +we start to officially support 64-bit Linux as base platform. This comes +along with the following changes: + +* We replaced the 'spec-x86.mk' file with new 'spec-x86_32.mk' and 'spec-x86_64.mk' + files. The default version of 'base-linux/etc/specs.conf' automatically + chooses the right spec file according to the output of 'uname -m'. Therefore, + output of the build processes matches your host architecture. This behaviour + can be changed by placing a customized 'spec.conf' file in your build directory's + 'etc/' subdirectory. + +* We added type definitions for 64-bit-specific fixed-size integers in the form + of a 64-bit-specific 'fixed_stdint.h' file. + +* Because using 64 bit instead of 32 bit changes the payload size of RPC + messages, we had to adjust several message buffers such as 'Ram_session_client' + and 'Input::Session_client', and adapted the used stack sizes. + +* Towards the goal of completely dissolving Genode's dependency on the Linux' glibc, + we implemented custom system-call bindings. Apparently, Linux' syscall interface + differs between 32 bit and 64 bit. For example, the 32-bit version handles + all socket-related calls via a compound 'socketcall' whereas the 64-bit + version uses distinct syscalls. Another difference is the handling of the + 'mmap' syscall and different behaviour of 'tkill'. The latter problem was + resolved by replacing 'tkill' with 'tgkill' and setting the thread-group + argument of the corresponding PID. Therefore, a 'Native_thread_id' on Linux + now comprises both the TID and the PID. + +* The 'Platform_env' on Linux contains a local implementation of the 'Rm_session' + interface, which uses 'mmap' to attach dataspaces to the process' address + space and maintains the region list in the form of a static array. This array + was dimensioned to 256 entries, which constrained the maximum amount of + usable memory when allocating a lot of small blocks via Genode's heap. Since + the heap allocates backing store at the granularity of only 16KB, the worst + case for reaching this limit was about 4MB. This was OK for our simple test + applications. But for using Qt4, in particular on 64 bit, this has become a + serious limitation. For now, we increased the region limit to 4096 and plan + to replace the static array with a dynamically growing data structure. + Furthermore, we made the heap granularity depend on the actual machine-word + size. Therefore, the heap allocates its backing store in 32KB blocks when + running on 64 bit. + + +Debugging hooks +=============== + +On Linux, we use gdb for debugging Genode. This is feasible as long as the +targeted process is running. However, during low-level debugging, we had the +recurring problem of a thread dying shortly after starting up. So we added a hook +for halting a thread at the startup in order to be able to attach gdb to the +thread before it dies. This simple hook lets the thread wait for a key press by +directly calling the 'read' syscall. We also added a simple debug facility for +printing debug messages bypassing Genode's LOG service by calling the 'write' +syscall directly. Both hooks are now part of the Linux version of the 'env' +library (see 'base-linux/src/base/env/debug.cc'). Note that these hooks are not +part of the Genode API. There exists no header file. + diff --git a/doc/release_notes-09-08.txt b/doc/release_notes-09-08.txt new file mode 100644 index 000000000..610666941 --- /dev/null +++ b/doc/release_notes-09-08.txt @@ -0,0 +1,573 @@ + + + ============================================== + Release notes for the Genode OS Framework 9.08 + ============================================== + + Genode Labs + + +Whereas the previous releases were focused on adding features to the framework, +the overall theme for the current release 9.08 was refinement. We took the +chance to revisit several parts of the framework that we considered as +interim solutions, and replaced them with solid and hopefully long-lasting +implementations. Specifically, we introduce a new lock implementation, a new +timer service, a platform-independent signalling mechanism, a completely +reworked startup code for all platforms, and thread-local storage support. +Even though some of the changes touches fundamental mechanisms, we managed +to keep the actual Genode API almost unmodified. + +With regard to features, the release introduces initial support for dynamic +linking, a core extension to enable a user-level variant of Linux to run on the +OKL4 version of Genode, and support for super pages and write-combined I/O +memory access on featured L4 platforms. + +The most significant change for the Genode Linux version is the grand unification with +the other base platforms. Now, the Linux version shares the same linker script +and most of the startup code with the supported L4 platforms. Thanks to our +evolved system-call bindings, we were further able to completely dissolve +Genode's dependency from Linux's glibc. Thereby, the Linux version of Genode is +on the track to become one of the lowest-complexity (in terms of source-code +complexity) Linux-kernel-based OSes available. + + +Base framework +############## + +New unified lock implementation +=============================== + +Since the first Genode release one year ago, the lock implementation had been +a known weak spot. To keep things simple, we employed a yielding spinlock +as basic synchronization primitive. All other thread-synchronization +mechanisms such as semaphores were based on this lock. In principle, the +yielding spinlock used to look like this: + +! class Lock { +! private: +! enum Lock_variable { UNLOCKED, LOCKED }; +! Lock_variable _lock_variable; +! +! public: +! void lock() { +! while (!cmpxchg(&_lock_variable, UNLOCKED, LOCKED)) +! yield_cpu_time(); +! } +! +! void Lock::unlock() { _lock_variable = UNLOCKED; } +! } + +The compare-exchange is an atomic operation that compares the current value +of '_lock_variable' to the value 'UNLOCKED', and, if equal, replaces the +value by 'LOCKED'. If this operation succeeds, 'cmpxchg' returns true, which +means that the lock acquisition succeeded. Otherwise, we know that the lock +is already owned by someone else, so we yield the CPU time to another thread. + +Besides the obvious simplicity of this solution, it does require minimal +CPU time in the non-contention case, which we considered to be the common case. In +the contention case however, this implementation has a number of drawbacks. +First, the lock is not fair, one thread may be able to grab and release the +lock a number of times before another thread has the chance to be +scheduled at the right time to proceed with the lock acquisition if the lock +is free. Second, the lock does not block the acquiring thread but lets it +actively spin. This behavior consumes CPU time and slows down other threads that +do real work. Furthermore, this lock is incompatible with the use of thread +priorities. If the lock is owned by a low-priority thread and a high-priority +thread tries to acquire a lock, the high-priority thread keeps being active +after calling 'yield_cpu_time()'. Therefore the lock owner starves and has no +chance to release the lock. This effect can be partially alleviated by replacing +'yield_cpu_time()' by a sleep function but this work-around implies higher +wake-up latencies. + +Because we regarded this yielding spinlock as an intermediate solution since the +first release, we are happy to introduce a completely new implementation now. +The new implementation is based on a wait queue of lock applicants that are +trying to acquire the lock. If a thread detects that the lock is already +owned by another thread (lock holder), it adds itself into the wait queue +of the lock and calls a blocking system call. When the lock owner releases +the lock, it wakes up the next member of the lock's wait queue. +In the non-contention case, the lock remains as cheap as the yielding +spinlock. Because the new lock employs a fifo wait queue, the lock guarantees +fairness in the contention case. The implementation has two interesting points +worth noting. In order to make the wait-queue operations thread safe, we use a simple +spinlock within the lock for protecting the wait queue. In practice, we +measured that there is almost never contention for this spin lock as two +threads would need to acquire the lock at exactly the same time. Nevertheless, +the lock remains safe even for this case. Thanks to the use of the additional spinlock within +the lock, the lock implementation is extremely simple. The seconds interesting +aspect is the base mechanism for blocking and waking up threads such +that there is no race between detecting contention and blocking. +On Linux, we use 'sleep' for blocking and 'SIGUSR1' to cancel the sleep operation. +Because Linux delivers signals to threads at kernel entry, +the wake-up signal gets reliably delivered even if it occurs prior +thread blocking. On OKL4 and Pistachio, we use the exchange-registers +('exregs') system call for both blocking and waking up threads. Because 'exregs' +returns the previous thread state, the sender of the wake-up +signal can detect if the targeted thread is already in a blocking state. +If not, it helps the thread to enter the blocking state by a thread-switch +and then repeats the wake-up. Unfortunately, Fiasco does not support the +reporting of the previous thread state as exregs return value. On this kernel, +we have to stick with the yielding spinlock. + + +New Platform-independent signalling mechanism +============================================= + +The release 8.11 introduced an API for asynchronous notifications. Until +recently, however, we have not used this API to a large extend because it +was not supported on all platforms (in particular OKL4) and its implementation +was pretty heavy-weight. Until now signalling required one additional thread for each signal +transmitter and each signal receiver. The current release introduces a +completely platform-independent light-weight (in terms of the use of +threads) signalling mechanism based on a new core service called SIGNAL. +A SIGNAL session can be used to allocate multiple signal receivers, each +represented by a unique signal-receiver capability. Via such a capability, +signals can be submitted to the receiver's session. The owner of a SIGNAL +session can receive signals submitted to the receivers of this session +by calling the blocking 'wait_for_signal' function. Based on this simple +mechanism, we have been able to reimplement Genode's signal API. Each +process creates one SIGNAL session at core and owns a dedicated thread +that blocks for signals submitted to any receiver allocated by the process. +Once, the signal thread receives a signal from core, it determines +the local signal-receiver context and dispatches the signal accordingly. + +The new implementation of the signal API required a small refinement. +The original version allowed the specification of an opaque argument +at the creation time of a signal receiver, which had been delivered with +each signal submitted to the respective receiver. The new version replaces +this opaque argument with a C++ class called 'Signal_context'. This allows +for a more object-oriented use of the signal API. + + +Generic support for thread-local storage +======================================== + +Throughout Genode we avoid relying on thread-local storage (TLS) and, in fact, +we had not needed such a feature while creating software solely using the +framework. However, when porting existing code to Genode, in particular Linux +device drivers and Qt-based applications, the need for TLS arises. For such +cases, we have now extended Genode's 'Thread' class with generic TLS +support. The static function 'Thread_base::myself()' returns a pointer to the +'Thread_base' object of the calling thread, which may be casted to a inherited +thread type (holding TLS information) as needed. + +The 'Thread_base' object is looked up by using the current stack pointer +as key into an AVL tree of registered stacks. Hence, the lookup traverses a +plain data structure and does not rely on platform-dependent CPU features +(such as 'gs' segment-register TLS lookups on Linux). + +Even though, Genode does provide a mechanism for TLS, we strongly discourage +the use of this feature when creating new code with the Genode API. A clean +C++ program never has to rely on side effects bypassing the programming +language. Instead, all context information needed by a function to operate, +should be passed to the function as arguments. + + +Core extensions to run Linux on top of Genode on OKL4 +##################################################### + +As announced on our road map, we are working on bringing a user-level variant +of the Linux kernel to Genode. During this release cycle, we focused on +enabling OKLinux aka Wombat to run on top of Genode. To run Wombat on Genode we +had to implement glue code between the wombat kernel code and the Genode API, +and slightly extend the PD service of core. + +The PD-service extension is a great show case for implementing inheritance +of RPC interfaces on Genode. The extended PD-session interface resides +in 'base-okl4/include/okl4_pd_session' and provides the following additional +functions: + +! Okl4::L4SpaceId_t space_id(); +! void space_pager(Thread_capability); + +The 'space_id' function returns the L4 address-space ID corresponding to +the PD session. The 'space_pager' function can be used to set the +protection domain as pager and exception handler for the specified +thread. This function is used by the Linux kernel to register itself +as pager and exception handler for all Linux user processes. + +In addition to the actual porting work, we elaborated on replacing the original +priority-based synchronization scheme with a different synchronization mechanism +based on OKL4's thread suspend/resume feature and Genode locks. This way, all +Linux threads and user processes run at the same priority as normal Genode +processes, which improves the overall (best-effort) performance and makes +Linux robust against starvation in the presence of a Genode process that is +active all the time. + +At the current stage, we are able to successfully boot OKLinux on Genode and +start the X Window System. The graphics output and user input are realized +via custom stub drivers that use Genode's input and frame-buffer interfaces +as back ends. + +We consider the current version as a proof of concept. It is not yet included +in the official release but we plan to make it a regular part of the official +Genode distribution with the next release. + + +Preliminary shared-library support +################################## + +Our Qt4 port made the need for dynamically linked binaries more than evident. +Statically linked programs using the Qt4 library tend to grow far beyond 10MB +of stripped binary size. To promote the practical use of Qt4 on Genode, we +ported the dynamic linker from FreeBSD (part of 'libexec') to Genode. +The port consists of three parts + +# Building the 'ldso' binary on Genode, using Genode's parent interface to + gain access to shared libraries and use Genode's address-space management + facilities to construct the address space of the dynamically loaded program. +# Adding support for the detection of dynamically linked binaries, the starting + of 'ldso' in the presence of a dynamically linked binary, and passing the + program's binary image to 'ldso'. +# Adding support for building shared libraries and dynamically linked + programs to the Genode build system. + +At the current stage, we have completed the first two steps and are able to +successfully load and run dynamically linked Qt4 applications. Thanks to +dynamic linking, the binary size of Qt4 programs drops by an order of +magnitude. Apparently, the use of shared qt libraries already pays off when +using only two Qt4 applications. + +You can find our port of 'ldso' in the separate 'ldso' repository. We will +finalize the build-system integration in the next weeks and plan to support +dynamic linking as regular feature as part of the 'os' repository with the next +release. + + +Operating-system services and libraries +####################################### + +Improved handling of XML configuration data +=========================================== + +Genode allows for configuring a whole process tree via a single configuration +file. Core provides the file named 'config' as a ROM-session dataspace to the +init process. Init attaches the dataspace into its own address space and +reads the configuration data via a simple XML parser. The XML parser takes +a null-terminated string as input and provides functions for traversing the +XML tree. This procedure, however, is a bit flawed because init cannot +expect XML data provided as a dataspace to be null terminated. On most platforms, +this was no problem so far because boot modules, as provided by core's ROM +service, used to be padded with zeros. However, there are platforms, in particular +OKL4, that do not initialize the padding space between boot modules. In this +case, the actual XML data is followed by arbitrary bits but possibly no null +termination. Furthermore, there exists the corner case of using a config +file with a size of a multiple of 4096 bytes. In this case, the null termination +would be expected just at the beginning of the page beyond the dataspace. + +There are two possible solutions for this problem: copying the content of +the config dataspace to a freshly allocated RAM dataspace and appending the +null termination, or passing a size-limit of the XML data to the XML parser. +We went for the latter solution to avoid the memory overhead of copying +configuration data just for appending the null termination. Making the XML +parser to respect a string-length boundary involved the following changes: + +* The 'strncpy' function had to be made robust against source strings that are not + null-terminated. Strictly speaking, passing a source buffer without + null-termination violates the function interface because, by definition, + 'src' is a string, which should always be null-terminated. The 'size' + argument usually refers to the bound of the 'dst' buffer. However, in our + use case, for the XML parser, the source string may not be properly terminated. + In this case, we want to ensure that the function does not read any characters + beyond 'src + size'. +* Enhanced 'ascii_to_ulong' function to accept an optional size-limitation + argument +* Added support for size-limited tokens in 'base/include/util/token.h' +* Added support for constructing an XML node from a size-limited string +* Adapted init to restrict the size of the config XML node to the file size + of the config file + + +Nitpicker GUI server +==================== + +* Avoid superfluous calls of 'framebuffer.refresh()' to improve the overall + performance + +* Fixed stacking of views behind all others, but in front of the background. + This problem occurred when seamlessly running another window system as + Nitpicker client. + + +Misc +==== + +:Alarm framework: + + Added 'next_deadline()' function to the alarm framework. This function is + used by the timer server to program the next one-shot timer interrupt, + depending on the scheduled timeouts. + +:DDE Kit: + + * Implemented 'dde_kit_thread_usleep()' and 'dde_kit_thread_nsleep()' + * Removed unused/useless 'dde_kit_init_threads()' function + +:Qt4: + + Added support for 'QProcess'. This class can be used to start Genode + applications from within Qt applications in a Qt4-compatible way. + + +Device drivers +############## + +New single-threaded timer service +================================= + +With the OKL4 support added with the previous release, the need for a new timer +service emerged. In contrast to the other supported kernels, OKL4 imposed two +restrictions, which made the old implementation unusable: + +* The kernel interface of OKL4 does not provide a time source. The kernel + uses a APIC timer internally to implement preemptive scheduling but, in + contrast to other L4 kernels that support IPC timeouts, OKL4 does not + expose wall-clock time to the user land. Therefore, the user land has to + provide a timer driver that programs a hardware timer, handles timer + interrupts, and makes the time source available to multiple clients. + +* OKL4 restricts the number of threads per address space according to a + global configuration value. By default, the current Genode version set + this value to 32. The old version of the timer service, however, employed + one thread for each timer client. So the number of timer clients was + severely limited. + +Motivated by these observations, we created a completely new timer service that +dispatches all clients with a single thread and also supports different time +sources as back ends. For example, the back ends for Linux, L4/Fiasco, and +L4ka::Pistachio simulate periodic timer interrupts using Linux' 'nanosleep' system +call - respective IPC timeouts. The OKL4 back end contains a PIT driver +and operates this timer device in one-shot mode. + +To implement the timer server in a single-threaded manner, we used an +experimental API extension to Genode's server framework. Please note that we +regard this extension as temporary and will possible remove it with the next +release. The timer will then service its clients using the Genode's signal API. + +Even though the timer service is a complete reimplementation, its interface +remains unmodified. So this change remains completely transparent at the API level. + + +VESA graphics driver +==================== + +The previous release introduced a simple PCI-bus virtualization into the VESA +driver. At startup, the VESA driver uses the PCI bus driver to find a VGA card +and provides this single PCI device to the VESA BIOS via a virtual PCI bus. All +access to the virtualized PCI device are then handled locally by the VESA +driver. In addition to PCI access, some VESA BIOS implementations tend to use +the programmable interval timer (PIT) device at initialization time. Because we +do not want to permit the VESA BIOS to gain access to the physical timer +device, the VESA driver does now provide an extremely crippled virtual PIT. +Well, it is just enough to make all VESA BIOS implementations happy that we +tested. + +On the feature side, we added support for VESA mode-list handling and a +default-mode fallback to the driver. + + +Misc +==== + +:SDL-based frame buffer and input driver: + + For making the Linux version of Genode more usable, we complemented the + existing key-code translations from SDL codes to Genode key codes. + +:PS/2 mouse and keyboard driver: + + Improved robustness against ring-buffer overruns in cases where input events + are produced at a higher rate than they can be handled, in particular, if + there is no input client connected to the driver. + + +Platform-specific changes +######################### + +Support for super pages +======================= + +Previous Genode versions for the OKL4, L4ka::Pistachio, and L4/Fiasco kernels used +4K pages only. The most visible implication was a very noticeable delay during +system startup on L4ka::Pistachio and L4/Fiasco. This delay was caused by core +requesting the all physical memory from the root memory manager (sigma0) - +page by page. Another disadvantage of using 4K pages only, is the resulting TLB footprint +of large linear mappings such as the frame buffer. Updating a 10bit frame buffer +with a resolution of 1024x768 would touch 384 pages and thereby significantly +pollute the TLB. + +This release introduces support for super pages for the L4ka::Pistachio and +L4/Fiasco versions of Genode. In contrast to normal 4K pages, a super page +describes a 4M region of virtual memory with a single entry in the page +directory. By supporting super pages in core, the overhead of the startup +protocol between core and sigma0 gets reduced by a factor of 1000. + +Unfortunately, OKL4 does not support super pages such that this feature remains +unused on this platform. However, since OKL4 does not employ a root memory +manager, there is no startup delay anyway. Only the advantage of super pages +with regard to reduced TLB footprint is not available on this platform. + + +Support for write-combined access to I/O memory +=============================================== + +To improve graphics performance, we added principle support for write combined I/O access +to the 'IO_MEM' service of core. The creator of an 'IO_MEM' session can now specify the +session argument "write_combined=yes" at session-creation time. Depending on the +actual base platform, core then tries to establish the correct page-table +attribute configuration when mapping the corresponding I/O dataspace. Setting +caching attributes differs for each kernel: + +* L4ka::Pistachio supports a 'MemoryControl' system call, which allows for specifying + caching attributes for a core-local virtual address range. The attributes are + propagated to other processes when core specifies such a memory range + as source operand during IPC map operations. However, with the current version, + we have not yet succeeded to establish the right attribute setting, so the performance + improvement is not noticeable. + +* On L4/Fiasco, we fully implemented the use of the right attributes for marking + the frame buffer for write-combined access. This change significantly boosts + the graphics performance and, with regard to graphics performance, serves us + as the benchmark for the other kernels. + +* OKL4 v2 does not support x86 page attribute tables. So write-combined access + to I/O memory cannot be enabled. + +* On Linux, the 'IO_MEM' service is not yet used because we still rely on libSDL + as hardware abstraction on this platform. + + +Unification of linker scripts and startup codes +=============================================== + +During the last year, we consistently improved portability and the support for +different kernel platforms. By working on different platforms in parallel, +code duplications get detected pretty easily. The startup code was a steady +source for such duplications. We have now generalized and unified the startup +code for all platforms: + +* On all base platforms (Linux-x86_32, Linux-x86_64, OKL4, L4ka::Pistachio, and + L4/Fiasco) Genode now uses the same linker script for statically linked + binaries. Therefore, the linker script has now become part of the 'base' + repository. + +* We unified the assembly startup code ('crt0') for all three L4 platforms. + Linux has a custom crt0 code residing in 'base-linux/src/platform'. For + the other platforms, the 'crt0' codes resides in the 'base/src/platform/' + directory. + +* We factored out the platform-depending bits of the C++ startup code + ('_main.cc') into platform-specific '_main_helper.h' files. The '_main.cc' + file has become generic and moved to 'base/src/platform'. + + +Linux +===== + +With the past two releases, we successively reduced the dependency of the +Linux version of core from the 'glibc'. Initially, this step had been +required to enable the use of our custom libc. For example, the 'mmap' +function of our libc uses Genode primitives to map dataspace to the +local address space. The back end of the used Genode functions, in turn, +relied on Linux' 'mmap' syscall. We cannot use syscall bindings provided +by the 'glibc' for issuing the 'mmap' syscall because the +binding would clash with our libc implementation of 'mmap'. Hence we +started to define our own syscall bindings. + +With the current version, the base system of Genode has become completely +independent of the 'glibc'. Our custom syscall bindings for the x86_32 and +x86_64 architectures reside in 'base-linux/src/platform' and consist of +35 relatively simple functions using a custom variant of the 'syscall' +function. The only exception here is the clone system call, which requires +assembly resides in a separate file. + +This last step on our way towards a glibc-free Genode on Linux pushes the +idea to only use the Linux kernel but no further Linux user infrastructure +to the max. However, it is still not entirely possible to build a Linux +based OS completely based on Genode. First, we have to set up the loopback +device to enable Genode's RPC communication over sockets. Second, we +still rely on libSDL as hardware abstraction and libSDL, in turn, relies +on the glibc. + +:Implications: + +Because the Linux version is now much in line with the other kernel platforms, +using custom startup code and direct system calls, we cannot support +host tool chains to compile this version of Genode anymore. Host tool chains, in +particular the C++ support library, rely on certain Linux features +such as thread-local storage via the 'gs' segment registers. These things are +normally handled by the glibc but Genode leaves them uninitialized. +To build the Linux version of Genode, you have to use the official +Genode tool chain. + + +OKL4 +==== + +The build process for Genode on OKL4 used to be quite complicated. Before +being able to build Genode, one had to build the original Iguana user land +of OKL4 because the Genode build system looked at the Iguana build directory +for the L4 headers actually used. We now have simplified this process by +not relying on the presence of the Iguana build directory anymore. All +needed header files are now shadowed from the OKL4 source tree +to an include location within Genode's build directory. Furthermore, we +build Iguana's boot-info library directly from within the Genode build system, +instead of linking the binary archive as produced by Iguana's build process. + +Of course, to run Genode on OKL4, you still need to build the OKL4 kernel +but the procedure of building the Genode user land is now much easier. + +Misc changes: + +* Fixed split of unmap address range into size2-aligned flexpages. The + 'unmap' function did not handle dataspaces with a size of more than 4MB + properly. +* Fixed line break in the console driver by appending a line feed to + each carriage return. This is in line with L4/Fiasco and L4ka::Pistachio, + which do the same trick when text is printed via their kernel debugger. + + +L4ka::Pistachio +=============== + +The previous version of core on Pistachio assumed a memory split of 2GB/2GB +between userland and kernel. Now, core reads the virtual-memory layout from +the kernel information page and thereby can use up to 3GB of virtual memory. + +*Important:* Because of the added support for super pages, the Pistachio +kernel must be built with the "new mapping database" feature enabled! + + +L4/Fiasco +========= + +Removed superfluous zeroing-out of the memory we get from sigma0. This change +further improves the startup performance of Genode on L4/Fiasco. + + +Build infrastructure +#################### + +Tool chain +========== + +* Bumped binutils version to 2.19.1 +* Support both x86_32 and x86_64 +* Made tool_chain's target directory customizable to enable building and + installing the tool chain with user privileges + + +Build system +============ + +* Do not include dependency rules when cleaning. This change brings not + only a major speedup but it also prevents dependency rules from messing + with generic rules, in particular those defined in 'spec-okl4.mk'. + +* Enable the use of '-ffunction-sections' combined with '-gc-sections' + by default and thereby reduce binary sizes by an average of 10-15%. + +* Because all base platforms, including Linux, now depend on the Genode tool + chain, the build system uses this tool chain as default. You can still + override the tool chain by creating a custom 'etc/tools.conf' file + in your build directory. diff --git a/doc/release_notes-09-11.txt b/doc/release_notes-09-11.txt new file mode 100644 index 000000000..a41f5db27 --- /dev/null +++ b/doc/release_notes-09-11.txt @@ -0,0 +1,1017 @@ + + + ============================================== + Release notes for the Genode OS Framework 9.11 + ============================================== + + Genode Labs + + +In contrast to the previous release, which had been mainly about important +refinements and optimizations under the hood, the release 9.11 is focused on +new features. + +Our brand new packet streaming framework enables the efficient communication of +bulk data between processes based on a shared-memory protocol and asynchronous +signalling. We put this new facility to use for the new NIC session interface. +This interface allows us to execute network drivers and network protocol stacks +in distinct processes. The most interesting current use case is the new +integration of the light-weight IP stack (lwIP) into Genode. +The most noticeable platform-related addition is the new support for the ARM +architecture to the OKL4 version of the framework. + +As with every release, we refined recently introduced features and tightly +integrated them into our mainline development. The most prominent of these +features is dynamic linking support, which was introduced with the previous +release and has now become fully integrated in the framework and the build +system. Also our steady improvement of the Linux device-driver +environment yields fruit in the form of USB storage support. With regard to +Qt4, we are proud to announce the availability of the Qt4/Webkit library +on all kernels supported by Genode. + +Furthermore, we added the paravirtualized Linux kernel called OKLinux to +the official Genode distribution. This variant of Linux can be executed +on top of the OKL4 version of Genode and provides a binary-compatible execution +environment for Linux programs alongside low-complexity native Genode +programs. + +This document compiles these and more changes between the versions 9.08 and +9.11 of Genode. It contains new bits of documentation and tries to put our +development into a broader context. + + +Base framework +############## + +The 'base-host' platform +======================== + +We added a new platform repository called 'base-host' to the Genode +distribution. This repository contains dummy implementations of +platform-specific Genode APIs to enable the compilation of Genode for the host +platform. Because the repository provides dummy implementations, most of the +generated binaries will not work. However, the repository serves two important +purposes. It documents all platform-specific APIs that must be filled out when +porting Genode to another platform, and it is the build environment for unit +tests executed on the host platform. + + +Signalling-framework refinements +================================ + +With our work on the packet-streaming facility described in Section +[Packet-stream interface], we discovered a not yet supported use case for the +signalling framework. + +The original implementation expected one or more signal-handling threads +to block or poll for signals from potentially different sources and dispatch +them in the order of arrival. +Such a thread would instantiate one signal receiver associated with +potentially many signal contexts (representing different signal sources). + +The new use case, however, requires one thread to be able to selectively handle +a subset of signal contexts at a time. The API already facilitated this +use case by a simple instantiation of multiple signal receivers and let one +thread handle signals for one or another signal source by querying the +different receivers. +Until now, this use case was not supported by the underlying implementation +because signals were submitted to signal receivers, which could only hold +one pending signal. A signal could only be supplied to a receiver if +there was no pending signal already stored at the receiver. Otherwise, +signal delivery for the complete process stalled. We have now changed +the implementation such that signals are always supplied to signal contexts +instead of receivers. This way, the order of signal arrival and signal +handling becomes completely decoupled and clears the way for a much more +flexible use of the signalling framework. + + +:Interface changes: + +Because the capability for signal submission refers to a signal context +rather than a signal receiver, we changed the class names of the signal API +accordingly. The previously called 'Signal_receiver_capability' is now called +'Signal_context_capability'. We also streamlined the interface of core's SIGNAL +service according to this new naming scheme. The latter change, however, is +completely transparent at the Genode API level. + + +C++ runtime improvements +======================== + +The base framework of Genode is written in C++, but without a C runtime +underneath. The C++ support libraries, however, use to depend on certain +functions normally provided by the C library. Therefore, Genode has to provide +custom implementations of these functions. This C++ runtime environment is +encapsulated in the 'cxx' library. We use to complement the 'cxx' library as +needed. + +One feature that was previously missing is proper synchronization of static +constructors. In contrast to constructors of global variables, which are +executed by the startup code before any other threads are created, static +constructors are executed lazily, potentially by different threads. A typical +static constructor looks like this: + +! Some_object *get_some_object() { +! static Some_object o; +! return &o; +! } + +When calling the function 'get_some_object' the first time, The instance of +'Some_object' is constructed at a static memory location. For all subsequent +calls of 'get_some_object', the once created object is not constructed again +but reused. This is a very handy alternative to global constructors when +objects inter-depend on each other. In contrast to the construction order +of global constructors, which is arbitrary, the call order of static constructors +is implicitly defined by the code such that object dependencies are recognised. +However, because static constructors are executed lazily, they may be called +by different threads. The previous version of 'cxx' had no synchronization +in place for protecting a static constructor from being concurrently +executed by more than one thread, resulting in a 'recursive_init_error' +run-time exception. + +With the Genode workloads getting more advanced and dynamic, we have seen +this condition to trigger and have added proper support for guarded static +C++ constructors into the 'cxx' library. + + +Library-based AVL tree +====================== + +Our AVL-tree implementation in 'base/include/util/avl_tree.h' is a fundamental +data structure for the framework. It is used at numerous places such as +memory allocators, address-space layout management, and the server-object +framework. Up to now, this implementation was a big template, instantiated +for each data type to organize. Moreover, most operations were implemented +using inline functions. By statically profiling the layout of Genode's +binaries, we observed that this inline code ended up multiple times in +the binaries. However, the program logic of all those instances was essentially +the same (e.g., how to perform a tree rotation). Only the policy (i.e., the +sort criterion) differs. We now have re-implemented the AVL tree as two parts, +the actual AVL-tree algorithm, which is independent from any template +parameters and resides in a library called 'avl_tree', and a policy-dependent +front-end template class residing in the 'avl_tree.h' header file. + +To our delight, this change reduced the average size of Genode's binaries +by 10%. For example, the core binary for OKL4 on x86 went from 305 KB +down to only 270 KB. + + +:Interface change: + +The new AVL-tree implementation comes along with a slight API change. The +operation to remove a node from an AVL tree used to be a member function of the +'Avl_node' object to remove. This function is now being provided by the +'Avl_tree' taking an 'Avl_node' as argument. Because the 'Avl_tree' is a +container of 'Avl_node' objects, this change makes the AVL tree more consistent +with other container classes such as 'List' and 'Fifo'. + + +Initial support for the ARM architecture +######################################## + +Right from the start of the project, the portability of the framework was a +primary concern. This is reflected by the framework's unique capability to +seamlessly run on four different kernels. With regard to the portability +among different CPU architectures, however, the development was focused on the +x86 architecture as this architecture is most common. With the release 9.11, +the project moves beyond the x86 architecture by adding support ARM CPUs and an +exemplary ARM-based SoC platform, namely GTA01. Because of all current Genode +base platforms the OKL4 is the most widely used kernel on ARM-based devices, +we have focused our efforts on this kernel first. The 'base-okl4' repository +comes now with support for the ARM-based GTA01 platform as used for the +Openmoko project. We choose this platform because it is supported +out-of-the-box by the OKL4 2.1.1 distribution. The ARM-specific code that +we had to add to the framework is surprisingly little. It covers the assembly +startup code for executables, support code for atomic operations, and the +platform driver for GTA01. Because the OKL4 kernels provides abstractions +for all other CPU-specific peculiarities, the code for all framework libraries +and components are the same for ARM and x86. This also includes the C++ +startup code and the linker script. + +The procedure for trying out the new ARM support with the GTA01 platform using +Qemu is decribed at a dedicated Wiki page: + + +:Genode/OKL4 on the GTA01 platfrom: + + [http://genode.org/community/wiki/GenodeOKL4OnTheGTA01Platform - Genode.org Community Wiki] + +Both the OKL4 version 2.1.1 and the GTA01 chip are not the most current +platforms but this combination turned out to be good as starting point. +Because we use OKL4 2.1.1 on a regular basis on x86, using this kernel on ARM +is an evolutionary intermediate step towards moving on to more recent kernels. + + +:Limitiations: + +* The platform driver for GTA01 is pretty limited. It is just as a + show case for running Genode on the Qemu-neo1973 emulator. The driver + is not tested on real hardware. +* This release contains the initial support, which currently covers the + base framework, the 'os', and the 'demo' repositories. Other repositories + such as 'libc', 'linux_drivers', and 'qt4' are not supported yet. +* Dynamic linking is not yet not supported on ARM + + +Paravirtualized Linux on Genode/OKL4 +#################################### + +OKLinux is a para-virtualized version of the Linux kernel running on top of the +micro-kernel OKL4. It enables us to execute Linux applications in the Genode +environment side-by-side with low-complexity native Genode applications, which +can implement security-critical functions without relying on the +high-complexity Linux kernel. Compared with most existing virtualization +solutions including Xen and KVM, the trusted computing base for such +security-critical components is one or more magnitudes smaller (the OKL4 kernel ++ Genode base framework are less than 30,000 lines of code). + +The original code of OKLinux relies on the Iguana framework - a bunch of +server components and libraries to simplify construction of applications +running on top of OKL4. The new 'oklinux' Genode repository contains a small +OKLinux support library, as well as a patch for OKLinux 2.6.23, that replaces +Iguana by the Genode framework. Nevertheless, our version of OKLinux stays to +be dependent on the OKL4 kernel, meaning that you can only use it in +combination with Genode running on top of OKL4. + +Usage +===== + +If you haven't build Genode for OKL4 yet, please refer to the following document: + +:[http://genode.org/community/wiki/GenodeOnOKL4 - Genode on OKL4 Wiki page]: + This Wiki page contains the information on how to build and use + Genode with OKL4. + +For building OKLinux for Genode, you first need to download and patch the +original sources. The top-level makefile of the 'oklinux' repository automates +this task. Just issue: + +! make prepare + +Afterwards you need to include the 'oklinux' repository into the Genode build +process. Just add the path to this directory to the 'REPOSITORIES' declaration +of the 'etc/build.conf' file within your build directory. +Now, you can change to your build directory and simply type: + +! make oklinux + +That's all. The 'bin/' directory within your build directory should now contain +a symbolic link to the 'vmlinux' binary. +To test your Linux binary, you also need to tweak the config file for init and +for the elfweaver tool. You will find examples for this in the 'config/' +directory of the 'oklinux' repository. Moreover, you will need to add a RAM disk +file to your setup as OKLinux for Genode only supports RAM disks by now. + +RAM disk +======== + +OKLinux provides a special block device driver, which uses a RAM disk as +backing-store. You can specify your RAM disk file on the kernel command line of +Linux by setting the 'igms_name=' parameter. +If you use a RAM-disk file that contains only a file system you have to set the +root parameter on the kernel command line to '/dev/igms0'. If your RAM disk +contains a whole partition table, state '/dev/igms0pn', whereby n stands for +the partition number containing the root file system. + +Kernel command line +=================== + +You can state the Linux kernel command line by using the XML config-file of the +init node that starts your Linux instance. In addition to the filename and +quota within the start section of Linux, you simply add the following: + +! +! igms_name=ramdisk root=/dev/igms0p1 +! + +Configure Linux +=============== + +This OKLinux package contains only a minimal Linux configuration. Especially, +any hardware drivers are missing, as Genode/OKL4 doesn't allow direct hardware +access from Linux. Instead, Linux accesses hardware indirectly through Genode +services. The current version of OKLinux comes with stub drivers for connecting +Linux to Genode's 'Input_session', 'Timer_session', and 'Framebuffer_session' +interfaces and we plan to add support for more device classes in the future. + +If you want to enable/disable options in Linux, you can simply do so by using +the normal Linux build system. You will find the '.config' file Linux is using +within the 'oklinux/' directory of your build directory. If you don't want to +tweak '.config' directly, you can also change to the 'oklinux/' directory of +your build directory and issue: + +! ARCH=l4 SYSTEM=i386 make menuconfig + +Then you will get the well known ncurses interface. + +Troubleshooting +=============== + +If you run into problems when building OKLinux and you want the build process +to be somehow more verbose, you can build OKLinux this way: + +! VERBOSE_LX_MK=1 make oklinux + +Example +======= + +The following screenshot shows Genode running on OKL4 with two instances +of OKLinux running. One instance booted the TinyCore Linux distribution +including the X Window System. The other instance booted a busybox-based +RAM Disk and runs with just about 16 MB of RAM. Each Linux kernel uses +a separate instance of the Liquid FB virtual frame buffer: + +[image img/tinycore_busybox_screen] + +The Genode process tree looks as follows (the figure omits usual Genode +components such as device drivers for PCI, PS/2, VESA, and the Timer): + +[image img/tinycore_busybox] + +The Linux Launcher node is just a slightly modified Init node with the only +difference being that requests for sessions to the Nitpicker GUI server or +to the timer are always delegated to the parent rather than to another +child. + + +Operating-system services and libraries +####################################### + +Completed support for dynamic linking +===================================== + +With the previous release, we introduced the initial version of a dynamic +linker for Genode. This version came in the form of a separate source-code +repository called 'ldso' containing the dynamic linker and the linker +scripts for building shared libraries and dynamically linked executables. +However, some pieces were still missing to make the dynamic linker +generally usable in practice. The Genode build system lacked proper support +for building and using shared libraries and the dynamic linker had been +only tested on the x86_32 platform on Pistachio and OKL4. +In the meanwhile, we filled these gaps. With the release 9.11, we completely +dissolved the dependency of the dynamic linker from the C library and, +thereby, could make the dynamic linker a regular part of the 'os' repository. +It now resides in the 'os/src/ldso' directory and supports all Genode base +platforms L4/Fiasco, L4ka::Pistachio, OKL4, and Linux on the x86_32 and +x86_64 architectures. We are especially delighted about the dynamic linker +functioning seamlessly on the Linux platform. Because 'ldso' uses only +the Genode API as back end, there are no platform-specific quirks needed. + + +:Usage: + +To build a shared library instead of a regular static library, you just need to +declare 'SHARED_LIB = yes' in the library-description file. When doing so, a +'.lib.so' file will be generated and installed in the +'/bin/' directory. For building an executable that uses a shared +library, no special precautions are needed. The build system will automatically +detect the use of shared libraries, concludes that the binary must be +dynamically linked, and will use the correct linker script. When loading a +dynamically linked program, the dynamic linker 'lsdo' and all used shared +objects must be loaded as well. + + +:Integration with the framework: + +On Genode, the 'process' library provides the API to create new processes from +ELF executables. The user of the 'process' library can register a capability to +a dataspace containing the dynamic linker via the function +'Process::dynamic_linker'. When creating a new process, the library first +revisits the ELF header of the executable to determine whether the binary is +statically or dynamically linked. If statically linked, the process library +proceeds with loading the ELF binary. Otherwise, it loads the dynamic linker as +registered beforehand. When the dynamic linker (ldso) starts up, it requests +the dataspace of the dynamically linked executable by opening a ROM session for +the magic file called 'binary'. Note that the dynamic linker does not even need +to know the real name of executable. Then ldso further loads all shared +libraries needed for the executable via ROM sessions with the names of the +respective shared object files and populates the local address space. After +having initialized the address space for the new executable, ldso jumps to the +executable's main function. + + +Packet-stream interface +======================= + +Up to now, Genode provides synchronous IPC calls and asynchronous signals as +inter-process communication primitives. The IPC framework transfers message +payload by copying data between processes via the kernel. The signalling +mechanism provides semantics similar to interrupts but does not support the +transfer of message payloads. With the new packet-stream interface, we +complement those inter-process communication facilities with a mechanism +that carries payload over a shared memory block employing an asynchronous +data-flow protocol. It is geared towards large bulk payloads such as +network traffic, block-device data, video frames, sound samples, and USB +URB packets. + +The packet-stream interface comes in the form of the single header file +'os/packet_stream.h' and supports the unidirectional streaming of bulk data +between processes via a shared-memory block. The public interface consists of +the two class templates 'Packet_stream_source', and 'Packet_stream_sink'. Both +communication parties agree on a policy with regard to the organization of the +communication buffer by specifying the same 'Packet_stream_policy' as template +argument. + +[image img/packet_stream] + +As illustrated in the Figure above, the communication buffer consists of +three parts, a submit queue, an acknowledgement queue, and a bulk buffer. +The submit queue contains packets generated by the source to be processed +by the sink. The acknowledgement queue contains packets that are processed +and acknowledged by the sink. The bulk buffer contains the actual payload. +The assignment of packets to bulk-buffer regions is performed by the +source. + +The interplay between source and sink for processing a single packet looks +as follows: + +# The source allocates a region of the bulk buffer for storing the packet + payload using 'alloc_packet'. It then requests the local start address of + the payload using 'packet_content' and fills the packet with data +# The source submits the packet to the submit queue via 'submit_packet' +# The sink requests a packet from the submit queue using 'get_packet', + determines the local start address of the payload using 'packet_content', + and processes the contained data +# After having finished the processing of the packet, the sink acknowledges + the packet using 'acknowledge_packet', placing the packet into the + acknowledgement queue +# The source reads the packet from the acknowledgement queue and releases + the packet using 'release_packet'. Thereby, the region of the bulk buffer + that was used by the packet becomes marked as free. + +This protocol has four corner cases that are handled by signals: + +:submit queue is full: when the source is trying to submit a new packet. + In this case, the source blocks and waits for the sink to remove packets + from the submit queue. If the sink observes such a condition (calling + 'get_packet' on a full submit queue, it delivers a 'ready_to_submit' + signal to wake up the source. + +:submit queue is empty: when the sink tries to obtain a packet via + 'get_packet'. The sink is going to block. If the source places a + packet into an empty submit queue, it delivers a 'packet_avail' + signal to wake up the sink. + +:acknowledgement queue is full: when the sink tries to acknowledge a packet + using 'acknowledge_packet'. The sink is going to block until the source + removes an acknowledged packet from the acknowledgement queue and delivers + a 'ready_to_ack' signal. + +:acknowledgement queue is empty: when the source tries to obtain an + acknowledged packet using 'get_acked_packet'. In this case, the source + will block until the sink places another acknowledged packet into the + empty acknowledgement queue and delivers a 'ack_avail' signal. + +These conditions can be avoided by querying the state of the submit and +acknowledge buffers using the functions 'packet_avail', +'ready_to_submit', 'ready_to_ack', and 'ack_avail'. + +If bidirectional data exchange between two processes is desired, two pairs +of 'Packet_stream_source' and 'Packet_stream_sink' should be instantiated. + + +NIC-session interface +===================== + +The NIC session interface is the first application of our new packet stream +facility. It allows executing network drivers as separate processes rather +than linked against the network protocol stack. A NIC session consists of +two packet streams, the transmission stream (TX) for sending packets and +the reception stream (RX) for receiving packets. Furthermore, each NIC +session comprises a simple RPC interface for requesting the MAC address of +the network adaptor and for defining signal handlers for the signals TX +ready-for-submit, TX acknowledgements-available, RX ready-to-ack, and RX +packet-available. By default, those signals are handled by default signal +handlers contained in blocking packet-stream functions. However, it is +possible to override the data-flow handlers to implement semantics similar +to the POSIX 'select' function, for example to wait for all possible +signals of multiple NIC sessions using only a single blocking function. +You can find the NIC-session interface as part of the 'os' repository +at 'os/include/nic_session/'. + + +Light-weight IP stack (lwIP) +============================ + +Our port of the light-weight IP stack (lwIP) builds upon the foundation +laid with the NIC-session interface. + +The following Figure illustrates the integration of a networking +application with lwIP that uses the NIC-session interface as back end. + +[image img/lwip] + +The port of the lwIP stack resides in the new 'libports' repository +described in Section [New libports repository]. It comes with +two examples, a loopback demonstration and a minimalistic HTTP server. +The examples are located at the 'libports' repository at 'src/test/lwip/'. +The lwIP back-end acts as a client of the NIC-session interface. +For the server counterpart, we added a DDE-Linux based stand-alone +network driver for PCnet32 to the 'linux_drivers' repository. + +For starting the HTTP-server test on L4ka::Pistachio, OKL4, and L4/Fiasco, +the following config file can be used: + +! +! +! timer +! 512K +! +! +! pci_drv +! 512K +! +! +! nic_drv +! 512K +! +! +! lwip_httpsrv_test +! 1M +! +! + +For trying out the example with Qemu, please refer to the instructions +given in the +[http://genode.org/documentation/release-notes/9.02#section-4 - description] +of the initial networking support added in Genode version 9.02. + + +MMX-based 2D blitting library +============================= + +Previous Genode releases already featured a 2D blitting library with a +MMX-based optimization for x86_32. This optimization, however, was not enabled +by default. Starting with the current release, several graphics-related parts +of Genode will profit from our revisited version of this library, which is now +enabled for both x86_32 and x86_64 by default. From this change, you can expect +a definite performance boost of the Nitpicker GUI server and all +Scout-widget-based applications such as the tutorial browser and launchpad. The +library interface is located at 'os/include/blit/blit.h'. On architectures with +no MMX, a generic implementation of the interface is used as fall back, which +makes it safe to use the 'blit' interface for developing portable +applications. + + +Zero-footprint runtime for Ada/Spark +==================================== + +At Genode Labs, we are exploring the use of the Spark subset of Ada to +implement security-critical code and use Genode as development platform. +For this reason, we have added support for executing freestanding Ada +code on Genode. An example of the use of Ada on Genode can be found at +'base/src/test/ada'. + +The program relies on the normal startup procedure of a Genode process. +Execution starts at the 'crt0' assembly entry provided by the startup library. +The 'crt0' code sets up the stack of the main thread and calls the '_main' +function implemented in the C++ portion of Genode's startup library. In turn, +the '_main' function calls 'main' of the actual program. The main function of +this example calls the Ada main procedure. The test further exercises the call +of C functions from Ada code. So the integration of Ada and C code is almost +seamless. + +For building the Ada test program, you must have installed the GNU GNAT Ada +compiler. Right now, we are using the host version of this compiler, which +is save as long as we do not use advanced Ada features such as exceptions. +To enable building the test program, add 'gnat' to the 'SPECS' declaration +of your '/etc/specs.conf'. Otherwise, the Genode build system +will skip the target. + +Please note that the current version of this program does not use 'gnatbind'. +Therefore, package elaboration is not executed. + + +Misc improvements of OS-level services and libraries +==================================================== + +:Init: + + Fixed quota-limitation problem in init. There was a race between the + call of 'env()->ram_session()->avail_quota()' and already running children + that donated quota via init to a server. During the quota transfer, child + quota gets temporarily transferred to init to be further transferred to + the server. In the worst case, such temporary quota was then assigned to + the last child when limiting its quota to 'avail_quota()'. We solved this + problem by deferring the start of child programs until all quota calculations + are finished. + +:Nitpicker GUI server: + + Prevent superfluous screen updates when switching clicking on different + views of the same session, making the GUI more responsive. + + +New libports repository +####################### + +With proper shared-library support in place and with our C runtime getting +more and more mature, we feel an increased desire to port existing popular +libraries to Genode. For this purpose, we have now introduced a dedicated +source-code repository called 'libports'. Following the approach taken +with our Qt4 porting effort, this repository does not contain actual source +code but a mechanism to download upstream library source codes and adapting +them to Genode. This way, we can easily keep track of the adaptions needed +for Genode and update libraries to later versions. + +:Usage: + +At the root of the 'libports' repository, there is a 'Makefile' automating +the task of downloading and preparing the library source codes. By just +typing 'make', you get an overview of the available libraries and further +instructions. + +In the common case, you might just want to prepare all libraries by issuing: +! make prepare + +Alternatively, you can select one particular library to prepare by +specifying the base name of a library (wihout the version number) as +command-line argument: +! make prepare LIB=freetype + +After having prepared the 'libports' repository, we are ready to include +the repository into the build process by appending it to the 'REPOSITORIES' +declaration of your '/etc/build.conf' file. + +:Under the hood: + +For each library, there is a file contained in the 'libports/ports/' +subdirectory. The file is named after the library and contains the +library-specific rules for downloading the source code and installing +header files. + +For reference, we have included ports of *Freetype2* and *Jpeg*. Note that +currently, these ports serve mainly the purpose of illustrating the use of the +'libports' repository and are not thoroughly tested. However, we have +successfully used them with Qt4. + +:How does 'libports' relate to the other repositories?: + +The 'libports' repository is meant as a place for porting popular libraries +that usually expect a POSIX-like environment - similar to the environment +provided by Genode's 'libc' repository. So 'libports' depends on 'libc' and, +consequently, on the repositories 'libc' depends on, most specifically the 'os' +repository. Because the dynamic linker is now a regular part of the 'os' +repository, libraries contained in 'libports' can (and should) be built as +shared libraries. + + +Device drivers +############## + +Device-driver environment +========================= + +We steadily improve our device-driver environment for executing Linux drivers +directly on Genode. For this release, we updated the Linux environment to the +Linux kernel version 2.6.20.21, and improved several parts of the +Linux-specific code, in particular the handling timers and tasklets. + +In the DDE Kit, we made the 'free()' function compatible with C99 (accepting a +NULL pointer as argument) and fixed a memory leak. + + +USB storage +=========== + +We extended our USB stack with the driver infrastructure needed for +accessing USB storage devices. The USB stack is ported from the Linux +kernel using the Linux device-driver environment. Our Genode-specific +support code consists of two parts: + +* We added emulation code for the Linux SCSI protocol layer as relied + on by the Linux USB stack. The currently supported SCSI commands are INQUIRY, + READ_10, WRITE_10, and READ_CAPACITY. Furthermore, we added a custom block + interface at 'linux_drivers/include/dde_linux26/block.h', which still has a + number of limitations (thread safe, synchronous, single block r/w requests + only). +* For the file-system layer, we ported the + [http://elm-chan.org/fsw/ff/00index_e.html - FatFs R0.07e library] + to Genode. This library allows us to access the directories and files of the + FAT file system on the USB device. It has been ported using our new + 'libports' repository. + +The new USB storage support can be tested using a test program supplied with +the 'linux_drivers' repository. It runs on all base platforms except on Linux. +The source code of the test is located at +'src/src/test/dde_linux26_usbstorage'. For compiling, you need to download the +'libffat' first. From the 'libports' repository, you can issue: +! make prepare LIB=ffat + +Furthermore, you must ensure that both the 'libports' and 'linux_drivers' +repositories are specified in the 'REPOSITORIES' declaration in your +'/etc/build.conf' file. Because of the dependency of the USB-storage +test from libffat, the program is not built by default until explicitely +enabled by stating that 'libffat' is available. This must be done by extending +the 'SPECS' variable in your '/etc/specs.conf': +! SPECS += libffat + +After these preparations, you can build the test program from your +build directory: +! make test/dde_linux26_usbstorage + +For executing the test, you need to specify Genode's 'timer' and 'pci_drv' +alongside the 'test-dde_linux26_usbstorage' program. The test program +will access an attached USB storage device, output the root directory +content and load the first 16 bytes of the first file. You can try this +out on Qemu by using a virtual USB storage device. First create a +disk image with a FAT file system: +! dd if=/dev/zero of= count=2048 +! mkfs.vfat +! mount -oloop +! cp +! umount + +Then you can attach this disk image to Qemu using the arguments +'-usb -usbdevice disk:'. + + +PS/2 mouse and keyboard driver +============================== + +We improved Genode's native PS/2 driver to be more robust against delays at +startup. During the time after the startup of the PS/2 driver until a +client connects, incoming input events used to fill up and eventually overflow +the event queue. Now, we start sampling input events only after a client +connects to the PS/2 driver. + +Furthermore, we have added support for the Intellimouse ImPS/2 and ExPS/2 +protocol extensions to support mice with a vertical scroll wheel and +5-button mouses. The improvement required no changes of the 'Input::Event' +interface. Scroll-wheel events are reported as 'WHEEL' events with the wheel +count delivered as 'ry' value. The buttons correspond to the key codes +'BTN_LEFT', 'BTN_RIGHT', 'BTN_MIDDLE', 'BTN_SIDE', 'BTN_EXTRA'. + +Regarding the keyboard driver, we do not print messages on the occurrence of +key-repeat events any longer. These messages tended to significantly slow down +keyboard-based applications such as the OKLinux console. + + +NIC driver implementing the NIC-session interface +================================================= + +We added a new NIC driver using the Linux Device Driver Environment, which +implements the server side of the new NIC-session interface described in +Section [NIC-session Interface]. The currently used Linux driver is 'pcnet' +that is implemented in Qemu. Nevertheless, it should be straight forward to +add other Linux network drivers the same way. + + +Qt4 and Webkit +############## + +We have extended our Qt4 port with Webkit support, which is one of the most +complex components of Qt4. One particularly interesting point was the dependency +of the JavaScript engine from the C++ standard template library. The Genode +tool chain, however, already features the STL headers, which worked out nicely +once we figured out a way to wrest the information about the STL header +location from the compiler. + +Because Qt4 applications have exceedingly large binary sizes relying on static +linking, we put Genode's newly available shared-library support to good use by +declaring all Qt4 libraries as shared objects. This way, Qt4 applications have +now become reasonably small. For example, the binary of the Tetrix example went +from over 10MB down to about 600KB. + +Since the Genode release 9.11, Qt4 depends on the 'libports' repository, +specifically on the 'freetype2' and 'jpeg' libraries. Please make sure +that you called the top-level Makefile of the 'libports' repository +for those preparing those libraries and that your 'REPOSITORIES' declaration +contains the 'libports' repository. + + +Applications +############ + +Seamless Xvfb integration into Genode on Linux +============================================== + +Xvfb is a virtual X server that uses a plain file as frame buffer instead of a +physical screen. The 'xvfb' glue program makes an Xvfb session available to the +Linux version of Genode such that both native Genode programs and X clients can +run seamlessly integrated in one Nitpicker session. Using the 'xvfb' glue +program contained in the 'os/src/app/xvfb' directory. Because Xvfb is executed +as Nitpicker client, it is possible to integrate multiple instances of Xvfb +into the same Nitpicker session. + +[image img/xvfb_screen] + +The scenario above uses two instances of Xvfb, which are displayed by the +Nitpicker GUI server executed on Genode. Each Xvfb process is connected +to Genode via a xvfb adaptor program, which is hybrid using both the Linux +API (for accessing the virtual frame buffer and performing its role as +X client) and the Genode API (for its role as Nitpicker client). + +[image img/xvfb] + + +:Preconditions for compiling: + +The xvfb adaptor tracks dirty screen regions using the X damage extension +and injects user-input events into the X server using the X test extension. +So you need the development packages of both extensions to compile it. The +Debian package for the X damage extension is called 'libxdamage-dev'. The +X test extension is normally installed by default or resides in a package +called 'libxtst-dev'. Furthermore you need to enhance your 'SPECS' declaration +in your '/etc/specs.conf' file as follows: + +! SPECS += x11 xdamage xtest + + +:Usage: + +First start Xvfb using the following command-line arguments: + +! Xvfb :1 -fbdir /tmp -screen 0 1024x768x16 + +While Xvfb is running, '/tmp/Xvfb_screen0' will contain the content of the X +server's frame buffer. This file must be specified for the 'xvfb' declaration +in the config file. In addition, the display of X server instance must be +declared via the 'display' tag. For example: + +! +! :1 +! /tmp/Xvfb_screen0 +! + + +:Known Limitations: + +* With the current version, some key codes are not mapped correctly. +* The screen mode of Nitpicker and the Xvfb session must be the same. + Only modes with 16bit color depth are supported. + + +Backdrop application +==================== + +For the Genode Live CD, we added a simple backdrop application to the 'demo' +repository, residing in 'src/app/backdrop'. It uses libpng to display a PNG +image as background of the Nitpicker GUI server. + + +:Usage: + +You have to specify the name of the PNG file to be used as background +image via a declaration in your config file: + +! +! background.png +! + + +:Limitations: + +The PNG file is expected to be equal to the screen size. No scaling +or tiling is supported. + + +Extended configurability of native applications +=============================================== + +:Launchpad: + +By default, launchpad displays a preconfigured list of programs and their +respective default memory quotas. The user can tweak the memory quota +for each entry with mouse and then start a program by clicking on its +name. As an alternative to using the default list, you can define the list +manually by supplying a configuration to Launchpad. The following example +configuration tells launchpad to display a list of two launcher entries: + +! +! +! sdl_pathfind +! 10M +! +! +! liquid_fb +! 10M +! +! +! init +! 10M +! +! +! hello +! 1M +! +! +! +! + +To use this configuration for a Launchpad started via init, you can +simply insert the launchpad configuration into the '' node +of the launchpad entry in init's 'config' file. + + +:Liquid frame buffer: + +Liquid frame buffer is an implementation of the frame buffer interface +running as a client of the Nitpicker GUI server. It supports the +following configuration options. The example shows the default +values. + +! +! +! +! on +! +! +! 400 +! 270 +! 500 +! 400 +! +! +! Liquid Framebuffer +! +! + +Because Liquid frame buffer creates the virtual frame-buffer window at +start time, not at session-creation time, sufficient memory resources must +be provided when starting the program. Consequently, the client does not +need to donate memory for the frame buffer backing store. + +Liquid frame buffer supports only one client. If multiple virtual frame +buffers are needed, multiple instances of the program should be used. + + +Misc improvements of native applications +======================================== + +* Fixed keyboard handling in Liquid FB, now all keyboard events are directed + to the window content, which makes Liquid FB more appropriate for hosting + an OKLinux console. + +* Replaced slow pixel copy code of the scout widget set with the MMX-based 2D + blitting library and thereby improved the graphics performance of + applications such as launchpad, liquid FB, and scout. + +* Defer creation of Nitpicker view to the first buffer refresh. This avoids + artifacts when moving the mouse over designated view area during at the + startup of a scout application. + + +Platform-specific changes +######################### + +:L4ka::Pistachio: + +We further extended our work regarding *write-combined access to I/O* memory +to the L4ka::Pistachio base platform. So this platform can now also enjoy the +performance boost that we experienced on the L4/Fiasco platform when enabling +write-combined I/O for the frame buffer. + + +:Linux: + +To enable the dynamic linker to work on Linux the same way as on the other +platforms, we enhanced the Linux-specific *local region manager* to handle an +optional local address and offset when attaching a dataspace. + +Thread destruction on Linux works asynchronous by a sending a signal +via the 'tgkill' system call to the thread to be killed. Unfortunately, the +Linux kernel delivers signals only in the kernel-entry path. This means that +after calling 'tgkill', the to-be-killed thread still moves on until it enters +the kernel (either by issuing a system call or when being preempted). This has +the side effect that the thread continues to access its stack for a while. If +killing a thread in the local address space and immediately freeing the stack +of the killed thread by using the 'munmap' system call, the process would +ultimately receive a segmentation fault. To solve this problem, we need to +ensure that the to-be-killed thread is really not executing any instructions +anymore before freeing the stack. We do this by repetitively issuing 'tgkill' +for the thread until an EINVAL error is returned. + + +:OKL4: + +We changed the serial output of core to use the OKL4 kernel debugger for +printing the output of core instead of poking the comports directly. This way, +the console is not anymore x86-specific but platform-independent. + + +Build system +############ + +* For debugging Genode applications using the GDB stub of Qemu, + applications should use distinct virtual memory ranges. Otherwise, + breakpoints set in one program would trigger when another program + accesses the breakpointed virtual address. Therefore, we have + introduced the 'LD_TEXT_ADDR' variable to the build system. + A value assigned to this variable in a 'target.mk' file overrides + the default link address. + +* The integration of dynamic linking support into the build system + led to some architectural changes. Most importantly, the final linking stage + is now performed by a separate 'make' instance executing 'base/mk/link.mk'. + However, this change has no implications on the use of the build system. + +* Generate symbols for marking the end of binary data linked via the + 'SRC_BIN' mechanism. The start and end of binary data are marked by the + symbols '_binary__start' and '_binary__end'. + +* Use 'AS_OPT' also for linking binary data, which is important to make + the resulting object file always compatible with the compiled objects. + This is important on architectures with non-unified calling conventions. diff --git a/doc/release_notes-10-02.txt b/doc/release_notes-10-02.txt new file mode 100644 index 000000000..5d78eb5cb --- /dev/null +++ b/doc/release_notes-10-02.txt @@ -0,0 +1,1224 @@ + + + =============================================== + Release notes for the Genode OS Framework 10.02 + =============================================== + + Genode Labs + + + +After the release of the feature-packed version 9.11, we turned our attention +to improving the platform support of the framework. The current release 10.02 +bears fruit of these efforts on several levels. + +First, we are proud to announce the support for two new base platforms, namely +the NOVA hypervisor and the Codezero microkernel. These new kernels complement +the already supported base platforms Linux, L4/Fiasco, L4ka::Pistachio, and +OKL4. So why do we address so many different kernels instead of focusing our +efforts to one selected platform? Our observation is that different applications +pose different requirements on the kernel. Most kernels have a specific profile +with regard to security, hardware support, complexity, scheduling, resource +management, and licensing that may make them fit well for one application area +but not perfectly suited for a different use case. There is no single perfect +kernel and there doesn't need to be one. By using Genode, applications +developed for one kernel can be ported to all the other supported platforms with +a simple recompile. We believe that making Genode available on a new kernel is +beneficial for the kernel developers, application developers, and users alike. +For kernel developers, Genode brings additional workloads to stress-test their +kernel, and it extends the application area of the kernel. Application +developers can address several kernel platforms at once instead of tying their +programs to one particular platform. Finally, users and system integrators can +pick their kernel of choice for the problem at hand. Broadening the platform +support for Genode helps to make the framework more relevant. + +Second, we introduced a new way for managing real-time priorities, which fits +perfectly with the recursive system structure of Genode. This clears the way to +multi-media and other real-time workloads that we target with our upcoming +work. We implemented the concept for the L4ka::Pistachio and OKL4 platforms. +With real-time priorities on OKL4, it is possible to run multiple instances of +the OKLinux kernel at the same time, each instance at a different priority. + +Third, we vastly improved the existing framework, extended the ARM architecture +support to cover dynamic loading and the C runtime, introduced a new +thread-context management, added a plugin-concept to our C runtime, and +improved several device drivers. + +Even though platform support is the main focus of this release, we introduced a +number of new features, in particular the initial port of the Python 2.6 script +interpreter. + + +NOVA hypervisor as new base platform +#################################### + +When we started the development of Genode in 2006 at the OS Group of the +Technische Universität Dresden, it was originally designated to be the user +land of a next-generation and to-be-developed new kernel called NOVA. Because +the kernel was not ready at that time, we had to rely on intermediate solutions +as kernel platform such as L4/Fiasco and Linux during development. These +circumstances led us to the extremely portable design that Genode has today and +motivated us to make Genode available on the whole family of L4 microkernels. +In December 2009, the day we waited for a long time had come. The first version +of NOVA was publicly released: + +:Official website of the NOVA hypervisor: + [http://hypervisor.org] + +Besides the novel and modern kernel interface, NOVA has a list of features that +sets it apart from most other microkernels, in particular support for +virtualization hardware, multi-processor support, and capability-based +security. + + +Why bringing Genode to NOVA? +============================ + +NOVA is an acronym for NOVA OS Virtualization Architecture. It stands for a +radically new approach of combining full x86 virtualization with microkernel +design principles. Because NOVA is a microkernelized hypervisor, the term +microhypervisor was coined. In its current form, it successfully addresses +three main challenges. First, how to consolidate a microkernel system-call API +with a hypercall API in such a way that the API remains orthogonal? The answer +to this question lies in NOVA's unique IPC interface. Second, how to implement +a virtual machine monitor outside the hypervisor without spoiling +performance? The Vancouver virtual machine monitor that runs on top NOVA proves +that a decomposition at this system level is not only feasible but can yield +high performance. Third, being a modern microkernel, NOVA set out to pursue a +capability-based security model, which is a challenge on its own. + +Up to now, the NOVA developers were most concerned about optimizing and +evaluating NOVA for the execution of virtual machines, not so much about +running a fine-grained decomposed multi-server operating system. This is where +Genode comes into play. With our port of Genode to NOVA, we contribute the +workload to evaluate NOVA's kernel API against this use case. We are happy to +report that the results so far are overly positive. + +At this point, we want to thank the main developers of NOVA Udo Steinberg and +Bernhard Kauer for making their exceptional work and documentation publicly +available, and for being so responsive to our questions. We also greatly +enjoyed the technical discussions we had and look forward to the future +evolution of NOVA. + + +Challenges +========== + +From all currently supported base platforms of Genode, the port to NOVA was the +most venturesome effort. It is the first platform with kernel support for +capabilities and local names. That means no process except the kernel has +global knowledge. This raises a number of questions that seem extremely hard +to solve at the first sight. For example: There are no global IDs for threads +and other kernel objects. So how to address the destination for an IPC message? +Or another example: A thread does not know its own identity per se and there is +no system call similar to 'getpid' or 'l4_myself', not even a way to get a +pointer to a thread's own user-level thread-control block (UTCB). The UTCB, +however, is needed to invoke system calls. So how can a thread obtain its UTCB +in order to use system calls? The answers to these questions must be provided by +user-level concepts. Fortunately, Genode was designed for a capability kernel +right from the beginning so that we already had solutions to most of these +questions. In the following, we give a brief summary of the specifics of Genode +on NOVA: + +* We maintain our own system-call bindings for NOVA ('base-nova/include/nova/') + derived from the NOVA specification. We put the bindings under MIT license + to encourage their use outside of Genode. + +* Core runs directly as roottask on the NOVA hypervisor. On startup, core + maps the complete I/O port range to itself and implements debug output via + comport 0. + +* Because NOVA does not allow rootask to have a BSS segment, we need a slightly + modified linker script for core (see 'src/platform/roottask.ld'). + All other Genode programs use Genode's generic linker script. + +* The Genode 'Capability' type consists of a portal selector expressing the + destination of a capability invocation and a global object ID expressing + the identity of the object when the capability is specified as an invocation + argument. In the latter case, the global ID is needed because of a limitation + of the current system-call interface. In the future, we are going to entirely + remove the global ID. + +* Thread-local data such as the UTCB pointer is provided by the new thread + context management introduced with the Genode release 10.02. It enables + each thread to determine its thread-local data using the current stack + pointer. + +* NOVA provides threads without time called local execution contexts (EC). + Local ECs are intended as server-side RPC handlers. The processing time + needed to perform RPC requests is provided by the client during the RPC call. + This way, RPC semantics becomes very similar to function call semantics with + regard to the accounting of CPU time. Genode already distinguishes normal + threads (with CPU time) and server-side RPC handlers ('Server_activation') + and, therefore, can fully utilize this elegant mechanism without changing the + Genode API. + +* On NOVA, there are no IPC send or IPC receive operations. Hence, this part + of Genode's IPC framework cannot be implemented on NOVA. However, the + corresponding classes 'Ipc_istream' and 'Ipc_ostream' are never used directly + but only as building blocks for the actually used 'Ipc_client' and + 'Ipc_server' classes. Compared with the other Genode base platforms, Genode's + API for synchronous IPC communication maps more directly onto the NOVA + system-call interface. + +* The Lock implementation utilizes NOVA's semaphore as a utility to let a + thread block in the attempt to get a contended lock. In contrast to the + intuitive way of using one kernel semaphore for each user lock, we use only + one kernel semaphore per thread and the peer-to-peer wake-up mechanism we + introduced in the release 9.08. This has two advantages: First, a lock does + not consume a kernel resource, and second, the full semantics of the Genode + lock including the 'cancel-blocking' semantics are preserved. + +* NOVA does not support server-side out-of-order processing of RPC requests. + This is particularly problematic in three cases: Page-fault handling, signal + delivery, and the timer service. + + A page-fault handler can receive a page fault request only if the previous + page fault has been answered. However, if there is no answer for a + page-fault, the page-fault handler has to decide whether to reply with a + dummy answer (in this case, the faulter will immediately raise the same page + fault again) or block until the page-fault can be resolved. But in the latter + case, the page-fault handler cannot handle any other page faults. This is + unfeasible if there is only one page-fault handler in the system. Therefore, + we instantiate one pager per user thread. This way, we can block and unblock + individual threads when faulting. + + Another classical use case for out-of-order RPC processing is signal + delivery. Each process has a signal-receiver thread that blocks at core's + signal service using an RPC call. This way, core can selectively deliver + signals by replying to one of these in-flight RPCs with a zero-timeout + response (preserving the fire-and-forget signal semantics). On NOVA however, + a server cannot have multiple RPCs in flight. Hence, we use a NOVA semaphore + shared between core and the signal-receiver thread to wakeup the + signal-receiver on the occurrence of a signal. Because a semaphore-up + operation does not carry payload, the signal has to perform a non-blocking + RPC call to core to pick up the details about the signal. Thanks to Genode's + RPC framework, the use of the NOVA semaphore is hidden in NOVA-specific stub + code for the signal interface and remains completely transparent at API + level. + + For the timer service, we currently use one thread per client to avoid the need + for out-of-order RPC processing. + +* Because NOVA provides no time source, we use the x86 PIT as user-level time + source, similar as on OKL4. + +* On the current version of NOVA, kernel capabilities are delegated using IPC. + Genode supports this scheme by being able to marshal 'Capability' objects as + RPC message payload. In contrast to all other Genode base platforms where + the 'Capability' object is just plain data, the NOVA version must marshal + 'Capability' objects such that the kernel translates the sender-local name to + the receiver-local name. This special treatment is achieved by overloading + the marshalling and unmarshalling operators of Genode's RPC framework. The + transfer of capabilities is completely transparent at API level and no + modification of existing RPC stub code was needed. + + +How to explore Genode on NOVA? +============================== + +The Genode release 10.02 supports the NOVA pre-release version 0.1. You can +download the archive here: + +:Download NOVA version 0.1: + [http://os.inf.tu-dresden.de/~us15/nova/nova-hypervisor-0.1.tar.bz2] + +For building NOVA, please refer to the 'README' file contained in the archive. +Normally, a simple 'make' in the 'build/' subdirectory is all you need to +get a freshly baked 'hypervisor' binary. + +The NOVA platform support for Genode resides in the 'base-nova/' repository. +To create a build directory prepared for compiling Genode for NOVA, you can use +the 'create_builddir' tool. From the top-level Genode directory, issue the +following command: + +! ./tool/builddir/create_builddir nova_x86 GENODE_DIR=. BUILD_DIR=

+ +This tool will create a fresh build directory at the location specified +as 'BUILD_DIR'. Provided that you have installed the +[http://genode.org/download/tool-chain - Genode tool chain], you can now build +Genode by using 'make' from within the new build directory. + +Note that in contrast to most other kernels, the Genode build process does not +need to know about the source code of the kernel. This is because Genode +maintains its own system-call bindings for this kernel. The bindings reside in +'base-nova/include/nova/'. + +NOVA supports multi-boot boot loaders such as GRUB, Pulsar, or gPXE. For +example, a GRUB configuration entry for booting the Genode demo scenario +with NOVA looks as follows, whereas 'genode/' is a symbolic link to the +'bin/' subdirectory of the Genode build directory and the 'config' file +is a copy of 'os/config/demo'. + +! title Genode demo scenario +! kernel /hypervisor noapic +! module /genode/core +! module /genode/init +! module /config/demo/config +! module /genode/timer +! module /genode/ps2_drv +! module /genode/pci_drv +! module /genode/vesa_drv +! module /genode/launchpad +! module /genode/nitpicker +! module /genode/liquid_fb +! module /genode/nitlog +! module /genode/testnit +! module /genode/scout + +Please note the 'noapic' argument for the NOVA hypervisor. This argument +enables the use of ordinary PIC IRQ numbers, as relied on by our current +PIT-based timer driver. + + +Limitations +=========== + +The current NOVA version of Genode is able to run the complete Genode demo +scenario including several device drivers (PIT, PS/2, VESA, PCI) and the GUI. +At version 0.1, however, NOVA is not yet complete and misses some features +needed to make Genode fully functional. The current limitations are: + +* No real-time priority support: NOVA supports priority-based scheduling + but, in the current version, it allows each thread to create scheduling + contexts with arbitrary scheduling parameters. This makes it impossible + to enforce priority assignment from a central point as facilitated with + Genode's priority concept. + +* No multi-processor support: NOVA supports multi-processor CPUs through + binding each execution context (ECs) to a particular CPU. Because everyone + can create ECs, every process could use multiple CPUs. However, Genode's API + devises a more restrictive way of allocating and assigning resources. In + short, physical resource usage should be arbitrated by core and the creation + of physical ECs should be performed by core only. However, Remote EC creation + is not yet supported by NOVA. Even though, multiple CPU can be used with + Genode on NOVA right now by using NOVA system calls directly, there is no + support at the Genode API level. + +* Missing revoke syscall: NOVA is not be able to revoke memory mappings or + destroy kernel objects such as ECs and protection domains. In practice, this + means that programs and complete Genode subsystems can be started but not + killed. Because virtual addresses cannot be reused, code that relies on + 'unmap' will produce errors. This is the case for the dynamic loader or + programs that destroy threads at runtime. + +Please note that these issues are known and worked on by the NOVA developers. +So we expect Genode to become more complete on NOVA soon. + + +Codezero kernel as new base platform +#################################### + +Codezero is a microkernel primarily targeted to ARM-based embedded systems. +It is developed as an open-source project by a British company called B-Labs. + +:B-Labs website: + [http://b-labs.co.uk] + +The Codezero kernel was first made publicly available in summer 2009. The +latest version, documentation, and community resources are available at the +project website: + +:Codezero project website: + [http://l4dev.org] + +As highlighted by the name of the project website, the design of the kernel is +closely related to the family of L4 microkernels. In short, the kernel provides +a minimalistic set of functionality for managing address spaces, threads, and +communication between threads, but leaves complicated policy and device access +to user-level components. + +To put Codezero in relation to other L4 kernels, here is a quick summary on the +most important design aspects as implemented with the version 0.2, and how +our port of Genode relates to them: + +* In the line of the original L4 interface, the kernel uses global name spaces + for kernel objects such as threads and address spaces. + +* For the interaction between a user thread and the kernel, the concept of + user-level thread-control blocks (UTCB) is used. A UTCB is a small + thread-specific region in the thread's virtual address space, which is + always mapped. The access to the UTCB can never raise a page fault, + which makes it perfect for the kernel to access system-call arguments, + in particular IPC payload copied from/to user threads. In contrast to other + L4 kernels, the location of UTCBs within the virtual address space is managed + by the user land. + + On Genode, core keeps track of the UTCB locations for all user threads. + This way, the physical backing store for the UTCB can be properly accounted + to the corresponding protection domain. + +* The kernel provides three kinds of synchronous inter-process communication + (IPC): Short IPC carries payload in CPU registers only. Full IPC copies + message payload via the UTCBs of the communicating parties. Extended IPC + transfers a variable-sized message from/to arbitrary locations of the + sender/receiver address spaces. During an extended IPC, page faults may + occur. + + Genode solely relies on extended IPC, leaving the other IPC mechanisms to + future optimizations. + +* The scheduling of threads is based on hard priorities. Threads with the + same priority are executed in a round-robin fashion. The kernel supports + time-slice-based preemption. + + Genode does not support Codezero priorities yet. + +* The original L4 interface leaves the question on how to manage and account + kernel resources such as the memory used for page tables unanswered. + Codezero makes the accounting of such resources explicit, enables the + user-land to manage them in a responsible way, and prevent kernel-resource + denial-of-service problems. + +* In contrast to the original L4.v2 and L4.x0 interfaces, the kernel provides + no time source in the form of IPC timeouts to the user land. A time source + must be provided by a user-space timer driver. Genode employs such a timer + services on all platforms so that it is not constricted by this limitation. + +In several ways, Codezero goes beyond the known L4 interfaces. The most +noticeable addition is the support of so-called containers. A container is +similar to a virtual machine. It is an execution environment that holds a set +of physical resources such as RAM and devices. The number of containers and the +physical resources assigned to them are static and have to be defined at build +time. The code executed inside a container can roughly be classified by two +categories. First, there are static programs that require strong isolation from the +rest of the system but no classical operating-system infrastructure, for +example special-purpose telecommunication stacks or cryptographic functionality +of an embedded device. Second, there are kernel-like workloads, which use the L4 +interface to substructure the container into address spaces, for example a +paravirtualized Linux kernel that uses Codezero address spaces to protect Linux +processes. Genode runs inside a container and facilitates Codezero's L4 +interface to implement its multi-server architecture. + +The second major addition is the use of a quite interesting flavor of a +capability concept to manage the authorization of processes to access system +resources and system calls. In contrast to most current approaches, Codezero +does not attempt to localize the naming of physical objects such as +address-space IDs and thread ID. So a capability is not referred to via a local +name but a global name. However, for delegating authorization throughout the +system, the capability approach is employed. A process that possesses a capability +to an object can deal with the object. It can further delegate this access +right to another party (to which it holds a capability). In a way, this +approach keeps the kernel interface true to the original L4 interface but +provides a much stronger concept for access control. However, it is important +to point out that the problem of ambient authority is not (yet) addressed by +this concept. If a capability is not used directly but specified as an argument +to a remote service, this argument is passed as a plain value not +protected by the kernel. Because the identity of the referenced object can be +faked by the client, the server has to check the plausibility of the argument. +For the server, however, this check is difficult/impossible because it has no +way to know whether the client actually possesses the capability it is talking +about. + +The current port of Genode to Codezero does not make use of the capability +concept for fine-grained communication control, yet. As with the other L4 +kernels, each object is identified by a unique ID allocated by a core service. +There is no mechanism in place to prevent faked object IDs. + + +:Thanks: +We want to thank the main developer of Codezero Bahadir Balban for his great +responsiveness to our feature requests and questions. Without his help, the +port would have taken much more effort. We hope that our framework will be of +value to the Codezero community. + + +Using Genode with Codezero +========================== + +The port of Genode is known to work with the devel branch of Codezero version +0.2 as of 2010-02-19. + +To download the Codezero source code from the official source-code repository, +you can use the following commands: + +!git clone git://git.l4dev.org/codezero.git +!git checkout -b devel --track origin/devel + +In addition to downloading the source code, you will need to apply the small +patch 'base-codezero/lcd.patch' to the Codezero kernel to enable the device +support for the LCD display. Go to the 'codezero.git/' directory and issue: + +!patch -p1 < /base-codezero/lcd.patch + +For a quick start with Codezero, please follow the "Getting Started with the +Codezero Development" guide, in particular the installation of the tool chain: + +:Getting started with Codezero: + [http://www.l4dev.org/getting_started] + +The following steps guide you through building and starting Genode on Codezero +using the Versatilepb platform as emulated by Qemu. + +# Create a Genode build directory for the Codezero/Versatilepb platform. + Go to the Genode directory and use the following command where '' + is the designated location of the new Genode build directory and + '' is the 'codezero.git/' directory with the Codezero + source tree, both specified as absolute directories. + ! ./tool/builddir/create_builddir codezero_versatilepb \ + ! GENODE_DIR=. \ + ! BUILD_DIR= \ + ! L4_DIR= + + With the build directory created, Genode targets can immediately be + compiled for Codezero. For a quick test, go to the new build directory and + issue: + ! make init + + In addition to being a Genode build directory, the directory is already + prepared to be used as Codezero container. In particular, it holds a + 'SConstruct' file that will be called by the Codezero build system. In this + file, you will find the list of Genode targets to be automatically built when + executing the Codezero build process. Depending on your work flow, you may + need to adapt this file. + +# To import the Genode container into the Codezero configuration system, + go to the 'codezero.git/' directory and use the following command: + + ! ./scripts/baremetal/baremetal_add_container.py \ + ! -a -i Genode -s + +# Now, we can add and configure a new instance of this container via the + Codezero configuration system: + ! ./configure.py + + Using the interactive configuration tool, select to use a single container + and set up the following values for this bare-metal container, choose a + sensible 'Container Name' (e.g., 'genode0') and select the 'Genode' entry in + the 'Baremetal Project' menu. + + :Default pager parameters: + ! 0x40000 Pager LMA + ! 0x100000 Pager VMA + These values are important because they are currently hard-wired in the + linker script used by Genode. If you need to adopt these values, make + sure to also update the Genode linker script located at + 'base-codezero/src/platform/genode.ld'. + + :Physical Memory Regions: + ! 1 Number of Physical Regions + ! 0x40000 Physical Region 0 Start Address + ! 0x4000000 Physical Region 0 End Address + We only use 64MB of memory. The physical memory between 0 and 0x40000 is + used by the kernel. + + :Virtual Memory Regions: + ! 1 Number of Virtual Regions + ! 0x0 Virtual Region 0 Start Address + ! 0x50000000 Virtual Region 0 End Address + It is important to choose the end address such that the virtual memory + covers the thread context area. The context area is defined at + 'base/include/base/thread.h'. + + :Container Devices (Capabilities): + Enable the LCD display in the 'CLCD Menu'. + + The configuration system will copy the Genode container template to + 'codezero.git/conts/genode0'. Hence, if you need to adjust the container's + 'SConscript' file, you need to edit 'codezero.git/conts/genode.0/SConscript'. + The original Genode build directory is only used as template when creating + a new Codezero container but it will never be looked at by the Codezero build + system. + +# After completing the configuration, it is time to build both Codezero and + Genode. Thanks to the 'SConscript' file in the Genode container, the Genode + build process is executed automatically: + ! ./build.py + + You will find the end result of the build process at + ! ./build/final.elf + +# Now you can try out Genode on Qemu: + ! qemu-system-arm -s -kernel build/final.elf \ + ! -serial stdio -m 128 -M versatilepb & + + The default configuration starts the nitpicker GUI server and the launchpad + application. The versatilepb platform driver is quite limited. It does + support the LCD display as emulated by Qemu but no user input, yet. + + +Limitations +=========== + +At the current stage, the Genode version for Codezero is primarily geared +towards the developers of Codezero as a workload to stress their kernel. It +still has a number of limitations that would affect the real-world use: + +* Because the only platform supported out of the box by the official Codezero + source tree is the ARM-based Versatilebp board, Genode is currently tied to + this hardware platform. When Codezero moves beyond this particular platform, + we will add a modular concept for platform support packages to Genode. + +* The current timer driver at 'os/src/drivers/timer/codezero/' is a dummy + driver that just yields the CPU time instead of blocking. It is not + suitable as time source. + +* The versatilepb platform driver at 'os/src/drivers/platform/versatilepb/' + does only support the LCD display as provided by Qemu but it was not tested on + real hardware. Because Codezero does not yet allow the assignment of the + Versatilepb PS/2 controller to a container, the current user-input driver is + just a dummy. + +* The lock implementation is based on a simple spinlock using an atomic + compare-exchange operation, which is implemented via Codezero's kernel mutex. + The lock works and is safe but it has a number of drawbacks with regard to + fairness, efficiency, and its interaction with scheduling. + +* Core's IRQ service is not yet implemented because the IRQ-handling interface + of Codezero is still in flux. + +* Because we compile Genode with the same tool chain (Codesourcery ARM tool + chain) as used for Codezero, there are still subtle differences in the + linker scripts, making Genode's dynamic linker not yet functional on + Codezero. + +* Even though Codezero provides priority-based scheduling, Genode does not + allow assigning priorities to Codezero processes, yet. + +* Currently, all Genode boot modules are linked as binary data against core, + which is then loaded as single image into a container. For this reason, core + must be build after all binaries. This solution is far from being convenient + because changing the list of boot modules requires changes in core's + 'platform.cc' and 'target.mk' file. + + +New thread-context management +############################# + +With the current release, we introduced a new stack management concept that is +now consistently used on all Genode base platforms. Because the new concept +does not only cover the stack allocation but also other thread-specific context +information, we speak of thread-context management. The stack of a Genode +thread used to be a member of the 'Thread' object with its size specified as +template argument. This stack-allocation scheme was chosen because it was easy +to implement on all base platforms and is straight-forward to use. But there +are two problems with this approach. + +First, the implementation of thread-local storage (TLS) is either platform +dependent or costly. There are kernels with support for TLS, mostly by the +means of a special register that holds a pointer to a thread-local data +structure (e.g., the UTCB pointer). But using such a facility implicates +platform-specific code on Genode's side. For kernels with no TLS support, we +introduced a unified TLS concept that registers stacks alongside with +thread-local data at a thread registry. To access the TLS of a thread, this +thread registry can be queried with the current stack pointer of a caller. +This query, however, is costly because it traverses a data structure. Up to +now, we accepted these costs because native Genode code did not use TLS. TLS +was only needed for code ported from the Linux kernel. However, with NOVA, +there is now a kernel that requires the user land to provide a fast TLS +mechanism to look up the current thread's UTCB in order to perform system +calls. On this kernel, a fast TLS mechanism is important. + +The second disadvantage of the original stack allocation scheme is critical +to all base platforms: Stack overflows could not be detected. For each stack, +the developer had to specify a stack size. A good estimation for this value +is hard, in particular when calling functions of library code with unknown +stack usage patterns. If chosen too small, the stack could overflow, corrupting +the data surrounding the 'Thread' object. Such errors are extremely cumbersome +to detect. If chosen too large, memory gets wasted. + +For storing thread-specific data (called thread context) such as the stack and +thread-local data, we have now introduced a dedicated portion of the virtual address +space. This portion is called thread-context area. Within the thread-context +area, each thread has a fixed-sized slot, a thread context. The layout of each +thread context looks as follows + +[image img/thread_context] + +; lower address +; ... +; ============================ <- aligned at 'CONTEXT_VIRTUAL_SIZE' +; +; empty +; +; ---------------------------- +; +; stack +; (top) <- initial stack pointer +; ---------------------------- <- address of 'Context' object +; additional context members +; ---------------------------- +; UTCB +; ============================ <- aligned at 'CONTEXT_VIRTUAL_SIZE' +; ... +; higher address + +On some platforms, a user-level thread-control block (UTCB) area contains +data shared between the user-level thread and the kernel. It is typically +used for transferring IPC message payload or for system-call arguments. +The additional context members are a reference to the corresponding +'Thread_base' object and the name of the thread. + +The thread context is a virtual memory area, initially not backed by real +memory. When a new thread is created, an empty thread context gets assigned +to the new thread and populated with memory pages for the stack and the +additional context members. Note that this memory is allocated from the RAM +session of the process environment and gets not accounted for when using the +'sizeof()' operand on a 'Thread_base' object. + +This way, stack overflows are immediately detected because the corresponding +thread produces a page fault within the thread-context area. Data corruption +can never occur. + +We implemented this concept for all base platforms and thereby made the +stack-overflow protection and the fast TLS feature available to all platforms. +On L4ka::Pistachio, OKL4, L4/Fiasco, Codezero, and NOVA, the thread-context +area is implemented as a managed dataspace. This ensures that the unused +virtual memory of the sparsely populated thread-context area is never selected +for attaching regular dataspaces into the process' address space. On Linux, the +thread-context area is implemented via a fixed offset added to the local +address for the 'mmap' system call. So on this platform, there is no protection +in place to prevent regular dataspaces from being attached within the +thread-context area. + +Please note that in contrast to the original 'Thread' object, which contained +the stack, the new version does not account for the memory consumed by the +stack when using the 'sizeof()' operator. This has to be considered for +multi-threaded servers that want to account client-specific threads to the +memory donated by the corresponding client. + + +Real-time priorities +#################### + +There are two application areas generally regarded as predestined for +microkernels, high security and real time. Whereas the development of Genode +was primarily focused on the former application area so far, we observe growing +interest in using the framework for soft real-time applications, in particular +multi-media workload. Most of Genode's supported base platforms already provide +some way of real-time scheduling support, hard priorities with round-robin +scheduling of threads with the same priority being the most widely used +scheduling scheme. What has been missing until now was a way to access these +facilities through Genode's API or configuration interfaces. We deferred the +introduction for such interfaces for a very good reason: It is hard to get +right. Even though priority-based scheduling is generally well understood, the +combination with dynamic workload where differently prioritized processes are +started and deleted at runtime and interact with each other is extremely hard +to manage. At least, this had been our experience with building complex +scenarios with the Dresden real-time operating system (DROPS). Combined with +optimizations such as time-slice donating IPC calls, the behaviour of complex +scenarios tended to become indeterministic and hardly possible to capture. + +Genode imposes an additional requirement onto all its interfaces. They have to +support the recursive structure of the system. Only if any subsystem of +processes is consistent on its own, it is possible to replicate it at an arbitrary +location within Genode's process tree. Assigning global priorities to single +processes, however, would break this condition. For example, non-related +subsystems could interfere with each other if both used the same range of +priorities for priority-based synchronization within the respective subsystem. +If executed alone, each of those subsystems would run perfectly but integrated +into one setup, they would interfere with each other, yielding unpredictable +results. We have now found a way to manage real-time priorities such that the +recursive nature Genode is not only preserved but actually put to good use. + + +Harmonic priority-range subdivision +=================================== + +We call Genode's priority management concept harmonic priority-range +subdivision. Priorities are not assigned to activities as global values but +they can be virtualized at each node in Genode's process tree. At startup time, +core assigns the right to use the complete range of priorities to the init +process. Init is free to assign those priorities to any of the CPU sessions it +creates at core, in particular to the CPU sessions it creates on behalf its +children and their grandchildren. Init, however, neither knows nor is it +interested in the structure of its child subsystems. It only wants to make sure +that one subsystem is prioritized over another. For this reason, it uses the +most significant bits of the priority range to express its policy but leaves +the lesser significant bits to be defined by the respective subsystems. For +example, if init wants to enforce that one subsystem has a higher priority than +all others, it would need to distinguish two priorities. For each CPU-session +request originating from one of its clients, it would diminish the supplied +priority argument by shifting the argument by one bit to the right and +replacing the most significant bit with its own policy. Effectively, init +divides its own range of priorities into two subranges. Both subranges, in +turn, can be managed the same way by the respective child. The concept works +recursively. + + +Implementation +============== + +The implementation consists of two parts. First, there is the actual management +implemented as part of the parent protocol. For each CPU session request, +the parent evaluates the priority argument and supplements its own policy. +At this management level, a logical priority range of 0...2^16 is used to pass +the policy arguments from child to parent. A lower value represents a higher +priority. The second part is the platform-specific code in core that translates +priority arguments into kernel priorities and assigns them to physical +threads. Because the typical resolution for priority values is lower than 2^16, +this quantization can lead to the loss of the lower-significant priority bits. +In this case, differently prioritized CPU sessions can end up using the same +physical priority. For this reason, we recommend to not use priorities for +synchronization purposes. + + +Usage +===== + +The assignment of priorities to subsystems is done via two additional tags in +init's 'config' file. The '' tag specifies how many priority levels +are distinguished by the init instance. The value must be a power of two. Each +'' node can contain an optional '' declaration, which holds a +value between -priolevels + 1 and 0. This way, priorities can only be lowered, +never alleviated above init's priority. If no '' tag is specified, +the default value of 0 (init's own priority) is used. For an example, here is a +'config' file starting several nested instances of the init process using +different priority subranges. + +! +! +! 2 +! +! init +! 0 +! 5M +! +! +! 4 +! +! init +! +! -1 +! 512K +! +! +! init +! +! -2 +! 2M +! +! +! init +! 768K +! +! +! +! +! +! +! init +! +! -1 +! 6M +! +! +! + +On kernels that support priorities and where priority 128 is used as priority +limit (this is the case for OKL4 and Pistachio), this configuration should +result in the following assignments of physical priorities to process-tree +nodes: + +[image img/priorities] + +The red marker shows the resulting priority of the corresponding process. + +; 128 : core +; 128 : core->init +; 128 : core->init->init +; 112 : core->init->init->init +; 98 : core->init->init->init.2 +; 98 : core->init->init->init.2->init +; 64 : core->init->init.2 + +With Genode 10.02, we implemented the described concept for the OKL4 and +L4ka::Pistachio base platforms first. On both platforms, a priority range of 0 +to 128 is used. + +On L4/Fiasco, we were not yet able to apply this concept because on this +kernel, the used lock implementation is based on a yielding spinlock. +If a thread at a high priority would attempt to acquire a contended lock, +it would infinitely yield the CPU to itself, letting all other threads in +the system starve. In order to make real-time priorities usable on L4/Fiasco +we would need to change the lock first. + + +Base framework +############## + +Read-only dataspaces +==================== + +Until now, we have not handled ROM dataspaces any different from RAM dataspaces +in core except for their predefined content. With the Genode workload becoming +more complex, ROM files tend to get shared between different processes and need +protection. Now, dataspaces of ROM modules are always mapped read-only. + +Enabled the use of super pages by default +========================================= + +Since release 9.08, we support super pages as an experimental feature. Now, +this feature is enabled by default on L4/Fiasco, L4ka::Pistachio, and NOVA. + +Enabled managed dataspaces by default +===================================== + +We originally introduced managed dataspaces with the release 8.11. However, +because we had no pressing use cases, it remained a experimental feature +until now. The new thread-context management introduced with this release +prompted us to promote managed dataspaces to become a regular feature. +Originally there was one problem holding us back from this decision, which +was the handling of cyclic references between nested dataspaces. However, +we do now simply limit the number of nesting levels to a fixed value. + +Streamlined server framework +============================ + +We removed the 'add_activation()' functionality from the server and pager +libraries because on all platforms server activations and entry points have +a one-to-one relationship. This API was originally intended to support +platforms that are able to trigger one of many worker threads via a single +entry point. This was envisioned by an early design of NOVA. However, no +kernel (including NOVA) supports such a feature as of today. + +Furthermore, we added a dedicated 'Pager_capability' type. On most +platforms, a pager is simply a thread. So using a 'Thread_capability' as type +for the 'Pager_capability' was sufficient. On NOVA, however, a pager is not +necessarily a thread. So we need to reflect this difference in the types. + +PD session interface +==================== + +To support capability kernels with support for local names, it is not +sufficient to provide the parent capability to a new child by passing a plain +data argument to the new child during ELF loading anymore. We also need to tell +the kernel about the delegated right of the child to talk to its parent. This is +achieved using the new 'assign_parent' function of the PD session interface. +This function allows the creator of a new process to register the parent +capability. + +Singleton services +================== + +There are services, in particular device drivers, that support only one session +at a time. This characteristic was not easy to express in the framework. +Consequently, such services tended to handle the case of a second session +request inconsistently. We have now enhanced the 'Root_component' template with +a policy parameter to 'Root_component' that allows the specification of a +session-creation policy. The most important policy is whether a service can +have a single or multiple clients. +[http://genode.org/documentation/api/static_content/code/base/include/root/component - See the improved template...] + +Out-of-order RPC replies +======================== + +In the previous release, we introduced a transitional API for supporting +out-of-order RPC replies. This API is currently used by the timer and +signal services but is declared deprecated. The original implementation +used a blocking send operation to deliver replies, which is not desired +and can cause infinite blocking times in the presence of misbehaving clients. +Therefore, we changed the implementation to send explicit replies with no +timeout. Thanks to Frank Kaiser for pointing out this issue. + + +Operating-system services and libraries +####################################### + +Python scripting +================ + +We have ported a minimal Python 2.6.4 interpreter to Genode. The port is +provided with the 'libports' repository. It is based on the official +Python code available from the website: + +:Python website: + [http://www.python.org] + +To fetch the upstream Python source code, call 'make prepare' from within the +'libports' directory. To include Python in your build process, add 'libports' +to your 'build.conf' file. + +A test program for the script interpreter is provided at +'libports/src/test/python'. When building this test program, a shared library +'python.lib.so' will be generated. A sample Genode configuration +('config_sample') file that starts a Python script can be found within this +directory. If you are not using Linux as a Genode base platform, do not forget +to add 'python.lib.so' to your boot module list. + +We regard this initial port as the first step to make a complete Python +runtime. At the current stage, there is support for 'Rom_session' Python +scripts to serve basic scripting needs, currently geared towards automated +testing. Modules and standard modules are not yet supported. + + +Plugin-interface for the C library +================================== + +The recent addition of the lwIP stack to Genode stimulated our need to make the +C runtime extensible by providing multiple back ends, lwIP being one of them. +Therefore, we introduced a libc-internal plugin interface, which is able to +dispatch libc calls to one of potentially many plugins. The plugin interface +covers the most used file operations and a few selected networking functions. +By default, if no plugin is used, those functions point to dummy +implementations. If however, a plugin is linked against a libc-using program, +calls to 'open' or 'socket' are directed to the registered plugins, resulting +in plugin-specific file handles. File operations on such a file handle are then +dispatched by the corresponding plugin. + +The first functional plugin is the support for lwIP. This makes it possible to +compile BSD-socket based network code to use lwIP on Genode. Just add the +following declaration in your 'target.mk': + +! LIBS += libc libc_lwip lwip + +The 'libc' library is the generic C runtime, 'lwip' is the raw lwIP stack, and +'libc_lwip' is the lwip plugin for the C runtime - the glue between 'lwip' and +'libc'. The initialization of lwip is not yet part of the 'lwip' plugin. + +:Limitations: +We expand the libc-plugin interface on a per case basis. Please refer to +'libc/include/libc-plugin/plugin.h' to obtain the list of currently supported +functions. Please note that 'select' is not yet supported. + + +ARM architecture support for the C library +========================================== + +We enhanced our port of the FreeBSD libc with support for the ARM +architecture. In the ARM version, the following files are excluded: +:libm: 'e_acosl.c', 'e_asinl.c', 'e_atan2l.c', 'e_hypotl.c', 's_atanl.c', + 's_cosl.c', 's_frexpl.c', 's_nextafterl.c', 's_nexttoward.c', + 's_rintl.c', s_scalbnl.c', 's_sinl.c', 's_tanl.c', 's_fmal.c', + +:libc-gen: 'setjmp.S' + +Atomic operation on ARM are not supported. Although these operations are +defined in 'machine/atomic.h', their original FreeBSD implementations are +not functional because we do not emulate the required FreeBSD environment +(see: 'sysarch.h'). However, these functions are not a regular part of +the libc anyway and are not referenced from any other libc code. + + +Light-weight IP stack +===================== + +After introducing LwIP support with our last release, we stabilized the port +and combined it with our libc implementation. Moreover, we upgraded the lwIP +library to the latest stable version 1.3.2. For convenience reasons, we +added initialization code, setting up the LwIP stack, the NIC session back end, +and optionally DHCP. + +The example programs 'http_srv' and 'loopback' within the 'libports' repository +show how to use the LwIP stack directly or as a plugin with the libc. The +first one makes direct use of the LwIP library and demonstrates how to deal +with the new initialization routine, to setup the session to the NIC driver +and to request an IP address via DHCP. The second example doesn't use the +socket interface of the LwIP library directly but uses the libc variant instead. +It doesn't initialize the NIC session back end but uses the loopback +device provided by the LwIP library itself. + + +Device-driver environment kit +============================= + +The basis for Genode's legacy driver emulation environment was granted some +maintenance. DDE kit now utilizes the thread registry and is able to adopt +alien threads. This unimpressive feature permits the execution of driver code +directly from server activations, i.e., adds support for single-threaded +drivers. + + +Dynamic linker +============== + +We added dynamic linking support for OKL4 on the ARM architecture. +Because of the tool chain used on this platform, we had to revisit our +linker scripts (one warning is left because of 'gc-sections') and removed the +dependency on gcc builtin functions (with the exception of 'alloca'). + +To ease debugging on Linux, we revised the handling of registrations of +libraries and dynamic binaries, and thereby, made gdb debugging of +dynamically linked programs possible. + +Furthermore, we prepared the future support for the 'dl' API ('dlopen', 'dlsym' +etc.) calls by enabling the linker to register exported linker symbols at startup. +This is achieved by emulating '.hash', '.dynsym', and '.dynstr' sections within +the linker object. + + +Misc +==== + +* Prevent running over the XML data on sub node identification. This + change fixes a problem with parsing the 'config' file on OKL4. + +* C Runtime: Disable definition of 'pthread_cancel' symbol because it + collides with a weak implementation provided (and relied on) by the C++ + support library. + + +Device drivers +############## + +PIT timer driver +================ + +We use the x86 Programmable Interval Timer (PIT) on kernels that provide no +time source via their kernel APIs, i.e., OKL4 and NOVA. + +Up to now, the accuracy of the timer implementation was not a big concern +because we wanted to satisfy the use cases of blocking for a short amount +of time (ca. 10ms) as needed by many periodic processes such as interactive +GUI applications, DDE device drivers, and the OKLinux timer loop. Achieving +exact wake-up times with a user-level timing service that get preemptively +scheduled alongside an unknown number of other threads is impossible anyway. +However, with the introduction of real-time priorities in the current release, +real-time workload and the accuracy of the timer driver becomes important. +For this reasons we improved the timer implementation. + +* Corrected programming of one-shot timer IRQs. In the function for assigning + the next timeout, the specified argument was not taken over to the + corresponding member variable. This way, the timer implementation was not + operating in one-shot mode but it periodically triggered at a high rate. + This change should take off load from the CPU. + +* Replaced counter-latch command with read-back in PIT timer. We use the PIT + status byte to detect counter wrap arounds and adjust our tick count + accordingly. This fixes problems with long single timeouts. + +Thanks to Frank Kaiser for investigating these timer-accuracy issues and +providing us with suggestions to fix them. + + +NIC driver for Linux +==================== + +Genode provides the NIC session interface and a DDE Linux 2.6 based +driver for AMD PCnet32 devices since release 9.11. The NIC driver adds +networking support for all Genode base platforms except Linux. With the current +release we filled that gap with the TAP-based 'nic_drv'. The driver +itself accesses '/dev/net/tun' for 'tap0' and needs no super-user +privileges. Therefore, the device has to be configured prior to +running Genode like the following. + +! sudo tunctl -u $$USER -t tap0 +! sudo ip link set tap0 up +! sudo ip address add 10.0.0.1/24 brd + dev tap0 + +Give it a try with the +[http://genode.org/documentation/release-notes/9.11#section-17 - lwIP example scenario]. +Please note that lwIP is configured for DHCP and does not assign a +static IP configuration to its end of the wire. Hence, you should run +a DHCP server on tap0, e.g. + +! sudo /usr/sbin/dhcpd3 -d -f -cf /tmp/dhcpd.conf \ +! -pf /tmp/dhcpd.pid -lf /tmp/dhcpd.lease tap0 + +An example 'dhcpd.conf' may look like + +! subnet 10.0.0.0 netmask 255.255.255.0 { +! range 10.0.0.16 10.0.0.31; +! } + +The DHCP server's log will show you that the driver fakes an ethernet +NIC with the MAC address 02-00-00-00-00-01. + + +VESA driver +=========== + +Our VESA driver used to set a default resolution of 1024x768 at 16 bit color +depth, which could be changed by specifying session arguments. However, most +of the time, clients are able to adapt itself to the framebuffer resolution and +do not want to implement the policy of defining the screen mode. Now we made the +VESA driver configurable, taking the burden of choosing a screen mode from the +client. A client can still request a particular resolution but for the common +case, it is policy free. + +If no configuration is supplied, the driver sets up a resolution of 1024x768 at +16 bit color depth. This behaviour can be overridden by supplying the following +arguments via Genode's config mechanism: + +! +! +! 1024 +! +! +! 768 +! +! +! 16 +! + +Note that only VESA modes but no arbitrary combination of values are supported. +To find out which graphics modes exist on your platform, you might use the +'vbeprobe' command of the GRUB boot loader. Also, the driver will print a list +of supported modes if the specified values are invalid. + + +Paravirtualized Linux refinements +################################# + +The para-virtualized Linux port introduced in Genode Release 9.11 has been +refined, especially the block driver providing a root file system for Linux +has been completely reworked. Also the configuration facilities changed a bit. +Moreover, few problems that occurred when using multiple Linux instances, or +when using one instance under heavy load have been fixed. At this point, we +like to thank Sven Fülster for providing information and a fix for a bug +triggered by a race condition. + +:Repository structure: +We rearranged the structure of the 'oklinux' repository. The downloaded +archive and the original OKLinux code are now stored under 'download' +and respectively 'contrib' analog to the 'libports' repository +structure. + +:Rom-file block driver: +The block driver using a ramdisk as backing store as contained in the original +OKLinux port has been replaced by a new implementation that uses a dataspace +provided by the ROM session interface to provide a read-only block driver. + +The read-only block driver can be used together with UnionFS (stackable +file system) or the Cowloop driver (copy-on-write block device) for Linux to +obtain a writeable root-file system, like it is done in many Linux Live-CDs. + +To use the new rom-file block driver you first need to specify what file to use +as block device. This can be done by adding a 'rom_file' section in the XML +configuration of your Linux instance: + +! +! rootfs.img +! + +Of course, you need to add this file to your list of boot modules. + +The block device is named 'gda' within the Linux Kernel (e.g., take +a look at '/proc/partitions'). When using it as root-file system, you +might specify the following in your configuration: + +! +! root=/dev/gda1 +! rootfs.img +! + +Assuming the rom-file contains a valid partition table and the root file system +is located in the first partition. + + +Distribution changes +#################### + +Starting with the release 10.02, we will no longer distribute our slightly +customized version of the L4/Fiasco kernel together with the official Genode +distribution but instead will provide this kernel as a separate archive. Our +original intention with packaging L4/Fiasco with Genode was to give newcomers +a convenient way to start working with Genode on a real microkernel without the +need to download the whole TUDOS source tree where the main-line development of +L4/Fiasco is hosted. In the meanwhile, the number of supported base platforms +greatly increased to 6 different kernels. There are now plenty of opportunities +to get started with real microkernels so that the special case of hosting +L4/Fiasco with Genode is no longer justified. We want to leave it up to you to +pick the kernel that suits your needs best, and provide assistance via our wiki +and mailing list. + diff --git a/doc/release_notes-10-05.txt b/doc/release_notes-10-05.txt new file mode 100644 index 000000000..3be144657 --- /dev/null +++ b/doc/release_notes-10-05.txt @@ -0,0 +1,1211 @@ + + + =============================================== + Release notes for the Genode OS Framework 10.05 + =============================================== + + Genode Labs + + + +With version 10.05, the Genode project aimed at creating the infrastructure +needed to accommodate usage scenarios of ever increasing complexity. We are +excited to have reached the milestone to run the first version the +fully-fledged Arora web browser as native Genode process. The other major leap +regarding Genode's infrastructure is a new configuration concept that lets us +realize usage scenarios that we have dreamed of for a long time. + +With the new configuration concept, we are now able to leverage the full power +of Genode's hierarchical process structure. It enables us to implement +fine-grained access control to all services that our system is comprised of. +But the concept just as well offers extremely flexible ways of routing +client-session requests throughout the Genode process tree to a matching +server. We are looking forward to present several showcase scenarios of this +new tool soon. + +The second major focus for the current release was adding support for audio +output. We created a modular audio-streaming solution consisting of device +drivers for popular sound cards, an audio-mixer component, and a client +application interface. The combination of these new components with +real-time priorities introduced with the previous release and Genode's new +configuration concept lays the foundation for high-quality audio processing +on Genode. + +Apart from these major topics, the new version comes with numerous functional +improvements. For example, our ongoing efforts to tightly integrate the +paravirtualized OKLinux kernel with native Genode components have culminated +in the added support for the seamless integration of the X Window System with +Genode's nitpicker GUI. + +To boost the productivity of the Genode developers, we have implemented +a new build system, which is compatible with the original one but operates +much faster, in particular when used on systems with multiple CPUs. + + +Enabling mandatory access control +################################# + +Since drafting the Genode architecture, we envisioned a flexible way to +construct complex usage scenarios out of Genode's process nodes +used as generic building blocks. Thanks to the strictly hierarchic and, +at the same time, recursive structure of Genode, a parent has full control +over the way, its children interact with each other and with the parent. +The initial implementation provided three different examples of such +policies, core's policy regarding the init process, the static policy +of the init process, and a more dynamic policy of the interactive +launchpad application. + +:Core's policy: assigns all resources not used by core itself to init. + Session requests coming from init could only refer to one of core's + locally implemented services. Because init is the only child of core, + there is no policy about the interaction between multiple children. + +:Init's policy: is driven by a configuration file, which declares a number + of children, their respective memory quotas and file names. Each child is + able to announce services but each service can be announced only once. Any + attempt of a child to announce an already existing service is denied. Session + requests of all children are resolved in a uniform way. If the requested + session refers to a service provided by core, init delegates the session + request to its parent. These services are hard-coded to RAM, PD, RM, ROM, + CPU, IRQ, IO_MEM, IO_PORT, CAP, SIGNAL, and LOG. Otherwise, the session + request is delegated to one of the children. If the requested service is not + yet announced, the aspiring client is put to halt until the service becomes + available. + +:Launchpad's policy: enriches init's policy by a small detail, but with + a great effect. Instead of using fixed set of service names to take the + decision about whether to forward a session request to the parent or to one + of the other children, it implements the following rule: If the requested + service was announced by a child of launchpad, the request is delegated to + the child. Otherwise, the request is delegated to the parent. This simple + modification allows children to override arbitrary services normally provided + by core. For example, the nitlog application implements core's LOG interface. + After started, all requests for a LOG session end up at nitlog instead of + core, and this way, LOG output could be easily routed to the screen rather + than to core's debug output. Another example is exercised by the tutorial of + Genode default demo scenario, which allows for starting a second instance of + the nitpicker GUI server within a windowed frame buffer, which, in turn, + relies on the first instance of nitpicker. + +Those three policies served us well for the past three years. Core's policy +is simple and exactly what core needs for starting and accommodating the +init process. Launchpad's policy illustrates well the power of Genode's +recursive structure. But the limitations of init's policy have become +apparent with our more recent use cases. We observed the following limitations. + +* The set of services provided by the parent is predefined and hard-coded + in the source code of init. Even though init's configuration concept allows + for running multiple nested init instances, the fixed set of parent services + severe limits the practical use of this feature. In fact, for some use + cases, we had to combine different init implementations to achieve our + goals. + +* Within one instance of init, there is no way to restrict the access of one + child to services provided by another child or to core's services. + +* Among children of one init instance, each service can be announced only + once, based on a first-come-first-serve policy. There is no restriction + about which child is permitted to announce which service. But there are + legitimate uses of having multiple implementations of one interface present. + For example, in the presence of more than one network cards, multiple network + drivers may need to announce a NIC service each. + +Despite of these limitations, the init configuration has one strong point, +which is ease of use. Our challenge with designing a new configuration +concept was to overcome the mentioned limitations while preserving the +ease of use as far as possible. + + +Configuration +============= + +At the parent-child interface, there are two operations that are subject to +policy decisions of the parent, the child announcing a service and the +child requesting a service. If a child announces a service, the parent is up +to decide if and how to make this service accessible to its other children. +When a child requests a service, the parent may deny the session request, +delegate the request to its own parent, implement the requested service +locally, or open a session at one of its other children. This decision may +depend on the requested service or session-construction arguments provided +by the child. Apart from assigning resources to children, the central +element of the policy implemented in the parent is a set of rules to +route session requests. Therefore, the new configuration concept is laid out +around processes and the routing of session requests. The concept is best +illustrated by an example (executable on Linux): + +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! + +First, there is the declaration of services provided by the parent of the +configured init instance. In this case, we declare that the parent provides a +CAP service and a LOG service. For each child to start, there is a '' +node describing resource assignments, declaring services provided by the child +and holding a routing table for session requests originating from the child. +The first child is called "timer" and implements the "Timer" service. To +implement this service, the timer requires a CAP session. In the routing table, +we define that a CAP session request gets delegated to init's parent. The +second process called "test-timer" is a client of the timer service. In its +routing table, we see that requests for "Timer" sessions should be routed to +the "timer" child whereas requests for "LOG" sessions should be delegated to +init's parent. Per-child service routing rules provide a flexible way to +express arbitrary client-server relationships. For example, service requests +may be transparently mediated through special policy components acting upon +session-construction arguments. There might be multiple children implementing +the same service, each addressed by different routing tables. If there is no +valid route to a requested service, the service is denied. In the example +above, the routing tables act effectively as a whitelist of services the child +is allowed to use. + +In practice, usage scenarios become more complex than the basic example, +increasing the size of routing tables. Furthermore, in many practical cases, +multiple children may use the same set of services, and require duplicated +routing tables within the configuration. In particular during development, the +elaborative specification of routing tables tend to become an inconvenience. +To alleviate this problem, there are two mechanisms, wildcards and a default +route. Instead of specifying a list of single service routes targeting the same +destination, the wildcard '' becomes handy. For example, instead +of specifying +! +! +! +! +! +! +! +the following shortcut can be used: +! +! +! +The latter version is not as strict as the first one because it permits the +child to create sessions at the parent, which were not whitelisted in the +elaborative version. Therefore, the use of wildcards is discouraged for +configuring untrusted components. Wildcards and explicit routes may be combined +as illustrated by the following example: +! +! +! +! +The routing table is processed starting with the first entry. If the route +matches the service request, it is taken, otherwise the remaining +routing-table entries are visited. This way, the explicit service route of +"LOG" sessions to "nitlog" shadows the LOG service provided by the parent. + +To emulate the traditional init policy, which allows a child to use services +provided by arbitrary other children, there is a further wildcard called +''. Using this wildcard, the traditional policy can be expressed +as follows: +! +! +! +! +This rule would delegate all session requests referring to one of the parent's +services to the parent. If no parent service matches the session request, the +request is routed to any child providing the service. The rule can be further +reduced to: +! +! +! +Potential ambiguities caused by multiple children providing the same service +are detected automatically. In this case, the ambiguity must be resolved using +an explicit route preceding the wildcards. + +To reduce the need to specify the same routing table for many children +in one configuration, there is a '' mechanism. The default +route is declared within the '' node and used for each '' +entry with no '' node. In particular during development, the default +route becomes handy to keep the configuration tidy and neat. + +We believe that with the combination of explicit routes and wildcards, we +have designed a solution that scales well from being convenient to use during +development towards being highly secure at deployment time. If only explicit +rules are present in the configuration, the permitted relationships between all +processes are explicitly defined and can be easily verified. Note however that +the degree those rules are enforced at the kernel-interface level depends on +the used base platform. + + +Advanced features +================= + +In addition to the service routing facility described in the previous section, +the following features are worth noting: + + +Resource quota saturation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a specified resource (i.e., RAM quota) exceeds the available resources. +The available resources are assigned completely to the child. This makes +it possible to assign all remaining resources to the last child by +simply specifying an overly large quantum. + + +Multiple instantiation of a single ELF binary +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each '' node requires a unique 'name' attribute. By default, the +value of this attribute is used as file name for obtaining the ELF +binary at the parent's ROM service. If multiple instances of the same +ELF binary are needed, the binary name can be explicitly specified +using a '' sub node of the '' node: +! +This way, the unique child names can be chosen independently from the +binary file name. + + +Nested configuration +~~~~~~~~~~~~~~~~~~~~ + +Each '' node can host a '' sub node. The content of this sub +node is provided to the child when a ROM session for the file name "config" is +requested. Thereby, arbitrary configuration parameters can be passed to the +child. For example, the following configuration starts 'timer-test' within an +init instance within another init instance. To show the flexibility of init's +service routing facility, the "Timer" session of the second-level 'timer-test' +child is routed to the timer service started at the first-level init instance. +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +The services ROM, RAM, CPU, RM, and PD are required by the second-level +init instance to create the timer-test process. + +As illustrated by this example, the use of the nested configuration feature +enables the construction of arbitrarily complex process trees via a single +configuration file. + + +Priority support +~~~~~~~~~~~~~~~~ + +The number of CPU priorities to be distinguished by init can be specified with +'prio_levels' attribute of the '' node. The value must be a power of +two. By default, no priorities are used. To assign a priority to a child +process, a priority value can be specified as 'priority' attribute of the +corresponding '' node. Valid priority values lie in the range of +-prio_levels + 1 (maximum priority degradation) to 0 (no priority degradation). + + +Using the new configuration concept +=================================== + +With the current release, the old configuration concept is still enabled by +default. With the upcoming release, we will change the default to the new +concept and declare the old concept as obsolete and to-be-removed. To enable +the new concept now, all you need to do is adding the following line to your +'/etc/specs.conf' file: +! SPECS += use_new_init +By adding this line, the build system will build the init variant at +'os/src/init/experimental' rather than the default variant at 'os/src/init'. +To get acquainted with the new configuration format, there are two example +configuration files located at 'os/src/init/experimental', which are both +ready-to-use with the Linux version of Genode. Both configurations produce the +same scenario but they differ in the way policy is expressed. The +'explicit_routing' example is an example for the elaborative specification +of all service routes. All service requests not explicitly specified +are denied. So this policy is a whitelist enforcing mandatory access +control on each session request. The example illustrates well that such a +elaborative specification is possible in an intuitive manner. However, it is +significantly more comprehensive than a traditional configuration file of +init. In cases where the elaborative specification of service routing is not +fundamentally important, in particular during development, the use of wildcards +can help to simplify the configuration. The 'wildcard' example demonstrates the +use of a default route for session-request resolution and wildcards. This +variant is less strict about which child uses which service. For development, +its simplicity is beneficial but for deployment, we recommend to remove +wildcards ('', '', and '') altogether. +The absence of such wildcards is easy to check automatically to ensure that +service routes are explicitly whitelisted. + + +Base framework +############## + +This section provides a description of a number of small changes of the base +framework. It is rather detailed to ease the migration of existing code to the +new Genode version. + + +New child management framework +============================== + +To realize the new configuration concept of init, we completely reworked the +child-management classes in 'base/child.h', 'base/service.h', and +'init/child.h'. The 'Child' class used to implement the most basic policy that +applies to all children, which comprises the protocols for trading memory quota +and the handling of the child's environment sessions. It was meant to be +derived by more a specialized policy such as init's policy. Thereby, each +derived implementation of the 'Child' class used to tailor the policy to a +further degree. We identified two problems with this approach. First, the +policy resulting from tweaking one or more inherited policies became hard to +grasp because it was spread in multiple files. For example, launchpad's policy +is influenced by 'launchpad.h', 'init/child.h', and 'base/child.h'. Second, we +observed that the generic program logic for resource trading was closely +intermingled with policy-specific code. This way, modifying an inherited policy +resulted in duplicating program logic code of the inherited class. + +With the new implementation, we completely separated the raw mechanisms +needed for running a child process from its policy. To illustrate the change, +lets look at the difference between the old and new 'Child' constructor: + +The original version looked a follows: +! Child(const char *name, +! Dataspace_capability elf_ds, +! Ram_session_capability ram, +! Cpu_session_capability cpu, +! Cap_session *cap, +! char *args[]) +The 'Child' class used to aggregate several pieces needed to run a child. +In particular, it contained a dedicated entry point and server-activation +thread to serve the parent interface for the child. The 'cap' argument +was merely required to construct the entry point. This design resulted +in a number of problems: The stack size of the server-activation thread +was fixed and could not be changed by an inherited class. Because the +entry point and server-activation thread were embedded in the child, +special accessor functions were needed to let the creator of a 'Child' +interact with them. For example, the start of the server activation +had to be triggered by a special 'finalize_construction' call. For using +the entry point for serving additional local services, the special accessor +function 'parent_entrypoint' was needed. + +The new 'Child' constructor looks as follows: +! Child(Dataspace_capability elf_ds, +! Ram_session_capability ram, +! Cpu_session_capability cpu, +! Server_entrypoint *entrypoint, +! Child_policy *policy) +One prominent change is that the entry point is now supplied as an argument, +which in principle allows for sharing one entry point by multiple children and +local services, and enables the use of arbitrary stack sizes. The more +significant change is the new 'Child_policy' argument, supplied to the +new child. The 'Child_policy' interface consists of the following functions: + +! const char *name() const; +The 'name' function returns the name of the child, which was previously be +directly supplied as argument to the 'Child' constructor. + +! Service *resolve_session_request(const char *service_name, +! const char *args); +This function is central to routing service requests to service providers. +A 'Service' is either a locally implemented service, a service provided by the +parent, or a service provided by another child. If the service request is +denied altogether, the function returns 0. Thanks to the 'args' argument, the +resolution of the service requests can be made dependent on session-construction +arguments, for example a 'filename' argument supplied to a new 'ROM' session. + +! void filter_session_args(const char *service, +! char *args, size_t args_len); +This function enables the transformation of session-construction arguments. Its +uses are manifold. For example, labeling each session by prefixing the 'label' +session argument with the child name informs the server about the identity of +the client (see 'Child_policy_enforce_labeling'). Another example is the +transformation applied to the CPU priority as introduced by the Genode version +10.02 (see 'Child_policy_handle_cpu_priorities'). + +! bool announce_service(const char *name, +! Root_capability root, +! Allocator *alloc) +This function takes note of new service announcements coming from the child. +An announcement can be denied by returning 0. Otherwise, the policy is free +to consider the new service for subsequent 'resolve_session_request' calls +coming from other children. + +With this policy toolkit, it was not only possible to easily reconstruct the +original behaviour of 'Core_child', 'Init::Child', and 'Launchpad::Child', it +also enabled the new configuration concept and service routing described in +Section [Enabling mandatory access control]. The 'Child' class defined in +'base/child.h' is no longer meant as a base class for customized child policies +but it solely contains the program logic for managing open sessions and +performing resource trading. + +Because 'base/child.h' is closely related to 'base/service.h', the classes +defined in the latter were also subject to a major overhaul. The new 'Service' +interface removed the need for differently typed service pools. Instead +there is now a single 'Service_registry'. Since the 'Service' class abstracts +from parent, local, or child servers, it obsoleted the 'Session_control' +interface. + + +Flexible page sizes +=================== + +Core's generic page-fault handling code has become able to handle any page size +supported by underlying platforms. On OKL4, L4ka::Pistachio, NOVA, and +L4/Fiasco, core uses flexpages as big as possible. The used subset of supported +page size can be tailored for each platform using a new helper function in the +platform-specific 'core/include/util.h'. The function 'constrain_map_size_log2' +takes a page size (log2) as argument and returns a nearest (lower or equal than +the argument) possible page size to be used for a mapping on this platform. + +To further unify the code among different kernels, we made the generic code +agnostic about the mapping source. On some kernels (e.g., OKL4, Codezero), map +operations refer to a physical address as source. On other kernels (typically, +kernels that rely on a mapping database), a core-local virtual address is used +as mapping source. Now, we introduced the function 'map_src_addr', which takes +both a core-local and a physical address as argument and returns one of those, +depending on the kernel. + + +Miscellaneous changes +===================== + +:Exception types: + +* Structured exception types of 'Parent' interface +* Introduced 'Rom_connection_failed' exception to the 'Rom_connection' + interface. Often, 'Rom_connection' errors are caused by user errors, which + are hard to come by when the program aborts with a 'Parent::Service_denied' + exception. The new exception type make the problem cause easier to spot. +* Removed redundant 'Rom_file_missing' exception from the 'Rom_session' + interface. By definition, this exception cannot occur because a ROM session + for a non-existing file will throw a 'Parent::Service_denied' exception. + + +:Growing heap chunks: + +The heap used to grow by chunks of 16 KB (on 32 bit) respectively 32 KB (on 64 +bit) blocks for small allocations. Each block is backed by an independent +dataspace. On Linux, this is problematic because each dataspace is represented +as a distinct file attached to the local address space via 'mmap'. Because on +Linux, the maximum number of open file descriptors is limited to 1024, Genode +processes on Linux could not use more than 16/32 MB of small memory blocks. +This limitation became noticeable with Qt4 applications, which rely on a large +number of small allocations. To alleviate this problem, we changed the +allocation of heap chunks from a fixed block size to an exponentially growing +block size, capped at a maximum block size of 4/8 MB. + + +:String parsing functions: + +We unified the various ascii-parsing functions such as 'ascii_to_ulong', +'ascii_to_long', 'ascii_to_size', 'ascii_to_double' defined in +'util/string.h' to be overloads of a single function template called +'ascii_to': +! template +! size_t ascii_to(const char *s, T *result, +! unsigned base = 0); +There are specializations for 'long', 'unsigned long', and 'double'. For +parsing size values suffixed with 'K', 'M', or 'G', there is a helper class +called 'Number_of_bytes' wrapping 'size_t'. When passing a pointer to such an +object as argument to 'ascii_to', the suffix-handling specialization is +selected. + +The use of overloading makes it easier to use the parsing functionality from +function templates which can thereby stay type-independent. For example, +'Xml_node::value' already benefits from this unification. Please note that we +removed the string-length argument that was supported by some of the previous +parsing functions. All 'ascii_to' implementations do expect null-terminated +strings. + + +:Tokenizer: + +The 'Token' class is used by the 'Arg_string' class and the XML parser, which +used to employ the same syntactic rules for identifiers. Because this is no +longer the case, we made those rules customizable by turning the 'Token' class +into a class template taking a scanner policy as argument. The scanner policy +consists of a boolean function 'identifier_char' for classifying a character as +part of an identifier. + + +:Synchronization: + +* Removed a rare race condition in the generic 'Cancelable_lock' + implementation introduced by a compiler optimization. We discovered this + problem first while running multiple instances of OKLinux under heavy load. + +* Corrected the handling of lock cancellations for locks that were doubly + locked by the same thread. The cancellation condition is detected by + verifying the current lock owner when being waked up. If the unblocking + was caused by a regular 'unlock' by the old lock owner, the unblocked + thread is the new lock owner. Otherwise, the blocking was canceled. + However, if a thread took a lock twice, it is the lock owner, regardless + of the cause of getting unblocked. So lock cancellations went undetected + in this special case. We solved this ambiguity by reseting the lock + ownership when the current lock owner tries to take the lock again. + + +:Startup code and C++ runtime: + +In '_main', we make sure to initialize the exception handling prior processing +global constructors. This way, exceptions that occur while executing such +constructors can be handled. Because the exception-handling initialization +relies on 'malloc', which in turn relies on the heap, which in turn, depends on +'Genode::env()', global constructors must no longer be used in the base +framework. Otherwise, this change would result in cyclic dependencies. Hence, +we replaced the last few occurrences of global constructors with local static +constructors. For example, in 'base-okl4/src/base/console/okl4_console.cc', we +replaced: +! static Okl4_console okl4_console; +with +! static Okl4_console &okl4_console() +! { +! static Okl4_console static_okl4_console; +! return static_okl4_console; +! } +and replaced the use of the 'okl4_console' object by the respective call +of 'okl4_console()'. + +Of course, global constructors outside the Genode base frameworks are fully +supported. + + +:Dedicated I/O-port thread in core: + +To reduce the temporal inter-dependency of I/O port accesses from core's +services, we moved the I/O port service to a separate thread within core. +This way, I/O port operations are performed out of band with other core's +services. The immediate effect is an improved accuracy of the PIC timer +driver. + + +Operating-system services and libraries +####################################### + +XML parser +========== + +Since the initial Genode release, we employ a simple XML parser for runtime +configuration purposes. The parser comes in the form of the single header file +'os/include/util/xml_node.h'. We tied the feature set of the parser to the +simple structures that we use for the init process, supporting nested '' +and '' as well as XML comments. This simple parser served as well +over the last two years. The new configuration concept introduced with the +current release, however, prompted us to reconsider its feature set. We noted +that although using the existing markup to express complex attributes and +relations is principally possible, the resulting configuration files tend to +become incomprehensible and hard to maintain. After exemplarily applying XML +attributes and empty-element tags to our show-case configurations, we observed +that those negative effects could be entirely mitigated. On the other hand, +extending the XML parser would imply an increase in code complexity, which we +generally want to avoid. Fortunately, with around 100 lines of code added for +the provisioning of XML attributes and empty-element tags, the effect on code +complexity turned out to be reasonably low so that we decided for the extension +of the XML parser. Because this extension would require an API change, we took +the chance of further streamlining the interface and implementation of the +XML parser. The changes are: + +* The length-limited parsing of XML data is now fully implemented. +* The supported forms for tag names are no longer a subset of the XML + standard. Now, underline, colon, dot, and minus characters are supported. +* Optimized the search through an XML node for a specified node type by + omitting a call of '_num_sub_nodes'. This way, the XML node is walked only + once per call, not twice. +* The accessor functions for the content of an XML node ('content', 'read') + had been unified to the template function 'value', which allows for the + interpretation of all types supported by the 'ascii_to' overloads defined + in 'base/include/util/string.h'. +* Renamed function 'is_type' to 'has_type' +* Added support for empty-element tags of the form ' +* Added support for XML attributes using the new 'Xml_node::Attribute' + class +* Renamed exception type 'Nonexistent_subnode' to 'Nonexistent_sub_node' to + foster consistence with the Genode coding style + + +Timed semaphore +=============== + +We added a semaphore variant supporting timeouts to the 'os' repository. +The new 'Timed_semaphore' extends the interface of the original semaphore +with a new 'down' function taking a timeout as argument. The implementation +relies on a jiffies thread that is started the first time a timeout is +used. The timeout granularity is set to 50 ms to accommodate our current +use cases (TCP timeouts and 'select' timeouts). + + +Added 'select' to C library +=========================== + +We enhanced our C library with a 'select' implementation that interplays +with the libc plugin concept. This way, a libc plugin such as the lwIP back +end can wake up blocking 'select' calls that include their respective file +descriptors. Furthermore, the new 'select' implementation includes support +for timeouts. The timeout handling is provided via a timed semaphore. + +Further changes are the addition of 'lseek' and '_open' to the plugin +interface, and the a sanitized dummy for 'gai_strerror', which is required +for running Arora. + + +Device-class interfaces for NIC and Audio-out +============================================= + +While looking into implementing further device class interfaces, we observed +recurring patterns in the use of Genode's packet stream API. Naturally, there +are four use cases that are related to packet streams. +# A client transmits packets +# A server receives packets +# A client receives packets +# A server transmits packets +For each of these cases, we have created utility classes to ease the +realization of packet-stream-based inter-component interfaces. We grouped the +first two cases (transmit channel - TX) and the latter two cases (receive +channel - RX). The corresponding utility classes reside in +'os/include/packet_stream_rx/' and 'os/include/packet_stream_tx/'. +Each of both directories contain the usual elements of an RPC interface, an +abstract interface shared between client and server (called 'packet_steam_rx.h' +or 'packet_stream_tx.h'), the client-side interface ('client.h') and the +server-side interface ('server.h'). + +By applying these new generalized utility classes to the existing 'Nic_session' +interface, we were able significantly reduce the code complexity of this +device-class interface and expect less code duplications when introducing +further device classes. + +Note that the change of the 'Nic_session' interface implies a slight API +change. The former wrapper functions such as 'rx_packet_avail()' were removed +in favor for a generic accessor to the stream interface. So this code must be +changed to 'rx()->packet_avail()' and respectively for the other wrapper +functions. + + +Audio playback sessions +----------------------- + +The audio-out interface extends Genode's device-class interfaces by a +facility for audio-stream playback. It is based on the packet-stream +framework and uses one TX stream per audio channel. Therefore, the +channel configuration is not limited by the interface and audio-out +servers are free to provide custom channel configurations to clients. +For example, standard stereo playback creates to sessions - one for +"left" and one for "right". More sophisticated applications may +request 5.1 surround sound with channels for front left/center/right +and rear left/right plus low-frequency effects (LFE). + +The actual PCM stream data uses 32-bit floating point numbers. This +implementation fosters complex audio applications with multi-channel +mixers and effect chains on all platforms supported by Genode. + + +Servers +======= + +Nitpicker +~~~~~~~~~ + +We implemented the per-client background handling. Each client is free +to select one of its views as background. If the user activates the client, +the background view gets displayed as desktop background. In the presence +of multiple window systems as nitpicker clients, each window system can +have its own fully operational desktop. Depending on the client the user +is currently interacting with, the corresponding desktop is displayed. + + +Audio-out mixer +~~~~~~~~~~~~~~~ + +Based on the new audio-out session interface, we implemented an audio +mixer, which currently supports up to 16 stereo inputs and uses one +stereo output. We also added a simple audio-test program that plays +configured raw stereo float-PCM streams. The configuration snippet +configures input to direct all 'Audio_out' sessions to the mixer +except for the mixer itself, which uses the audio driver as back end. +The complete example can be found in 'os/config/mixer': + +! +! ... +! +! +! +! +! +! +! ... +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! silence.f32 +! silence.f32 +! +! +! + +The scenario includes the audio test with two configured PCM-stream +files. + + +Device drivers +############## + +Audio out +========= + +With the introduction of the audio-out session interface, we +implemented drivers for certain audio back ends. Currently, each of +them provides two channels - "front left" and "front right". + +On Linux, the 'audio_out_drv' uses the ALSA libraries and opens the +ALSA device 'hw'. Therefore, it needs exclusive access to the sound +hardware and other media applications should be closed. For real +hardware support, we ported the following drivers in DDE Linux 2.6: + +* Ensoniq ES1370 (ens1370.c) +* Intel earlier ICH and before (intel8x0.c) +* Intel HD Audio + +If you use the Qemu emulator to run Genode, activate the sound +hardware with the '-soundhw all' command line switch. Please note that +the default sound back end may stutter on some systems. In this case, +you may try other back ends by configuring the 'QEMU_AUDIO_DRV' +environment variable (see 'qemu -audio-help' for more information). +The following worked best on our systems: + +! QEMU_AUDIO_DRV=oss qemu -soundhw all -cdrom genode.iso + +Timer +===== + +We improved the accuracy of the Linux-specific and PIT-based timer +implementations. On Linux, the timer relies on absolute time provided by the +'gettimeofday' system call rather than accumulated sleep times. For the PIT +timer driver, we removed the caching of the current time and instead read the +PIT counter register directly as needed. + +Input +===== + +Added input driver for PL050 PS/2 mouse and keyboard as found on the +VersatilePB platform. + + +Protocol stacks and libraries +############################# + +zlib and libpng +=============== + +Since the first Genode release, the 'demo' repository contains ports of +'zlib' and 'libpng' to enable the Scout tutorial browser to display PNG +images. These libraries were meant to be used with the 'mini_libc' that +is also part of the 'demo' repository. For other use cases than Scout, +this port is incomplete. To provide a fully-fledged zlib and libpng to +Qt4, we renamed the old library ports to 'libz_static' and 'libpng_static', +and added fresh ports of the current zlib-1.2.5 and libpng-1.4.1 to the +'libports' repository. In contrast to the old libraries, the new versions +are built as shared objects. + + +libSDL +====== + +We integrated Stefan Kalkowski's original port of libSDL-1.2.13 into the +libports repository. Currently, it comes with back ends for SDL video +using Genode's 'Framebuffer' interface and SDL events using Genode's 'Input' +interface. + + +Qt4 +=== + +* Enabled support for handling JPEG, GIF, and PNG files. Qt4 does no + longer depends on the 'libz' and 'libpng' libraries of the 'demo' + depository. It uses shared libraries provided by the 'libports' + repository instead. +* Qt4 threads are now named as "Qt " +* Let Qt4 use the standard C++ library that comes with the GCC tool chain. + This change removed the need for custom 'new' and 'delete' operators. +* The timeout handling is now accomplished using the new 'select' + implementation of the C library. +* Added support for more mouse buttons than only the left one +* Avoid unsupported timezone conversions +* Some Qt4 applications use to rely on local pipes for internal thread + synchronization. Such pipes do not carry payload but are used only as + a mechanism to block and wake up threads. To support such applications, + we added a libc plugin with a simplified implementation of 'pipe()', + which is essentially a lock. The libc plugin is called 'qt_libc_pipe' + and comes as a shared library with the 'qt4' repository. +* Enabled networking support by reverting to the original event dispatcher + implementation using the new 'select()' and 'pipe()' functions of the + C library +* Added 'Thread_qt::stack_base()' function to return the stack base of a + thread as this function is required by Webkit's Javascript engine. +* We moved the Dejavu-Sans font to a library because we use to link this + TTF font as a static resource to various Qt4 applications. By placing + it into a library, we avoid duplicating the font file in the source + tree. + + +lwIP stack +========== + +* The lwIP-specific timed semaphore implementation and the corresponding alarm + thread have been replaced by the new generic timed semaphore provided by + the 'os' repository. + +* We improved the integration of lwIP with the C library. The 'libc_lwip' + plugin supports the new 'select' implementation. To improve the convenience + of configuring lwIP, we added two helper libraries 'libc_lwip_loopback' and + 'libc_lwip_nic_dhcp'. If either of both libraries is linked against a + lwIP-using target, the IP stack gets configured to use a loopback device or + a NIC adaptor with a dynamically acquired IP address. This facilitates the + reuse of existing BSD-socket-based networking code on Genode without + modifications. For an example, please see 'libports/src/test/lwip/loopback'. + The 'http_srv' example is still using lwIP directly without relying on the + 'libc_lwip' plugin. + + +X event tracker +=============== + +We refined our window event tracker used for the seamless integration of the +X Window System with the nitpicker GUI. (either on Linux using Xvfb, or with +OKLinux using our Genode-FB stub driver) We improved the accuracy of window +stack operations and reduced pixel artifacts regarding the mouse cursor. The +latter problem, however, is not yet completely solved. Unfortunately, the X +mouse cursor is not captured by the X damage extension used to track screen +updates. Therefore, we need to employ heuristics, which have still room for +improvement. + + +DDE Kit +======= + +We added support for handling sub-page-size I/O memory regions. To hand out +different I/O resources that reside on the same page to different processes, +we changed the I/O memory allocator in core to use byte granularity. +A page is handed out if the requested sub range is still available. +Consequently, one and the same I/O memory page may be mapped to multiple +drivers, each meant to access a portion of the page. At the DDE Kit API +level, small I/O regions are handled completely transparent to the user +of the API. + + +GUI and sound for paravirtualized Linux +####################################### + +With the current release, we brought forward the integration of OKLinux with +native Genode components. This integration is achieved by so-called stub +drivers --- Linux device drivers that use Genode's services instead of hardware +devices. Our original port of OKLinux to Genode came with stub drivers for +virtual timer, user input, and framebuffer devices using Genode's timer-session, +input-session, and framebuffer-session interfaces. We have now complemented +our stub driver with drivers for the seamless integration of the X Window System +with the nitpicker GUI and sound. The Genode stub drivers are unconditionally +compiled into the OKLinux kernel. There is no need to enable them in the Linux +kernel configuration. + +GUI +=== + +The seamless integration of the X Window System running on OKLinux with the +natively running nitpicker GUI is achieved by an enhanced Linux framebuffer +device. The number of virtual framebuffer devices can be declared in the +Genode configuration of the OKLinux kernel as follows: +! +! +! +! +! +! +The order in the 'screens' section determine the order of visible devices in +Linux. A '' entry declares a regular 'fb' device corresponding +to the 'Framebuffer' session. A '' entry declares an enhanced 'fb' +device that supports the propagation with window-stacking events (via 'ioctl'), +which are needed for the seamless integration of the X Window System. The first +device is typically used as boot console. For this reason, it should be a +regular ''. To run the X Window System in seamless mode using the +configuration above, the X server must be configured to use the '/dev/fb1' +device and the X session must start the X event tracker application, which +feeds window-state transitions to the enhanced '/dev/fb1' device. The X event +tracker is a plain Linux program located at 'oklinux/src/app/xev_track'. Note +that this program must be build for a Linux host platformm using a separate +build directory. This build directory must use the 'base-host' repository and +extend the 'SPECS' variable with 'x11', 'xtest', and 'xdamage'. + +[image img/red_green_screenshot] + +The screenshot shows two Linux instances and the native launchpad application +seamlessly integrated into a single GUI. We slightly modified nitpicker to tint +each client using a different color when activating the X-Ray mode. Even though +multiple window systems are running in parallel, the tinting and nitpicker's +floating labels reveal the information about which part of the screen belongs +to which protection domain. + + +Applications +############ + +In the line of the 'libports' repository, we added a new 'ports' repository. +Whereas 'libports' is meant as a place for 3rd-party libraries, 'ports' is the +designated place for 3rd-party applications. The mechanism for downloading +and preparing upstream source-code distributions is exactly the same. +The first application is the Qt4-based Arora web browser. + +Arora web browser +================= + +Arora has its origin as an example application for Qt4. However, it emancipated +itself to be a separate project. + +:[http://arora.googlecode.com]: Arora project website + +Even though compared with other browsers, its popularity is relatively small +but for us, it is perfect to stretch the bounds of our Genode infrastructure. +The following screenshot shows Arora running as native Genode process. + +[image img/arora_screenshot] + +Porting Arora to Genode motivated many improvements of our C library, the Qt4 +port, and the lwIP stack. In the current state, the application is fully +functional and can be used to browse complex websites. However, our port is +still a proof of concept and not fully stable. As one interesting detail, Arora +on Genode is directly linked against the lwIP stack, which obtains an IP +address via DHCP. + +To download the Arora source code, issue 'make prepare' from the 'ports' +repository. For building the application, make sure to have also prepared the +'qt4' and 'libports' repositories and specified those repositories in +your '/etc/build.conf' file. From within your build directory, you +can then issue 'make app/arora'. + +For a first test drive, we recommend to use the Linux version of Genode. +On Linux, we can use Genode's TUN/TAP device driver located at +'os/src/drivers/nic/linux'. This driver uses the 'tap0' device to send and +receive raw Ethernet packets. To create such a device, issue the following +command (use 'sudo'): +! tunctl -b -u +This command should return the name of the TAP device. If no other +TAP devices were created prior executing 'tunctl', this should be 'tap0'. +You can activate the device via: +! ifconfig tap0 up +To associate 'tap0' with your Ethernet device connected to the internet, +you will further need a bridge device: +! brctl addbr br0 +This command creates a new bridge device called br0. Let's now associate +the bridge with both, your 'eth0' device (connected to the internet) and +the 'tap0' device: +! brctl addif br0 eth0 +! brctl addif br0 tap0 +Now you can activate the bridge and try to obtain a fresh IP address. +! ifconfig br0 up +! dhclient br0 + +After following these steps, the 'os/src/drivers/nic/linux' driver should +be functional and you can use the following init configuration (using the +traditional version of init) for starting Arora on Linux: +! +! +! fb_sdl +! 2M +! +! +! timer +! 0x20000 +! +! +! nitpicker +! 1M +! +! +! nic_drv +! 1M +! +! +! arora +! 2G +! +! + +Note that the default memory pool used by Genode on Linux might be too small +for running Arora. To increase the amount of usable memory, change the +'_some_mem' declaration in 'base-linux/src/core/platform.cc'. + + +Platform-specific changes +######################### + +:Codezero: + +* Adapted Genode to the development branch of Codezero version 0.3 and the + 'gcc-4.4' tool chain + +* Implemented the Genode lock using Codezero's kernel mutex as block/unblock + mechanism. This way, the lock supports the full semantics of the lock + API including lock cancellation. The design is the same as for the lock on + NOVA. There is one kernel mutex per thread, which is part of the thread + context. A tread can block itself by trying to acquire the mutex twice. + Another thread can then wake up the thread by releasing the mutex. + +* Added a new mechanism for ROM modules via a separate ROM ELF image. The + new mechanism relies on the new 'gen_romfs' tool at 'base-codezero/tool'. + For changing the set of boot modules, core must no longer be modified + or recompiled. + +* Implemented core's IRQ service for Codezero + + +:NOVA: + +To use NOVA on recent hardware, which often lacks RS232 ports, a serial PCI +card can be used for debugging. Because such a card is not initialized by the +BIOS, we added proper serial initialization code to NOVA core console. + + +Build system +############ + +New two-stage build system +========================== + +Since the thorough +[http://www.genode-labs.com/publications/scons-vs-make-2008.pdf - analysis of the Genode build system] +by Ludwig Hähne in 2008, we were planning to incorporate his findings into +Genode. In his study, he reimplemented Genode's make-based build system using +SCons and compared both implementations. For us, the two most interesting +conclusions were that recursive make is evil and that SCons scales worse than +make with regard to memory usage. + +The statement about recursive make was not entirely new to us but because we +were not aware of a good alternative, our build system relies on this scheme +for handling inter-library dependencies. This becomes troublesome if enabling +parallel builds using '-j'. If two libraries A and B depend on the same +library C, both A and B can be build concurrently, and both will issue the +build process for library C. However, if one and the same library C is build +twice in parallel, race conditions happen. For this reason, we explicitly +disabled parallel execution of make rules using GNU make's '.NOTPARALLEL' +feature. But this severe limits the scalability of the build system. + +But according to the findings of the study, 'SCons' seemed to be no viable +alternative for other reasons, most importantly its memory foot print when +dealing with large source trees. SCons uses to create a complete dependency +graph by reading all SConscript files of the source tree. For building the +complete tree, this is of course needed but most of the time, only little parts +of the tree are of interest. In contrast, the Genode build system processes +only those build-description files that are required for the current build, +resulting in a constant memory usage regardless of the size of the source tree. + +Finally, the study evidenced that flat make outperformed both SCons and +recursive make in terms of performance, scalability regarding parallelism, +and memory footprint. + +Consequently, we sticked with our make-based solution but were seeking for +a way to avoid recursive make. We have now realized a solution that replaces +our original single-pass recursive make by a two-stage approach. + +At the first stage, a library-dependency graph is generated by using recursive +make with no parallelism. Starting from the set of targets specified at the +top-level make instance, all library-description files required by those +targets are traversed in a recursive manner. So libraries can depend on further +libraries. During this process, a dependency graph for the particular set of +targets is generated. It is represented as a makefile called +'/var/libdeps'. + +The second stage is driven by the generated '/var/libdeps' file, +taking full advantage of parallelism. Because all inter-library dependencies +are now specified at one flat makefile, race conditions cannot occur and the +build performance effectively corresponds to a flat make. For building +each single leaf of the dependency graph (either a library or a target), +a sub make is used, which encapsulates the leaf-specific build environment +such as custom compiler flags or defines. + +Following the lines of the original build system, both stages visit only +those build-description files that are required for the current set of +targets. If this set is large, the first stage causes a clearly visible delay +prior the actual build, which was formerly obscured in the call sequence of +recursive make instances. The work flow of developing interdependent components +such as an application, a protocol stack, and a device driver often comprehends +a build command that is repetitively used. For example, +! make drivers server app/stresstest +To avoid the costs for regenerating the same dependency graph again and again, +the first stage can be skipped by using: +! make again +This way, the current version of 'var/libdeps' is reused for the second +stage. In practice, this shortcut provides a welcome performance boost for such +work flows. + +From the developer's point of view, the new build system is fully compatible +with the old one but faster. No build-description files need to be changed to +take advantage of it. We slightly changed the build output because the grouping +of build steps to targets is no longer useful when building in parallel. The +best way for directing the build system to work in parallel is specifying a +'-j' argument in your '/etc/build.conf' file, for example +! 'MAKE += -j 4' + +Further improvements are a much faster clean rule, an improved handling of +defect or missing libraries, and the detection of ambiguous target names. + + +Improved interplay between shared objects and static libraries +============================================================== + +We reworked the interplay between static libraries and shared objects to cover +cases where static libraries are linked against shared objects. Because this is +possible, all libraries are now compiled as position-independent code (using +the '-fPIC' compiler option) unless specified otherwise. This behaviour can be +disabled per library by adding 'CC_OPT_PIC =' to its library-description file. + diff --git a/doc/release_notes-10-08.txt b/doc/release_notes-10-08.txt new file mode 100644 index 000000000..54aea4954 --- /dev/null +++ b/doc/release_notes-10-08.txt @@ -0,0 +1,618 @@ + + + =============================================== + Release notes for the Genode OS Framework 10.08 + =============================================== + + Genode Labs + + + +The Genode project is back with the feature-packed release 10.08, set out to +bring device support of the Genode OS Framework to the next level. Our road +map hinted at two particular spots of activity, introducing wireless networking +and enabling hardware-accelerated graphics. To pursue the first goal, we pushed +the boundaries of our Linux device driver environment by porting Madwifi to +Genode. When it comes to hardware-accelerated graphics, today the Gallium3D +protocol stack and the corresponding GEM GPU drivers are the state of the art +on Linux and other UNIX-like operating systems. With the current release, we +make this powerful graphics architecture available on Genode, including +support for hardware-accelerated 3D graphics on Intel GMA GPUs. But we haven't +stopped with our device-driver related activities here as we introduce a new +ATAPI driver accommodated with an ISO9660 file-system implementation, and we +largely revisited our existing driver base. + +Apart from device-driver support, two major features of the release are the +upgrade of the Qt4 framework from version 4.5.2 to version 4.6.3 alongside with +many performance and stability improvements, and the added support for dynamic +linking on ARM platforms with both the OKL4 and Codezero kernels. + + +Gallium3D and Intel's Graphics Execution Manager +################################################ + +Gallium3D is the most modern and most actively developed open-source software +stack regarding hardware-accelerated graphics. It is mainly deployed on +Linux but it has been ported to other operating systems such as the BSD family, +OpenSolaris, AROS, and now Genode. In the following, we will first provide +a little background about Gallium3D followed by a rough overview on how its +components fit into Genode. + +Gallium3D was designed to displace the long-evolved but inherently insecure +direct rendering infrastructure on Linux. With DRI, each application that uses +hardware-accelerated graphics has unlimited access to the GPU and its resources +such as the frame buffer. To allow multiple applications to use the GPU in a +time-shared manner, those applications have to behave cooperatively. There is a +central service, the DRM driver in the kernel, orchestrating the applications +in such a way that well-behaved applications use distinct GPU resources such as memory +and contexts, and so come along nicely. However, there are no effective measures +against misbehaving applications. A further consequence of this architecture +is the multitude of vendor-specific protocol implementations. Because each +application contains an instance of the GPU driver accessing the broad hardware +interface of the graphics device, each vendor happened to take a different route +for translating graphics APIs such as OpenGL to the actual device interface. +Consequently, the code basis for graphics protocol stacks has become extremely +complex and fragmented. In contrast, the designers of Gallium3D set out to +modularize the protocol stack such that generic code is easy to use by different +vendors and the vendor-specific portion of the protocol stack stays as small as +possible, thereby lowering the costs for new-device support in the future. + +In contrast to DRI, a Gallium-based application does not operate directly on +the GPU device. Instead, it uses a higher-level abstraction, namely buffer +objects for holding data operated on by the GPU. Buffer objects can contain +pixels, geometry data, and GPU command streams. The latter type of buffer +object can be issued for execution to perform actual GPU operations. The +buffer-object interface is provided by a central service called graphics +execution manager (GEM) normally residing in the kernel. GEM arbitrates the +allocation of buffer objects, manages cache coherency between GPU and CPU, and +passes buffers objects containing GPU command streams scheduled for execution to the +graphics device. + +The high-complexity Gallium3D protocol stack is instantiated for each +application and acts as a client of the (relatively) low-complexity GEM. +Provided that the GPU command stream assembled by a Gallium3D application +cannot subvert the operation of GEM, this architecture removes the complex +protocol stack from the trusted computing base. Only the low-complexity GEM +service must be trusted with regard to security, robustness, and the absence +of GPU-based inter-application crosstalk. In contrast to DRI, Gallium3D is +perfectly in line with the architecturally principles of Genode. On Linux, the +Gallium3D stack communicates with GEM via 'ioctl' operations on the '/dev/drm/' +device interface. In the context of Genode, GEM should be executed as a +user-level device driver and resource multiplexer providing the GEM operations +as a GPU session interface by the means of RPC and shared memory. Each +Gallium3D application connects to the GPU server and operates on a GPU session. + + +The puzzle pieces of Mesa/Gallium3D +=================================== + +Gallium3D is part of the Mesa OpenGL library. It comes as a set of modules +consisting of state trackers (API implementations such as OpenGL), +drivers (translating generic graphics commands into device-specific command +streams), winsys (the glue between a window system and the Gallium3D +application), and a large library of utilities. Most of the code is +independent from the actual GPU device as well as the operating-system. + +On Genode, Mesa-7.8.1 has been incorporated into the 'libports' repository. +However, we only use the Gallium3D-related parts of Mesa on Genode. For +example, the port does not make use of the Mesa swrast facility for +software-based rendering. It rather relies on the Gallium3D softpipe driver. +When built, all generic Gallium3D-related parts of Mesa are linked into the +single 'gallium.lib.so' library. + +The following figure gives an overview of how the components of the graphics +software stack relate to each other. The components are described in the +following. + +[image img/gallium3d] + + +EGL driver +~~~~~~~~~~ + +EGL is an OS-agnostic API for managing rendering buffers. The implementation +of this API is OS-specific because, among other things, it cares about the +interaction of the application with the native windowing system such that the +result of GPU-based rendering can be displayed in the GUI. EGL also plays a special +role among the Gallium state trackers because it is used by other state trackers. +The window-system-specific code is called EGL driver. Mesa-7.8.1 comes with +EGL drivers for the X window system and the Linux kernel-mode-switching +interface. For Genode, we have added a new EGL driver that uses Genode's +'Framebuffer_session' interface as back end. It is located at +'libports/src/lib/egl'. Because the EGL driver is GPU-independent, it is +part of 'gallium.lib.so'. The following screenshot shows the EGL driver +in action. + +[image img/gallium_softpipe_screen] + + +Gallium i915 GPU driver +~~~~~~~~~~~~~~~~~~~~~~~ + +We picked Intel's GMA as the first series of supported GPUs because they +are well documented and GEM has been implemented first for Intel GPUs. +There are two Gallium3D drivers for Intel GPUs called i915 and i965. +Currently, we have ported the i915 driver that supports the following GPU +models: i915 G/GM, i945 G/GM/GME, G33G, Q33G, and Q35G. On Genode, the Gallium3D +GPU driver comes in the form of a shared library called 'gallium-i915.lib.so'. +Is gets dynamically loaded by the EGL driver using 'dlopen'. If the EGL +driver fails to load the 'gallium-i915.lib.so' driver, it falls back +to the softpipe driver compiled into 'gallium.lib.so'. + +Because there is no build dependency to this shared library, we created a +pseudo build target at 'libports/src/lib/gallium/i915' for the sole purpose of +building this library as a side effect. + +On Linux, the code contained in 'gallium-i915.lib.so' communicates with the '/dev/drm/' +device interface. However, the interaction with the device is not performed +directly but via a library called 'libdrm'. + + +libdrm +~~~~~~ + +The DRM library is a convenient front end to the '/dev/drm/' device. At +first glance, replacing this library with a Genode-specific implementation +seems natural. However, because 'libdrm' is not only a mere wrapper around the 'ioctl' +interface of the device but also contains a substantial amount of program logic +and device heuristics, we decided to reuse this library unmodified and go for +the 'ioctl' interface as a hook to connect Gallium3D with GEM. Hence, 'libdrm' +has become part of the 'libports' repository alongside Mesa. Ultimately, +'libdrm' translates all requests coming from 'gallium-i915.lib.so' to +(GPU-specific) 'ioctl' and 'mmap' operations on the '/dev/drm/' device. On +Genode, there is no such device. In fact, there are no device nodes at all. +Instead, we use our libc plugin mechanism to redirect operations on this +specific device file to a dedicated libc plugin. When 'libdrm' opens '/dev/drm/', the +libc plugin located at 'src/lib/libdrm/' takes over the responsibility of the +returned file descriptor. Therefore all file operations on this file handle are +handed over to the plugin. This is the point where we can transparently +incorporate RPC communication to the GEM service. For now, however, we do not +have RPC stub code yet. Instead, we link the GEM code directly into +'gallium-i915.lib.so'. This allows us to implement the GEM-related 'ioctl' +operations by calling GEM code directly. For the interaction between +'libdrm' and GEM, we added a preliminary 'Gpu_driver' interface located at +'os/include/gpu/'. + + +Graphics execution manager +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GEM is normally part of the Linux kernel and dispatches (a subset of) +operations on '/dev/drm/'. We use the Intel-specific GEM code taken from +Linux-2.6.34. This code relies on the Intel-AGP subsystem to manage the GPU's +graphics translation table. Therefore, we had to port the Intel-AGP sub system +as well. Because GEM is a relatively new feature of the Linux kernel, we +decided to not use our Linux device driver environment (currently based on the +kernel version 2.6.20) for our porting work but rather went for the creation +of a driver-specific Linux emulation environment. The code is part of the +'linux_drivers' repository and located at 'src/drivers/gpu/'. The 'contrib/' +subdirectory contains unmodified Linux code, the 'i915' subdirectory contains +the implementation of the 'Gpu_driver' interface and code for emulating Linux +interfaces in a way that the 'contrib/' code behaves like in its natural +execution environment. + + +Building an OpenGL application +============================== + +As an example on how to build an OpenGL application on Genode, the 'libports' +repository provides a slightly modified version of the famous Gears demo. +You can find the code under 'libports/app/eglgears/'. + +Prior building the application, make sure that you have issued 'make +prepare' for the 'mesa' and 'libdrm' libraries. In the root of the 'libports' +repository, issue the following commands to download the upstream source +codes for these libraries and to integrate them with the Genode build system: + +! make prepare PKG=mesa +! make prepare PKG=libdrm + +After having added 'libc' and 'libports' to the 'REPOSITORIES' declaration +of your '/etc/build.conf', you can building 'eglgears' via +'make app/eglgears'. The build process will create the 'eglgears' executable +alongside with 'gallium.lib.so'. You can start 'eglgears' using a plain +framebuffer such as 'vesa_drv'. The EGL driver included in 'gallium.lib.so' +will try to load a shared library called 'gallium-i915.lib.so' and, if not +present, revert to the softpipe driver. + +If you want to give the hardware-accelerated version a spin, you will +need to build 'gallium-i915.lib.so'. The driver is only built when +the build 'SPECS' variable contains the keyword 'i915'. Simply add the +following line to your '/etc/specs.conf' file: + +! SPECS += i915 + +Currently, the GEM (contained in the 'linux_drivers' repository') is +linked to 'gallium-i915.lib.so'. Hence, the 'linux_drivers' repository +must be specified in your 'build.conf'. The Gallium driver is built +as a side effect of building a pseudo target via: + +! make lib/gallium + +If you add the resulting 'gallium-i915.lib.so' to core's ROM service, +the EGL driver will attempt to use the hardware driver. + + +Current limitations +=================== + +At the current stage, Gallium3D on Genode is able to run the Gears +demo using Intel GMA GPUs. However, the work done so far must be +regarded as the first of several steps towards a complete solution. +Let us highlight the most important limitations and construction +sites: + +* Both GEM and the Gallium3D protocol stack are executed as part of + a single process, accessing the GPU exclusively. Until we have + separated GEM from Gallium3D, only a single GPU-using application + can run at a time. +* Even though a Gallium3D application is able to use the GPU for + 3D rendering, the EGL driver relies on CPU-based blitting + in order to transfer the rendering result to the screen. +* Interrupt-based synchronization has not been implemented yet. The + GPU is expected to be faster than the CPU. For this reason, + the EGL driver waits for 5 ms after each rendered frame. + Proper vblank handling is desired. +* On some platforms, we observed pixel artifacts, which + we attribute to cache coherency issues. +* Resource deallocation within the GEM driver has not been implemented + yet. Therefore, we expect that the current version is not suited for + highly dynamic applications. +* The 'eglgears' demo runs fine with resolutions up to 800x600 + but we observed unstable behaviour with higher resolutions. + +Despite of these limitations, the first version of Gallium3D on +Genode showcases that a subsystem as comprehensive as Gallium3D +plus GEM can be natively executed on Genode. + + +Operating-system services and libraries +####################################### + +Init configuration concept +========================== + +The previous release 10.05 introduced a new configuration concept that enables +the specification of mandatory access-control rules among flexible ways to +route service requests throughout the system. With the current release, this +concept is used by default. We have adapted all example configurations to the +new format, polished the new init implementation, and moved it from +'os/src/init/experimental/' to 'os/src/init/'. The old init variant is still +available at 'os/src/init/traditional/' but it is scheduled to be removed with +the next release 10.11. + +The description of the configuration concept and the XML format is provided by +the document "Configuring the init process of Genode" located at +'os/doc/init.txt'. + + +Block session interface +======================= + +The block session interface extends Genode's range of device-class interfaces by +a general-purpose interface to access block devices. It is based on the +packet-stream framework, using a single TX-stream to transmit block requests +to the server-side. Clients are free to request read/write operations on +several sequent blocks at once. Services implementing the server-side of the +block-session interface can choose an appropriate block size and the kind of +block-operation they support (e.g., read-only device). The new block session +interface is located at 'os/include/block_session/'. + + +ROM-loop block device +===================== + +Based on the new block session interface, we implemented a service, which +provides a rom-file as read-only block-device. Linux users might know this +kind of service under the term "loop device". The following configuration +snippet shows how the file provided by a rom-session can be used as block +device: + +! +! ... +! +! +! +! +! livecd.iso +! +! + + +C runtime enhancements +====================== + +Both 'libc' and 'libm' are now built as *shared objects*, reducing the memory +footprint for scenarios with multiple libc-using applications. When starting +programs that use the libc, make sure to have 'libc.lib.so' and 'libm.lib.so' +available as files at the ROM service. + +Motivated by our work on Gallium3D and libdrm, we extended the libc *plugin* +*interface* with the support of 'ioctl' and 'mmap'. This change enables us to +install custom handlers of those libc calls for a specific file. In the +particular case, libdrm performs 'ioctl' and 'mmap' calls referring to the +'/dev/drm' device interface. Now, we can supply a libc plugin specific for a +single file. + +The update of Qt4 from version 4.5.2 to version 4.6.3 required refinements +of 'clock_gettime()', 'sysctl()', and 'getpagesize()'. Those functions +are still dummy stubs but with a meaningful behaviour from Qt4's perspective. + + +DDE Kit +======= + +During our various device-driver activities, we improved the DDE Kit support +library for device-driver developments. The revised handling of I/O memory +resources now allows multiple requests of the same resource to support, e.g., +multiple Linux 'ioremap()' calls. The I/O memory-mapping type is configurable +as uncached or write-combined, and DDE Kit automatically keeps track of +virtual-to-physical address mappings. Also, DDE Kit now provides 64-bit integer +types and a proper 'size_t'. + + +Dynamic linker +============== + +In order to support shared libraries on ARM platforms, we added EABI support to +the dynamic linker and Genode's build-system environment. Thus shared libraries +are now supported on Codezero and OKL4 GTA01 targets. This also includes C++ +exception handling. + +Additionally, we implemented libc's dynamic linking interface ('dlfcn.h') and are +now able to support dynamic loading of libraries by applications via 'dlopen' +and friends. + + +Device drivers +############## + +New ATAPI driver +================ + +With version 10.08, Genode provides a port of the low level ATA/ATPI driver +available from [http://ata-atapi.com]. Currently, the driver supports ATAPI +devices only and is implemented as a block-interface server (see Section +[Block device interface]). By default, the driver tries to take advantage of +the device's DMA engine but it can also operate in PIO mode as a fall-back +solution. + + +New wireless networking driver +============================== + +According to our roadmap, we introduce initial support for wireless networking. +Based on DDE Linux 2.6, a port of the madwifi driver (version 0.9.4) is now +available for Genode. This driver supports widely used wifi-cards containing an +Atheros chipset (namely: 5210, 5211, 5212). + +Due to the fact that part of the madwifi-project's contribution is binary code +in uuencoded form containing mandatory copyright headers, you need a 'uudecode' +binary installed on your system if you like to compile the madwifi driver for +Genode. If you're using Ubuntu/Debian or one of its derivates as your +development environment, you might install the necessary application via: + +! sudo aptitude install sharutils +! emerge sharutils (on Gentoo) + +This first wireless networking driver is in experimental stage and doesn't +support any form of encryption and authentication on the ieee80211 layer. So +you can only use it in conjunction with an unprotected access-point. + +To set an appropriate ESSID you can tweak the driver's configuration, like in +the following example: + +! +! ... +! +! +! +! +! My_access_point +! +! + +When started, the 'madwifi_drv' announces a "Nic" session usable by the +lwIP stack. + + +PCI driver +========== + +We enhanced the PCI bus scanning facility of our PCI driver with regard to +multi-function devices and added an accessor function for the physical +bus-device-function (BDF) ID. + + +VESA driver +=========== + +To support a wider range of graphics cards, we revised the VESA driver and +support more peculiarities of VBE implementations. Some of these are: unaligned +I/O port accesses, dependency on the physical BDF of PCI devices, support for +all flavours of PCI configuration space accesses. + + +PS/2 input driver +================= + +Even after long years of intensive use, the PS/2 driver is sometimes good for a +surprise, which prompted us to improve the keyboard scan code and mouse button +handling. The driver fully supports scan code set 1 and 2 keyboards and copes +with oddities like "fake shift" events and "yet another scan code for Pause". + + +Timer +===== + +The current release corrects a shortcoming of our timer driver on Pistachio and +Fiasco. Timing on these platforms is now more accurate. + + +Paravirtualized Linux +##################### + +Based on the new block-session interface, we implemented a new stub driver +for OKLinux that enables the usage of a block-session device within Linux. +Thereby, the old stub driver that provided a ROM file as block device +is no longer needed and will be removed with the next release. A ROM file +can now be supplied to Linux via the new ROM loop service. + + +Protocol stacks and libraries +############################# + +lwIP +==== + +Tweaking the configuration of the lightweight IP stack to better fit Genode's +application needs lead to a considerable improvement with respect to network +performance. + + +ISO9660 file system +=================== + +ISO9660 is the standard file system used on data CD/DVD medias. With the ATAPI +driver ready, we implemented ISO9660 support on top of the this driver. The +'iso9660' server implements the ROM-session interface and can be used by any +ROM connection. In order to take advantage of this new feature, we exploit +Genode's new configuration concept and route the client's ROM service +request to the ISO9660 server. + +Configuration file snippet: + +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! + +:Limitations: +The memory necessary to read a file from the ATAPI driver into memory is +currently accounted on behalf of the ISO9660 server, not for the client side. +Because of this limitation, it becomes necessary to equip the ISO server with +a sufficient memory quota. + +The 'Ecma-119' standard requires support for 8.3 upper-case file names only. +Because of this limitation, a number of unapproved ISO 9660 extensions have +evolved (eg. Joliet, Rock Ridge). Since we don't see using 8.3 file names within +Genode as an option, we added Rock Ridge extension support to the ISO 9660 +server. Please make sure that your ISO-creation tool supports the Rock Ridge +extension and enable it during ISO creation. + + +Qt4.6.3 +======= + +We updated our port of the Qt4 framework from version 4.5.2 to version 4.6.3. +Thereby, we changed the way of how the source code is organized. Previously, we +maintained copies of modified files within the 'qt4' repository. Now, we +keep those changes in the form of patches, which get applied to the 'contrib' +code when 'make prepare' is issued within the 'qt4' repository. This change +significantly reduces the size of the 'qt4' repository. We applied the same +approach to the port of the Arora browser. Furthermore, the performance and +stability of Qt4 and Webkit in particular have received a lot of attention, +resulting in a much improved Arora browsing experience. + + +Platform-specific changes +######################### + +OKL4 +==== + +With the current release, we have started to maintain a few patches of the +official version of the OKL4v2 kernel. The patches are located at +'base-okl4/patches' and have the following purpose: + +:'syscall_pic.patch': + + The original distribution of the OKL4 kernel comes with x86 syscall bindings + that use absolute addressing modes. Therefore, code using L4 syscalls + cannot be compiled as position-independent code (gcc option '-fPIC'). + Unfortunately, shared libraries must be compiled as position independent + because the location of such a library's text segment is not known at + compile time. Consequently, OKL4 syscalls cannot be issued by shared + libraries, which is a severe limitation. The patch fixes the problem + by changing all OKL4 syscall bindings and removing PIC-incompatible + addressing modes. It does not affect the functionality of the kernel. + +:'eabi_build.patch': + + The build system of the orignal OKL4 distribution is not prepared to + compile ARM EABI binaries as generated by modern tool chains such as the + Codesourcery GCC. The patch applies the needed changes to the OKL4 build + infrastructure. + + +Pistachio +========= + +Similar to the situation with the OKL4 kernel, we need to patch the Pistachio +system-call bindings to enable syscalls from shared libraries. The +corresponding patch is located at 'base-pistachio/patches' and is known to work +with Pistachio revision 'r782:57124b75c67c'. Without applying this patch, the +linker generates text relocation infos, which result in a run-time error of the +'ldso' on the attempt to modify the read-only text segment of a shared library. + + +Codezero +======== + +Because we enhanced our dynamic linker to support ARM EABI, shared libraries +are now fully usable with the Codezero kernel. + + +Tools and build system +###################### + +Unified tool chain for building ARM targets +=========================================== + +With the previous versions of Genode, +we took the approach to use the tool chains of the respective kernel +to build Genode targets. For example, we used to rely on NICTA's ARM cross compiler +(based on gcc-3.4) for building Genode for the OKL4/gta01 platform. +Genode on Codezero, however, used the Codesourcery tool chain. We identified this approach as +a dead end because we would need to support modern tool chains alongside +ancient tool chains that are no longer used in practice. For accommodating +the latter, we had to introduce special workarounds and make compromises. + +Therefore, we changed Genode to officially support one modern reference +tool chain to build all ARM-specific targets both on OKL4 and Codezero. +Currently, we use the Codesourcery tool chain version 2009q3-67, which +is available here: + +:Codesourcery ARM EABI tool chain: + [http://www.codesourcery.com/sgpp/lite/arm/portal/release1039] + +Because the original OKL4v2 distribution does not support modern ARM EABI tool +chains, it cannot be used out of the box anymore. But you can find a patch +to enable ARM EABI for OKL4v2 at 'base-okl4/patches/'. + + +Build system +============ + +* We changed the build system to link all shared libraries with + the '--whole-archive' option. + +* All libraries are now built as position-independent code (compiler + option '-fPIC') by default. It is possible to explicitly disable + '-fPIC' by adding 'CC_OPT_PIC=' to the library description file. + +* To ease the integration of third-party code into the Genode build + system, we have added a mechanism for setting source-file-specific + compiler options. Compiler arguments for a single file such as + 'main.cc' can be assigned by setting the build variable 'CC_OPT_main'. diff --git a/doc/release_notes-10-11.txt b/doc/release_notes-10-11.txt new file mode 100644 index 000000000..97c099a7c --- /dev/null +++ b/doc/release_notes-10-11.txt @@ -0,0 +1,871 @@ + + + =============================================== + Release notes for the Genode OS Framework 10.11 + =============================================== + + Genode Labs + + + +During the past three months, the Genode project was primarily driven by our +desire to create a bigger picture out of the rich set of components that we +introduced over time, in particular over the last year. Looking back at the +progress made since mid 2009, there were many functional additions to the +framework, waiting to get combined. To name a few, we added support for +networking, audio output, real-time priorities, mandatory access control, +USB, ATAPI block devices, Python, hardware-accelerated 3D graphics, Qt4, +the WebKit-based Arora browser, and the paravirtualized OKLinux kernel. +So many wonderful toys waiting to get played with. This is how the idea of +creating [http://genode.org/download/live-cds - the new Genode Live CD] was +born. In the past, Genode was mostly used in settings with a relatively static +configuration consisting of several components orchestrated to fulfill +a few special-purpose functions. Now, the time has come for the next step, +creating one dynamic setup that allows for the selection of different subsystems +at runtime rather than at boot time. + +This step is challenging in several ways. First, the processes that form +the base system have to run during the entire time of all demo setups. If +any of those processes contained stability problems or leaked memory, it would +subvert the complete system. Second, the components of all subsystems combined +are far too complex to be loaded into memory at boot time. This would not +only take too long but would consume a lot of RAM. Instead, those components +and their data had to be fetched from disk (CDROM) on demand. Third, because +multiple demo subsystems can be active at a time, low-level resources such as +networking and audio output must be multiplexed to prevent different +subsystems from interfering with each other. Finally, we had to create a +single boot and configuration concept that is able to align the needs of all +demos, yet staying manageable. + +Alongside these challenges, we came up with a lot of ideas about how Genode's +components could be composed in new creative ways. Some of these ideas such +as the browser-plugin concept and the http-based block server made it onto +the Live CD. So for producing the Live CD, we not only faced the said +technical challenges but also invested substantial development effort in new +components, which contributed to our overall goal. Two weeks ago, we released +the Live CD. This release-notes document is the story about how we got there. + +To keep ourself focused on the mission described above, we deferred the +original roadmap goal for this release, which was the creation of a Unix-like +runtime environment to enable compiling Genode on Genode. This will be the +primary goal for the next release. + + +Execution environment for gPXE drivers +###################################### + +Up to now, DDE Linux provided Genode with drivers for hardware devices +ranging from USB HID to WLAN. In preparation of the live CD, we +noticed the demand for support of a broader selection of ethernet +devices. Intel's e1000 PCI and PCIe cards seemed to mark the bottom +line of what we had to support. The major advantage of NIC drivers +from Linux is their optimization for maximum performance. This emerges +a major downside if DDE Linux comes into play: We have to provide all +the nifty interfaces used by the driver in our emulation framework. To +achieve our short-term goal of a great live CD experience, we had to +walk a different path. + +[http://gpxe.org/ - gPXE] is a lovely network boot loader / open-source +PXE ROM project and the successor of the famous Etherboot +implementation. Besides support for DNS, HTTP, iSCSI and AoE, gPXE +includes dozens of NIC drivers and applies a plain driver framework. +As we were also itching to evaluate DDE kit and the DDE approach at +large with this special _donator OS_, we went for implementing the +device-driver environment for gPXE (DDE gPXE). + +The current version provides drivers for e1000, e1000e, and pcnet +devices. The emulation framework comprises just about 600 lines of +code compared to more than 22,000 LOC reused unmodified from gPXE. +Benchmarks with the PCNet32 driver showed that DDE gPXE's performance +is comparable to DDE Linux. + +The gPXE driver environment comes in the form of the new 'dde_gpxe' +repository. For building DDE gPXE, you first need to download and patch +the original sources. The top-level makefile of this repository automates +this task. Just issue: + +! make prepare + +Now, you need to include the DDE gPXE repository into your Genode +build process. Just add the path to this directory to the +'REPOSITORIES' declaration of the 'etc/build.conf' file within your +build directory, for example + +! REPOSITORIES += $(GENODE_DIR)/dde_gpxe + +After successful build the DDE gPXE based ethernet driver is located +at 'bin/gpxe_nic_drv'. + + +On-demand paging +################ + +In the [http://genode.org/documentation/release-notes/8.11#section-8 - release 8.11], +we laid the foundation for implementing user-level dataspace managers. +But so far, the facility remained largely unused except for managing thread +contexts. This changed with this release. + +So what is a user-level dataspace manager and who needs it? In short, +Genode's memory management is based on dataspaces. A dataspace is a +container for memory. Normally, it is created via core's RAM or ROM +services. The RAM service hands out dataspaces containing contiguous +physical memory. After allocating such a RAM dataspace, the creator can +attach the dataspace to its own address space to access the dataspace +content. In addition, it can pass a dataspace reference (called dataspace +capability) to other processes, which, in turn, can than attach the same +dataspace to their local address space, thereby establishing shared memory. +Similarly, core's ROM service hands out boot-time binary data as dataspaces. + +For the most use cases of Genode so far, these two core services were the +only dataspace providers needed. However, there are use cases that require +more sophisticated memory management. For example, to implement swapping, +the content of a dataspace must be transferred to disk in a way that +is transparent to the users of the dataspace. In monolithic kernels, such +functionality is implemented in the kernel. But on a multi-server OS +such as Genode, this is no option. Implementing such a feature into +core would increase the trusted computing base of all applications +including those who do not need swapping. Core would need a hard-disk +driver, effectively subverting the Genode concept. Other examples for +advanced memory-management facilities are copy-on-write memory and +non-contiguous memory - complexity we wish to avoid at the root of the +process tree. Instead of implementing such memory management facilities +by itself, core provides a mechanism to let any process manage dataspaces. +This technique is also called user-level page-fault handling. + +For the Live CD, we decided to give Genode's user-level page-fault handling +facility a go. The incentive was accessing files stored on CDROM in an +elegant way. We wanted to make the CDROM access completely transparent to +the applications. An application should be able to use a ROM session as +if the file was stored at core's ROM service. But instead of being +provided by core, the session request would be delegated to an +alternative ROM service implementation that reads the data from disk +as needed. Some of the files stored in the CDROM are large. For example, +the disk image that we use for the Linux demo is 160MB. So reading +this file at once and keeping it in memory is not an option. Instead, only +those parts of the file should be read from disk, which are actually +needed. To uphold the illusion of dealing with plain ROM files for +the client, we need to employ on-demand-paging in the CDROM server. +Here is how it works. + +# The dataspace manager creates an empty managed dataspace. Core + already provides a tool for managing address spaces called + region manager (RM service). A RM session is an address space, + to which dataspaces can be attached. This is exactly what is + needed for a managed dataspace. So a dataspace manager uses the + same core service to define the layout of a managed dataspace + as is used to manage the address space of a process. In fact, + any RM session can be converted into a managed dataspace. + ! enum { MANAGED_DS_SIZE = 64*1024*1024 }; + ! Rm_connection rm(0, MANAGED_DS_SIZE); + This code creates a RM session with the size of 64MB. This is an empty + address space. A dataspace capability that corresponds to this address + space can then be requested via + ! Dataspace_capability ds = rm.dataspace(); + +# The dataspace capability can be passed to a client, which may + attach the dataspace to its local address space. Because the + managed dataspace is not populated by any backing store, however, + an access would trigger a page fault, halting the execution of + the client. Here, the page-fault protocol comes into play. + +# The dataspace manager registers itself for receiving a signal each time + a fault occurs: + ! Signal_receiver rec; + ! Signal_context client; + ! Signal_context_capability sig_cap = rec.manage(client); + ! rm.fault_handler(sig_cap); + When an empty part of the managed dataspace is accessed by any + process, a signal is delivered. The dataspace manager can then + retrieve the fault information (access type, fault address) and + dispatch the page fault by attaching a real dataspace at the + fault address of the managed dataspace. In a simple case, the code + looks as follows: + ! while (true) { + ! Signal signal = rec.wait_for_signal(); + ! for (int i = 0; i < signal.num(); i++) { + ! Rm_session::State state = rm.state(); + ! ds = alloc_backing_store_dataspace(PAGE_SIZE); + ! rm.attach_at(ds, state.addr & PAGE_MASK); + ! } + ! } + This simple page-fault handler would lazily allocate a page of + backing store memory each time a fault occurs. When the backing + store is attached to the managed dataspace, core will automatically + wake up the faulted client. + +# The example above has the problem that the dataspace manager has + to pay for the backing store that is indirectly used by the client. + To prevent the client from exhausting the dataspace manager's memory, + the dataspace manager may choose to use a limited pool of backing + store only. If this pool is exceeded, the dataspace manager can reuse + an already used backing-store block by first revoking it from its + current managed dataspace: + ! rm.detach(addr); + This will flush all mappings referring to the specified address + from all users of the managed dataspace. The next time, this + address region is accessed, a new signal will be delivered. + +This page-fault protocol has the following unique properties. First, +because core is used as a broker between client and dataspace manager, the +dataspace manager remains completely unaware of the identity of its client. +It does not even need to possess the communication right to the client. In +contrast, all other user-level page-fault protocols that we are aware of +require direct communication between client and dataspace manager. Second, +because dataspaces are used as first-level objects to resolve page faults, +page faults can be handed at an arbitrary granularity (of course, a multiple +of the physical page size). For example, a dataspace manager may decide to +attach backing-store dataspaces of 64K to the managed dataspace. So the +overhead produced by user-level page-fault handler can be traded for the +page-fault granularity. But most importantly, the API is the same across +all kernels that support user-level page fault handling. Thus the low-level +page-fault handling code becomes inherently portable. + +Having said that, we have completed the implementation of the described +core mechanisms, in particular the 'detach' facility, for OKL4. The ISO9660 +driver as featured on the Live CD implements the 'ROM' interface and +reads the contents of those files from CDROM on demand. It uses a +fixed pool of backing store, operates at a page-fault granularity of +64KB, and implements a simple fifo replacement strategy. + + +Base framework +############## + +There had been only a few changes to the base framework described as +follows. + +We unified the core-specific console implementation among all +base platforms and added synchronization of 'vprintf' calls. +The kernel-specific code resides now in the respective +'base-/src/base/console/core_console.h' files. + +We removed the argument-less constructor from 'Allocator_avl_tpl'. +This constructor created an allocator that uses itself for +meta-data allocation, which is the usual case when creating +local memory allocators. However, on Genode, this code is typically +used to build non-memory allocators such as address-space regions. +For these use cases, the default policy is dangerous. Hence, we +decided to remove the default policy. + +The 'printf' helper macros have been unified and simplified. The +available macros are 'PINF' for status information, 'PWRN' for warnings, +'PLOG' for log messages, and 'PERR' for errors. By default, the message +types are colored differently to make them easily distinguishable. +In addition to normal messages, there is the 'PDBG' for debugging +purposes. It remains to be the only macro that prints the function name +as message prefix and is meant for temporary messages, to be removed +before finalizing the code. + +Genode's on-demand-paging mechanism relies on the signalling framework. +Each managed dataspace is assigned to a distinct signal context. +Hence, signal contexts need to be created and disposed alongside +with managed dataspaces. We complemented the signalling framework +with a 'dissolve' function to enable the destruction of signal +contexts. + + +Operating-system services and libraries +####################################### + +Finished transition to new init concept +======================================= + +With the release 10.05, we introduced the +[http://genode.org/documentation/release-notes/10.05#section-0 - current configuration concept of init]. +This concept supports mandatory access control and provides flexible +ways for defining client-server relationships. Until now, we maintained +the old init concept. With the current release, the transition to the +new concept is finished and we removed the traditional init. +We retained the support for loading configurations for individual subsystems +from different files but adopted the syntax to the use of attributes. +Instead of +! subsystem.config +the new syntax is +! + + +Virtual network bridge (Proxy ARP) +================================== + +Since we originally added networking support to Genode, only one program could +use the networking facilities at a time. In the simplest form, such a program +included the network driver, protocol stack, and the actual application. For +example, the uIP stack featured with release 9.02 followed this approach. +In release 9.11 we added the 'Nic_session' interface to decouple the network +driver from the TCP/IP protocol stack. But the 1-to-1 relation between +application and network interface remained. With the current release, we +introduce the 'nic_bridge' server, which is able to multiplex the 'Nic_session' +interface. + +The implementation is roughly based on the proxy ARP RFC 1027. At startup, the +'nic_bridge' creates a 'Nic_session' to the real network driver and, in turn, +announces a 'Nic' service at its parent. But in contrast to a network driver +implementing this interface, 'nic_bridge' supports an arbitrary number of +'Nic_sessions' to be opened. From the client's perspective, such a session +looks like a real network adaptor. + +This way, it has become possible to run multiple TCP/IP stacks in +parallel, each obtaining a distinct IP address via DHCP. For example, +is has become possible to run multiple paravirtualized Linux kernels +alongside an lwIP-based web browser, each accessing the network via a +distinct IP address. + +As a side effect for developing the 'nic_bridge', we created a set +of utilities for implementing network protocols. The utilities are +located at 'os/include/net' and comprise protocol definitions for +ethernet, IPv4, UDP, ARP, and DHCP. + + +Nitpicker GUI server +==================== + +Our work on the Live CD motivated several improvements of the Nitpicker +GUI server. + + +Alpha blending +~~~~~~~~~~~~~~ + +In addition to nitpicker's plain pixel buffer interface that is compatible +with a normal framebuffer session, each nitpicker session can now have +an optional alpha channel as well as an corresponding input-mask channel +associated. Both the alpha channel and the input mask are contained in the +same dataspace as the pixel buffer. The pixel buffer is followed by +the 8-bit alpha values, which are then followed by the input-mask values. +This way, the presence of an alpha channel does not interfere with the +actual pixel format. Each 8-bit input mask value specifies the user-input +policy for the respective pixel. If the value is zero, user input +referring to the pixel is not handled by the client but "falls through" +the view that is visible in the background of the pixel. This is typically +the case for drop shadows. If the input-mask value is '1', the input +is handled by the client. + +With the input-mask mechanism in place, we no longer have a definitive +assignment of each pixel to a single client anymore. In principle, an +invisible client is able to track mouse movements by creating a full-screen +view with all alpha values set to '0' and all input-mask values set to '1'. +Once, the user clicks on this invisible view, the user input gets routed +to the invisible client instead of the actually visible view. This +security risk can be addressed at two levels: +* In X-Ray mode, nitpicker completely disables alpha blending + and the input-mask mechanism such that the user can identify the + client that is responsible for each pixel on screen. +* The use of the alpha channel is a session argument, which is specified + by nitpicker clients at session-creation time. Consequently, this + session argument is subjected to the policy of all processes involved + with routing the session request to nitpicker. Such a policy may permit + the use of an alpha channel only for trusted applications. + +_Caution:_ The use of alpha channels implies read operations from +the frame buffer. On typical PC graphics hardware, such operations are +extremely slow. For this reason, the VESA driver should operate in +buffered mode when using alpha blending in Nitpicker. + + +Tinted views in X-Ray mode +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We added support for tinting individual clients or groups of clients +with different colors based on their label as reported at session-creation +time. By using session colors, nitpicker assists the user to tell apart +different security domains without reading textual information. In +addition to the tinting effect, the title bar presents the session +color of the currently focused session. + +The following nitpicker configuration tints all views of the launchpad +subsystem in blue except for those views that belong to the testnit +child of launchpad. Those are tinted red. +! +! +! +! + + +Misc Nitpicker changes +~~~~~~~~~~~~~~~~~~~~~~ + +We introduced a so-called 'stay-top' session argument, which declares +that views created via this session should stay on top of other views. +This function is useful for menus that should always remain accessible +or banner images as used for Live CD. + +Nitpicker's reserved region at the top of the screen used to cover up +the screen area as seen by the clients. We have now excluded this area +from the coordinate system of the clients. + +We implemented the 'kill' mode that can be activated by the 'kill' key. +(typically this is the 'Print Screen' key) This feature allows the user +to select a client to be removed from the GUI. The client is not +actually killed but only locked out. The 'kill' mode is meant as an +emergency brake if an application behaves in ways not wanted by the +user. + + +ISO9660 server +============== + +As outlined in Section [On-demand paging], we revisited the ISO9660 server +to implement on-demand-paged dataspaces. It is the first real-world +use case for Genode's user-level page-fault protocol. The memory pool +to be used as backing store for managed dataspaces is dimensioned according +to the RAM assigned to the iso9660 server. The server divides this backing +store into blocks of 64KB and assigns those blocks to the managed dataspaces +in a fifo fashion. We found that using a granularity of 64KB improved the +performance over smaller block sizes because this way, we profit from reading +data ahead for each block request. This is particularly beneficial for +CDROM drives because of their extremely long seek times. + + +Audio mixer +=========== + +We added a new *channel synchronization* facility to the 'Audio_out_session' +interface. An 'Audio_out_session' refers to a single channel. For stereo +playback, two sessions must be created. At session-creation time, the +client can provide a hint about the channel type such as "front-left" as +session-construction argument. This design principally allows for supporting +setups with an arbitrary amount of channels. However, those channels must +be synchronized. For this reason, we introduced the 'sync_session' function +to the 'Audio_out_session' interface. It takes the session capability of +another 'Audio_out_session' as argument. The specified session is then +used as synchronization reference. + +To reduce the latency when stopping audio replay, we introduced a new *flush* +function to the 'Audio_out_session' interface. By calling this function, +a client can express that it is willing to discard all audio data already +submitted to the mixer. + +Furthermore, we improved the audio mixer to support both long-running +streams of audio and sporadic sounds. For the latter use case, low latency +is particularly critical. In this regard, the current implementation is a +vast improvement over the initial version. However, orchestrating the +mixer with audio drivers as well as with different clients (in particular +ALSA programs running on a paravirtualized Linux) is not trivial. In the +process, we learned a lot, which will eventually prompt us to further +optimize the current solution. + + +Nitpicker-based virtual Framebuffer +=================================== + +To support the browser-plugin demo, we introduced 'nit_fb', which is a +framebuffer service that uses the nitpicker GUI server as back end. It +is similar to the liquid framebuffer as featured in the 'demo' repository +but in contrast to liquid framebuffer, 'nit_fb' is non-interactive. +It has a fixed screen position and size. Furthermore, it does not +virtualize the framebuffer but passes through the framebuffer portion of +the nitpicker session, yielding better performance and lower latency. + +If instantiated multiple times, 'nit_fb' can be used to statically arrange +multiple virtual frame buffers on one physical screen. The size and screen +position of each 'nit_fb' instance can be defined via Genode's configuration +mechanism using the following attributes of the 'nit_fb' config node: +! +If 'refresh_rate' isn't set, the server will not trigger any refresh +operations by itself. + +On the Live CD, each browser plugin instantiates a separate instance of +'nit_fb' to present the plugin's content on screen. In this case, the +view position is not fixed because the view is further virtualized by the +loader, which imposes its policy onto 'nit_fb' - Genode's nested +policies at work! + + +TAR ROM service +=============== + +For large setups, listing individual files as boot modules in single-image +creation tools (e.g., elfweaver) or multiboot boot loaders can be +cumbersome, especially when many data files or shared libraries are +involved. To facilitate the grouping of files, 'tar_rom' is an +implementation of the 'ROM' interface that operates on a 'tar' file. + +The name of the TAR archive must be specified via the 'name' attribute of +an 'archive' tag, for example: + +! +! +! + +The backing store for the dataspaces exported via ROM sessions is accounted +on the 'rom_tar' service (not on its clients) to make the use of 'rom_tar' +transparent to the regular users of core's ROM service. Hence, this service +must not be used by multiple clients that do not trust each other. +Typically, 'tar_rom' is instantiated per client. + +The Live CD uses the 'tar_rom' service for the browser demo. Each plugin +is fetched from the web as a tar file containing the config file of the +plugin subsystem as well as supplemental binary files that are provided +to the plugin subsystem as ROM files. This way, a plugin can carry along +multiple components and data that form a complete Genode subsystem. + + +DDE Kit +======= + +The DDE kit underwent slight modifications since the previous release. +It now provides 64-bit integer types and a revised virtual PCI bus +implementation. + + +Device drivers +############## + +PCI bus +======= + +Genode was tested on several hardware platforms in preparation of the +current release. This revealed some deficiencies with the PCI bus +driver implementation. The revised driver now efficiently supports +platforms with many PCI busses (as PCIe demands) and correctly handles +multi-function devices. + + +VESA framebuffer +================ + +We updated the configuration syntax of the VESA driver to better match +the style of new init syntax, preferring the use of attributes rather than +XML sub nodes. Please refer to the updated documentation at +'os/src/drivers/framebuffer/vesa/README'. + +:Buffered output: + + To accommodate framebuffer clients that need to read from the frame buffer, + in particular the nitpicker GUI server operating with alpha channels, we + introduced a buffered mode to the VESA driver. If enabled, the VESA driver + will hand out a plain memory dataspace to the client rather than the + physical framebuffer. Each time, the client issues as 'refresh' operation + on the framebuffer-session interface, the VESA driver copies the corresponding + screen region from the client-side virtual framebuffer to the physical + framebuffer. Note that the VESA driver will require additional RAM quota + to allocate the client buffer. If the quota is insufficient, the driver will + fall back to non-buffered output. + +:Preinitialized video modes: + + As an alternative to letting the VESA driver set up a screen mode, the + driver has become able to reuse an already initialized mode, which is useful + if the VESA mode is already initialized by the boot loader. If the screen + is initialized that way, the 'preinit' attribute of the 'config' node can + be set to '"yes"' to prevent the driver from changing the mode. This way, + the driver will just query the current mode and make the already + initialized framebuffer available to its client. + + +Audio +===== + +We observed certain hardware platforms (in particular VirtualBox) to +behave strangely after ALSA buffer-underrun conditions. It seems that the +VirtualBox audio driver plays actually more frames than requested by +ALSA's 'writei' function, resulting in recurring replay of data that +was in the buffer at underrun time. As a work-around for this problem, +we zero-out the sound-hardware buffer in the condition of an ALSA buffer +underrun. This way, the recurring replay is still there, but it is +replaying silence. + +To improve the support for sporadic audio output, we added a check for the PCM +state for buffer underruns prior issuing the actual playback. In the event of +an underrun, we re-prepare the sound card before starting the playback. + +Furthermore, we implemented the new flush and channel-synchronization +abilities of the 'Audio_out_session' interface for the DDE Linux driver. + + +Paravirtualized Linux +##################### + +To support the demo scenarios that showcase the paravirtualized Linux kernel, +we enhanced our custom stub drivers of the OKLinux kernel. Thereby, we have +reached a high level of integration of OKLinux with native Genode services, +including audio output, block devices, framebuffer output, seamless integration +with the Nitpicker GUI, and networking. All stub drivers are compiled in by +default and are ready to use by specifying a device configuration in the config +node for the Linux kernel. This way, one Linux kernel image can be easily used +in different scenarios. + +:Integration with the Nitpicker GUI: + + We enhanced our fbdev stub driver with a mechanism to merge view reposition + events. If a X11 window is moved, a lot of subsequent events of this type are + generated. Using the new optimization, only the most recent state gets + reported to Nitpicker, making the X11 GUI more responsive. + +:UnionFS: + + As we noticed that unionfs is required by all our Linux scenarios, we decided + to include and enable the patch by default. + +:Network support: + + With the introduction of the 'nic_bridge', multiple networking stacks can run + on Genode at the same time, which paves the way for new use cases. We have now + added a stub driver using Genode's 'Nic_session' interface to make the new + facility available to Linux. + +:Audio output: + + We adapted the ALSA stub driver to the changes of the 'Audio_out_session' + interface, using the new channel synchronization and flush functions. + Thereby, we optimized the stub driver to keep latency and seek times of + Linux userland applications reasonably low. + +:Removed ROM file driver: + + With the addition of the 'Block_session' stub driver, the original ROM file + driver is no longer required. So we removed the stub. For using ROM files as + disk images for Linux, there is the 'rom_loopdev' server, which provides a + block session that operates on a ROM file. + +:Asynchronous block interface: + + To improve performance, we changed the block stub driver to facilitate the + asynchronous mode of operation as provided by the 'Block_session' interface. + This way, multiple block requests can be issued at once, thereby shadowing + the round trip times for individual requests. + + +Protocol stacks and libraries +############################# + +Gallium3D / Intel GEM +===================== + +We improved the cache handling of our DRM emulation code (implementing +'drm_clflush_pages') and our EGL driver, thereby fixing caching +artifacts on i945 GPUs. Furthermore, we added a temporary work-around +for the currently dysfunctional sequence-number tracking with i945 GPUs. +On this chipset, issuing the 'MI_STORE_DWORD_INDEX' GPU command used +for tracking sequence numbers apparently halts the processing the command +stream. This condition is normally handled by an interrupt. However, +we have not enabled interrupts yet. + +To prepare the future support for more Gallium drivers than i915, we +implemented a driver-selection facility in the EGL driver. The code +scans the PCI bus for a supported GPU and returns the name of the +corresponding driver library. If no driver library could be found, +the EGL driver falls back to softpipe rendering. + + +lwIP +==== + +We revised our port of the lwIP TCP/IP stack, and thereby improved its +stability and performance. + +* The lwIP library is now built as shared object, following the convention + for libraries contained in the 'libports' repository. +* By default (when using the 'libc_lwip_nic_dhcp' library), lwIP will + issue a DHCP request at startup. If this request times out, the loopback + device is set as default. +* If there is no 'Nic' service available, the lwIP stack will fall back to + the loopback device. +* We increased the default number of PCBs in lwIP to 64. +* We removed a corner case of the timed semaphore that could occur + when a timeout was triggered at the same time ,'up' was called. + In this case, the semaphore was unblocked but the timeout condition + was not reflected at the caller of 'down'. However, the lwIP code + relies on detecting those timeouts. + + +Qt4 +==== + +We implemented a custom *nitpicker plugin widget*, which allows for the +seamless integration of arbitrary nitpicker clients into a Qt4 application. +The primary use case is the browser plugin mechanism presented at +the Live CD. In principle, the 'QNitpickerViewWidget' allows for creating +mash-up allocations consisting of multiple native Genode programs. As shown +by the browser plugin demo, a Qt4 application can even integrate other +programs that run isolated from the Qt4 application, and thereby depend on +on a significantly less complex trusted computing base than the Qt4 +application itself. + +[image img/nitpicker_plugin] + +The image above illustrates the use of the 'QNitpickerViewWidget' in the +scenario presented on the Live CD. The browser obtains the Nitpicker view to be +embedded into the website from the loader service, which virtualizes the +Nitpicker session interface for the loaded plugin subsystem. The browser then +tells the loader about where to present the plugin view on screen. But it has +neither control over the plugin's execution nor can it observe any user +interaction with the plugin. + + +New Gems repository with HTTP-based block server +################################################ + +To give the web-browser demo of our Live CD a special twist, and to show off +the possibilities of a real multi-server OS, we decided to implement the +somewhat crazy idea of letting a Linux OS run on a disk image fetched at +runtime from a web server. This way, the Linux OS would start right away and +disk blocks would be streamed over the network as needed. Implementing this +idea was especially attractive because such a feature would be extremely hard +to implement on a classical OS but is a breeze to realize on Genode where all +device drivers and protocol stacks are running as distinct user-level +components. The following figure illustrates the idea: + +[image img/http_block] + +The block stub driver of the Linux kernel gets connected to a special block +driver called 'http_block', which does not access a real block device but +rather uses TCP/IP and HTTP to fetch disk blocks from a web server. + +Because the 'http_block' server is both user of high-level functionality (the +lwIP stack) and provider of a low-level interface ('Block_session'), the +program does not fit well into one of the existing source-code repositories. +The 'os' repository, which is normally hosting servers for low-level interfaces +is the wrong place for 'http_block' because this program would make the 'os' +repository depend on the higher-level 'libports' repository where the 'lwip' +stack is located. On the other hand, placing 'http_block' into the 'libports' +repository is also wrong because the program is not a ported library. It merely +uses libraries provided by 'libports'. In the future, we expect that native +Genode components that use both low-level and high-level repositories will +become rather the norm than an exception. Therefore, we introduced a new +repository called 'gems' for hosting such programs. + + +Tools +##### + +Automated coding-style checker +============================== + +As Genode's code base grows and new developers start to get involved, +we noticed recurring questions regarding coding style. There is a +[http://genode.org/documentation/developer-resources/coding_style - document] +describing our coding style but for people just starting to get involved, +adhering all the rules can become tedious. However, we stress the importance +of a consistent coding style for the project. Not only does a consistent style +make the framework more approachable for users, but it also eases the work +of all regular developers, who can feel right at home at any part of +the code. + +To avoid wasting precious developer time with coding-style fixes, we +have created a tool for the automated checking and (if possible) fixing +the adherence of source code to Genode's coding style. The tool is +located at 'tool/beautify'. It takes a source file as argument and +reports coding-style violations. The checks are fairly elaborative: +* Placement of braces and parenthesis +* Indentation and alignment, trailing spaces +* Vertical spacing (e.g., between member functions, above comments) +* Naming of member variables and functions (e.g., private members start with '_') +* Use of upper and lower case +* Presence of a file header with the mandatory fields +* Policy for function-header comments (comment at declaration, not + at implementation) +* Style of single-line comments, function-header comments, multi-line comments + +The user of 'beautify' may opt to let the tool fix most of the violations +automatically by specifying the command line arguments '-fix' and '-write'. +With only the '-fix' argument, the tool will output the fixed version of +the code via stdout. By specifying the '-write' argument, the changes will +be written back to the original file. In any case, we strongly recommend +to manually inspect all changes made by the tool. + +Under the hood, the tool consists of two parts. A custom C++ parser called +'parse_cxx' reads the source code and converts it to a syntax tree. In the +syntax tree, all formating information such as whitespaces are preserved. +The C++ parser is a separate command-line tool, which we also use for +other purposes (e.g., generating the API documentation at the website). +The actual 'beautify' tool calls 'parse_cxx', and applies its checks and +fixes to the output of 'parse_cxx'. For this reason, both tools have to +reside in the same directory. + + +Platform-specific changes +######################### + +OKL4 +==== + +:Added support for shared interrupts: + + The Genode Live CD operates on a large number of devices that trigger + interrupts (USB, keyboard, mouse, ATAPI, timer, network). On most + platforms, the chances are extremely high that some of them use + the same IRQ line. Therefore, we enhanced core's IRQ service to + allow multiple clients to request the same IRQ. If the interrupt occurs, + all clients referring to this interrupt are notified. The interrupt + gets cleared after all of those clients responded. Even though, we regard + PIC interrupts as a legacy, the support of shared interrupts enables + us to use OKL4 with such complex usage scenarios. + +:Revised page-fault handling: + + If a page fault occurs, the OKL4 kernel delivers a message to the page-fault + handler. The message contains the page-fault address and type as well as the + space ID where the fault happened. However, the identity of the faulting + thread is not delivered. Instead, the sender ID of the page fault message + contains the KTCB index of the faulting thread, which is only meaningful + within the kernel. This KTCB index is used as a reply token for answering the + page fault message. We wondered about why OKL4 choose to deliver the KTCB + index rather then the global thread ID as done for plain IPC messages. The + only reasonable answer is that by using the KTCB index directly in OKL4's + page-fault protocol, one lookup from the userland-defined thread ID to the + KTCB index can be avoided. However, this comes at the cost of losing the + identity of the faulting thread. We used to take the space ID as a key for + the fault context within core. However, with Genode's user-level page-fault + mechanism, this simplification does not suffice anymore. We have to know the + faulting thread as a page fault may not be answered immediately but at a + later time. During that time, the page-fault state has to be stored at core's + representation of the faulting thread. Our solution is reverting OKL4's + page-fault protocol to operate with global thread IDs only and to never make + kernel-internal KTCB indices visible at the user land. You can find the patch + for the OKL4 kernel at 'base-okl4/patches/reply_tid.patch'. + +:Reboot via kernel debugger: + + We fixed the reboot code of OKL4's kernel debugger to improve our work + flow. The patch can be found at 'base-okl4/patches/kdb_reboot.patch'. + +:Relieved conflict with libc 'limits.h': + + For some reason, the OKL4 kernel bindings provide definitions + normally found in libc headers. This circumstance ultimately leads + to trouble when combining OKL4 with a real C runtime. We have + relieved the problem with the patch 'base-okl4/patches/char_bit.patch'. + +:Exception handling: + + We added a diagnostic message to core that reports about exceptions + such as division by zero. + + +Pistachio +========= + +Our revised syscall bindings for supporting position-independent code +on L4ka::Pistachio have been integrated into the mainline development +of the kernel. Therefore, the patch is not needed anymore when using +a kernel revision newer than 'r791:0d25c1f65a3a'. + + +Linux +===== + +On Linux, we let the kernel manage all virtual address spaces for us, +except for the thread-context area. Because the kernel does not know +about the special meaning of the thread-context area, it may choose to +use this part of the virtual address space as target for 'mmap'. This +may lead to memory corruption. Fortunately, there is a way to tell the +kernel about virtual address regions that should be reserved. The +trick is to pre-populate the said region with anonymous memory using +the 'mmap' arguments 'MAP_PRIVATE', 'MAP_FIXED', 'MAP_ANONYMOUS', and +'PROT_NONE'. The kernel will still accept a fixed-address mapping +within such a reserved region (overmap) but won't consider using the +region by itself. The reservation must be done at the startup of each +process and each time when detaching a dataspace from the thread +context area. For the process startup, we use the hook function +'main_thread_bootstrap' in 'src/platform/_main_helper.h'. For reverting +detached dataspaces to a reserved region within the context area, we +added as special case to 'src/base/env/rm_session_mmap.cc'. +For hybrid programs (Genode processes that link against native +shared libraries of the Linux system), which are loaded by the dynamic +linker of Linux, we must further prevent the dynamic linker from +populating the thread-context area. This is achieved by adding a +special program segment at the linking stage of all elf binaries. + diff --git a/doc/release_notes-11-02.txt b/doc/release_notes-11-02.txt new file mode 100644 index 000000000..022186ad5 --- /dev/null +++ b/doc/release_notes-11-02.txt @@ -0,0 +1,876 @@ + + + =============================================== + Release notes for the Genode OS Framework 11.02 + =============================================== + + Genode Labs + + + +One year ago, the release 10.02 was our break-through with regard to the support +of multiple kernels as base platform for Genode. With the added support for +the NOVA hypervisor and the Codezero kernel, Genode applications could be executed +on 6 different kernels. With the current release, we take our commitment to +kernel platform support even further. With the added support for the Fiasco.OC +kernel, we make Genode available on one of the most feature-rich modern microkernels. +Additionally, we entered the realms of kernel design with our new platform support +for the Xilinx MicroBlaze architecture. This platform support comes in the shape +of a custom kernel specifically targeted to the MicroBlaze CPU architecture. +Furthermore, we updated our support for the NOVA Hypervisor to the bleeding-edge +version 0.3, which has been released earlier this month. + +With the current support for 8 different kernel platforms (L4/Fiasco, Linux, +L4ka::Pistachio, OKL4, NOVA, Codezero, Fiasco.OC, and native MicroBlaze), testing +and integrating application scenarios across all platforms becomes increasingly +challenging. Therefore, we introduce a new framework for automating such tasks. +Thanks to the tight integration of the automation tool with Genode's build system, +going back and forth between different kernels becomes an almost seamless +experience. + +Functionality-wise, the release carries on our vision to create a highly secure +yet easy to use general-purpose operating system. Because the Genode framework +is developed on Linux using the wonderful GNU tools, we consider the +availability of the GNU user land on Genode as crucial for using the system by +ourself. This motivation drives the creation of a custom execution environment +for GNU software on top of Genode. With the current release, we are proud to +present the first pieces of this execution environment. Even though not fully +functional yet, it clearly shows the direction of where we are heading. + + +Support for Fiasco.OC +##################### + +The OC in the name of the Fiasco.OC kernel stands for "object capability", hinting +at the most significant feature that sets current-generation microkernels such as +NOVA, seL4, and Fiasco.OC apart from their predecessors. Whereas previous L4 kernels +succeeded in protecting subsystems from each other, the new generation of kernels +is geared towards strict security policies. Traditionally, two protection domains +were able to communicate with each other if they both agreed. Communication partners +were typically globally known via their respective thread/task IDs. Obviously, this +policy is not able to guarantee the separation of subsystems. If two subsystems +conspire, they could always share information. Object-capability-based kernels +are taking the separation much further by prohibiting any communication between +protection domains by default. Two protection domains can communicate only if +a common acquaintance of both agrees. This default-deny policy facilitates the +creation of least-privilege security policies. From the ground up, Genode has +been designed as a capability-based system which is naturally capable of leveraging +kernel-based object-capability support if present. After NOVA, Fiasc.OC is the +second of Genode's base platforms that provides this feature. + +Apart from being a capability-based kernel, Fiasco.OC has a number of compelling +features such as thorough support for ARM platforms and the x86 32/64 bit +architectures. It supports SMP, hardware virtualization, and provides special +optimizations for running paravirtualized operating systems. + +Technically, Fiasco.OC is the successor of the L4/Fiasco kernel developed by +the OS group of the TU-Dresden. However, the kernel interface of Fiasco.OC has +not much in common with L4/Fiasco. Some heritages are still there (e.g., IPC +timeouts) but the kernel API has evolved to a fully object-oriented model. + +:Thanks: + + We are indebted to the main developer of Fiasco.OC Alexander Warg for being + very reponsive to our inquiries while doing the porting work. Thanks to his + support, the adaptation of Genode to this kernel has been an almost smooth + ride. + + +Prerequisites +============= + +You need GNU C & C++ Compilers, GNU Binutils, GNU Make, and Perl to use the +Fiasco.OC build system. On Debian/Ubuntu systems, you have to install the +following packages: + +! apt-get install make gawk g++ binutils pkg-config subversion + +Moreover, you need to download and install the tool-chain used by Genode. Have +a look at this page: + +:[http://genode.org/download/tool-chain]: + Genode tool-chain + + +Downloading and building Fiasco.OC +================================== + +Checkout the Fiasco.OC sources and tool-chain to an appropriated directory: + +! export REPOMGR_SVN_REV=27 +! svn cat http://svn.tudos.org/repos/oc/tudos/trunk/repomgr |\ +! perl - init http://svn.tudos.org/repos/oc/tudos fiasco l4re + + +Building the kernel +~~~~~~~~~~~~~~~~~~~ + +Create the build directory for the kernel: + +! cd /src/kernel/fiasco +! make BUILDDIR= + +Go to the build directory, configure the kernel: + +! cd mybuild +! make config + +This will launch the configuration menu. Here you can configure your kernel. +The default config is just fine to test the Genode port. It will build a +uniprocessor IA32 kernel with debugging features enabled. You can exit the menu and +save the configuration by simply typing 'x'. + +Now, build Fiasco.OC by invoking: + +! make + + +Building necessary tools +~~~~~~~~~~~~~~~~~~~~~~~~ + +To practically use Fiasco.OC, you need in addition to the kernel a tool to +bootstrap it, and the initial pager of the system, namely 'sigma0'. Both tools +can be found in the L4 runtime environment's base directory. Outgoing from +the directory where you checked out the sources, you have to change to the +following directory: + +! cd /src/l4 + +Create another build directory: + +! make B= + +Again, you might want to tweak the configuration: + +! make O= config + +Finally, build the tools: + +! make O= + + +Building the Fiasco.OC version of Genode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Fiasco.OC version of Genode is available at the Genode public subversion repository: + +:http://genode.org/download/subversion-repository: + Information about accessing the Genode public subversion repository + +Go to a directory where you want the Genode/Fiasco.OC build directory to remain. Use +the helper script in the 'tool/builddir' directory of the Genode source tree to +create the initial build environment. You need to state the absolute path to the +build directory of the L4 runtime environment as 'L4_DIR', as it contains the kernel +bindings needed by the Genode port. + +! /tool/builddir/create_builddir foc_x86_32 \ +! L4_DIR= \ +! GENODE_DIR= \ +! BUILD_DIR= + +Now, go to the newly created build directory and type make. + +! cd +! make + + +Booting Genode on top of Fiasco.OC +================================== + +Example GRUB configuration entry: + +! timeout 0 +! default 0 +! +! title Genode on Fiasco.OC +! kernel /bootstrap -modaddr=0x01100000 +! module /fiasco -serial_esc +! module /sigma0 +! module /core +! module /init +! module /config +! module /pci_drv +! module /vesa_drv +! module /ps2_drv +! module /timer +! module /nitpicker +! module /launchpad +! module /liquid_fb +! module /scout +! module /testnit +! module /nitlog + +For an example of a matching Genode 'config' file, please take a look +at 'os/config/demo'. + +The Genode binaries are located in '/bin', +the 'fiasco' kernel in ''. Assuming you compiled +for x86/586 (the default), you can find the 'bootstrap' binary in +'bin/x86_586' and 'sigma0' in 'bin/x86_586/l4f' within the +'' directory. + + +Current state +============= + +The adaptation of Genode to Fiasco.OC covers most parts of the Genode API +including advanced semantics such as cancelable locks and support for +real-time priorities. So far, it has been tested on the x86 architecture. +Because 'base-foc' does not contain x86-specific code, we expect no major +roadblocks for running Genode on Fiasco.OC on ARM. However, we have not +exercised tests in this regard. + +As of today, there exist the following limitations of the Fiasco.OC support: + +* The dynamic linker is not yet adapted to Fiasco.OC. Special care must + be taken for handling the parent capability for dynamically loaded + programs. We have already covered this issue for the NOVA version but + the adaptation to Fiasco.OC remains yet to be done. + +* The destruction of sub systems is not yet fully stable. Because Genode + forms a more dynamic workload than the original userland accompanied with + the kernel, the usage pattern of the kernel API triggers different + effects. We are working with the Fiasco.OC developers to remedy this + issue. + +* The signalling framework is not yet supported. A design exist but it is + not implemented yet. + +We believe however that none of these limitations are a significant hurdle for +starting to use Genode with this kernel. Please expect this issues to be +resolved with the upcoming Genode release. + + +Technical details about 'base-foc' +================================== + +The following technical bits are worth noting when exploring the use of +Genode with the 'base-foc' platform. + +* The timer implementation uses a one thread-per-client mode of operation. + We use IPC timeouts as time source. Hence, the timer driver is hardware + independent and should work out of the box on all hardware platforms + supported by Fiasco.OC. + +* Each 'Server_object' of Genode corresponds to a so-called IPC gate, + which is the Fiasco.OC kernel object used for capability invocation. + Therefore, protection and object integrity is provided at the fine + granularity of single 'Server_objects'. This is in line with our + support for NOVA's implementation of capability-based security. + +* In contrast to the lock implementation that we used with the original + L4/Fiasco kernel, the 'base-foc' lock is a fully-featured Genode lock + with support for lock cancellation and blocking. For blocking and + waking up lock applicants, we use Fiasco.OC's IRQ objects. + +* The allocator used for managing process-local capability selectors + does not yet support the reuse of capability selectors. + + +Further Information +=================== + +:genode/tool/builddir/README: + Reference manual for the 'create_builddir' script + +:[http://os.inf.tu-dresden.de/fiasco]: + Official website for the Fiasco.OC microkernel. + + +Noux - an execution environment for the GNU userland +#################################################### + +Even though Genode is currently mainly geared to the classical special-purpose +application domains for microkernel-based systems, the main property that sets +Genode apart from traditional systems is the thorough support for dynamic +workloads and the powerful mechanisms for handling hardware resources and +security policies in highly dynamic setting. We are convinced that Genode's +architecture scales far beyond static special-purpose domains and believe in +the feasibility of Genode as a solid foundation for a fully-fledged general +purpose operating system. Internally at Genode Labs, we set up the ultimate +goal to switch from Linux to Genode for our day-to-day work. We identified +several functionalities that we could not live without and systematically try +to bring those features to Genode. Of course, the most fundamental programs +are the tools needed to develop and build Genode. Currently we are developing +on Linux and enjoy using the GNU userland. + +Consequently, we require a solution for using this rich tool set on Genode. +The straight-forward way for making these tools available on Genode would be +running them within a virtualized Linux instance (e.g., using OKLinux on OKL4). +However, this approach would defeat our actual goal to create a highly secure +yet easy to use working environment because adding Linux to the picture would +involve administering the virtualized Linux system. We would prefer a native +solution that makes the overall system less, not more, complicated. This way +the idea for a native execution environment for the GNU userland on Genode +was born. The implementation is called Noux and the first bits of code are +featured in the 'ports' repository. Noux consists of two parts, a build +environment for compiling GNU programs such that they can be run as Genode +processes and an execution environment that provides the classical UNIX +functionality to these programs. + + +Noux build environment +====================== + +From our experience, porting existing UNIX applications to a non-UNIX system +tends to be a task of manual and time-consuming labour. One has to loosely +understand the build system and the relationship of the involved source codes, +implement dummy functions for unresolved references, and develop custom glue +code that interfaces the ported application to the actual system. Taking the +shortcut of changing the original code has to be avoided at any cost because +this produces recurring costs in the future when updating the application. In +short, this long-winding process does not scale. For porting a tool set such as +the GNU userland consisting of far more than a three-digit number of individual +programs, this manual approach becomes unfeasible. Therefore, we have created +a build environment that facilitates the use of the original procedure of +invoking './configure && make'. The challenge is to supply configure with +the right arguments and environment variables ('CFLAGS' and the like) such that +the package is configured against the Genode environment. The following +considerations must be taken: + +* Configure must not detect any global headers (e.g., '/usr/include/') + or libraries (e.g., '/usr/lib/'). This can be achieved by the '-nostdinc' and + '-nostdlib' options +* Configure has to use the same include-search paths as used for compiling + normal libc-using Genode programs +* Configure must use the Genode tool chain +* The final linking stage must use the Genode linker script, the Genode + base libraries, and other Genode-specific linker arguments. + +Thanks to the power of the GNU build system, all this can be achieved by +supplying arguments to './configure' and indirectly to the 'make' process via +environment variables. The new Noux build environment takes care of these +precautions. It comes in the form of the 'ports/mk/noux.mk' file which enables +the seamless integration of GNU packages with the Genode build system. To +compile a GNU package, the manual steps needed are reduced to the creation of a +'target.mk' file representing the package. This 'target.mk' defines the name +of the package (by default, the basename of the 'target.mk' enclosing directory +is assumed) and the location of the source package. With this approach, we +managed to build 'coreutils' (over 100 small UNIX utilities such as 'ls', 'cp', +'sort'), 'binutils' (GNU linker, assembler, object-file tools), 'findutils' +('find', 'xargs'), 'bash', 'dash', GNU make, and finally the GNU compiler +collection including 'g++'. The resulting binaries are ready to be executed as +native Genode processes. However, without the right environment that presents +the program the needed UNIX functionality, those programs won't do much. +This leads us to the Noux execution environment. + + +Noux execution environment +========================== + +The Noux execution environment plays the role of a UNIX kernel for programs +built via the Noux build environment. In contrast to a real kernel, the Noux +environment is a plain Genode user-level process that plays the role of being +the parent of one or multiple Noux processes. In addition of providing the +'Genode::Parent' interface, Noux also provides a locally implemented service called +'Noux::Session' that offers UNIX-like system-calls via an RPC interface. Each +hosted program is linked against a special Noux libc plugin that catches all +libc calls that would normally result in a system call. It then transparently +forwards this function call to the 'Noux::Session' interface. + +Currently the Noux execution environment implements the following +system calls: 'getcwd', 'write', 'stat', 'fstat', 'fcntl', 'open', +'close', 'dirent', 'fchdir', 'read', and 'execve'. + +The execution environment submits arguments (argc, argv, environment) to the +hosted program, manages its current working directory and receives its exit +code. File operations are targeted to a custom VFS infrastructure, which +principally allows a flexible configuration of the virtual file system visible +to the hosted programs. At the current stage, Noux supports mounting plain tar +archives obtained from core's ROM service as read-only file system. On startup, +the Noux environment starts one process (the init process) and connects the +file descriptor 1 (stdout) to Genode's LOG service. + +State of the implementation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The infrastructure implemented so far already allows the execution of many simple +UNIX tools such as 'ls -lRa', 'echo', 'seq', 'find'. The 'execve' system call +is implemented such that a new process is started that inherits the file +descriptors and the PID of the calling process. This allows using the exec +functionality of the 'bash' shell. However, because 'fork' is not implemented +yet, there is currently no way to start multiple programs hosted in a single +Noux execution environment. + +As of today, the Noux environment is not considered to be usable for practical +purposes. However, it clearly shows the feasibility of the path we are walking. +With the foundation laid, we are looking forward to expanding Noux to a capable +solution for running our beloved GNU userland tools on Genode. + +Vision +~~~~~~ + +The most significant intermediate result of pursuing the development of Noux is +the realization that such an environment is not exceedingly complex. Because of +the combination with Genode, we only need to provide a comfortable runtime as +expected by user processes but we can leave much of intricate parts of UNIX out +of the picture. For example, because we handle device drivers on Genode, we do +not need to consider device-user interaction in Noux. As another example, +because the problem of bootstrapping the OS is already solved by Genode, there +is no need to run an 'init' process within Noux. Our vision foresees that Noux +runtimes are to be created on demand for individual tasks such as editing a +file (starting a custom Noux instance containing only the file to edit and the +text editor), compiling source code (starting a custom Noux instance with only +the source code and the build tools). Because Noux is so simple, we expect the +runtime overhead of starting a Noux instance to be not more than the time +needed to spawn a shell in a normal UNIX-like system. + +Test drive +~~~~~~~~~~ + +To give Noux a spin, we recommend using Linux as base platform as this is +the platform we use for developing it. First, you will need to download the +source code of the GNU packages. From within the 'ports' repository, +use the following command: + +! make prepare PKG=coreutils + +This command will download the source code of the GNU coreutils. You may +also like to give the other packages a try. To see what is available, +just call 'make' without any argument. + +Create a build directory (e.g., using tool/builddir/create_builddir). +Change to the build directory and issue the command + +! make run/noux + +This command will execute the run script provided at 'ports/run/noux.run'. +First it builds core, init, and coreutils. Then it creates a tar archive +containing the installed coreutils. Finally, it starts the Noux environment on +Genode. Noux then mounts the TAR archive as file system and executes 'ls -laR', +showing the directory tree. + + +Approaching platform support for Xilinx MicroBlaze +################################################## + +With the release 11.02, we are excited to include the first version of our +custom platform support for the Xilinx MicroBlaze CPU architecture. MicroBlaze +is a so-called softcore CPU, which is commonly used as part of FPGA-based +System-on-Chip designs. At Genode Labs, we are regularly using this IP core, +in particular for our Genode FPGA Graphics Project, which is a GUI software stack +and a set of IP cores for implementing fully-fledged windowed GUIs on FPGAs: + +:Website of the Genode FPGA Graphics Project: + + [http://genode-labs.com/products/fpga-graphics] + +Ever since we first released the Genode FPGA project, we envisioned to combine +it with the Genode OS Framework. In Spring 2010, Martin Stein joined our team +at Genode Labs and accepted the challenge to bring the Genode OS Framework to +the realms of FPGA-based SoCs. Technically, this implies porting the framework +to the MicroBlaze CPU architecture. In contrast to most softcore CPUs such as +the popular Lattice Mico32, the MicroBlaze features a MMU, which is a fundamental +requirement for implementing a microkernel-based system. Architecturally-wise +MicroBlaze is a RISC CPU similar to MIPS. Many system parameters of the CPU +(caches, certain arithmetic and shift instructions) can be parametrized at +synthesizing time of the SoC. We found that the relatively simple architecture +of this CPU provides a perfect playground for pursuing some of our ideas about +kernel design that go beyond the scope of current microkernels. So instead of +adding MicroBlaze support into one of the existing microkernels already +supported by Genode, we went for a new kernel design. Deviating from the typical +microkernel, which is a self-sufficient program running in kernel mode that +executes user-level processes on top, our design regards the kernel as a part of +Genode's core. It is not a separate program but a library that implements the +glue between user-level core and the raw CPU. Specifically, it provides the +entrypoint for hardware exceptions, a thread scheduler, an IPC mechanism, and +functions to manipulate virtual address spaces (loading and flushing entries +from the CPU's software-loaded TLB). It does not manage any physical memory +resources or the relationship between processes. This is the job of core. +From the kernel-developer's point of view, the kernel part can be summarized as +follows: + +* The kernel provides user-level threads that are scheduled in a round-robin + fashion. +* Threads can communicate via synchronous IPC. +* There is a mechanism for blocking and waking up threads. This mechanism + can be used by Genode to implement locking as well as asynchronous + inter-process communication. +* There is a single kernel thread, which never blocks in the kernel code paths. + So the kernel acts as a state machine. Naturally, there is no concurrency in the + execution paths traversed in kernel mode, vastly simplifying these code parts. + However, all code paths are extremely short and bounded with regard to + execution time. Hence, we expect the interference with interrupt latencies + to be low. +* The IPC operation transfers payload between UTCBs only. Each thread has a + so-called user-level thread control block which is mapped transparently by + the kernel. Because of this mapping, user-level page faults cannot occur + during IPC transfers. +* There is no mapping database. Virtual address spaces are manipulated by + loading and flushing physical TLB entries. There is no caching of mappings + done in the kernel. All higher-level information about the interrelationship + of memory and processes is managed by the user-level core. +* Core runs in user mode, mapped 1-to-1 from the physical address space + except for its virtual thread-context area. +* The kernel paths are executed in physical address space (MicroBlaze). + Because both kernel code and user-level core code are observing the same + address-space layout, both worlds appear to run within a single address + space. +* User processes can use the entire virtual address space (4G) except for a + helper page for invoking syscalls and a page containing atomic operations. + There is no reservation used for the kernel. +* The MicroBlaze architecture lacks an atomic compare-and-swap instruction. On + user-level, this functionality is emulated via delayed preemption. A kernel- + provided page holds the sequence of operations to be executed atomically and + prevents (actually delays) the preemption of a thread that is currently + executing instructions at that page. +* The MicroBlaze MMU supports several different page sizes (1K up to 16MB). + Genode fully supports this feature for page sizes >= 4K. This way, the TLB + footprint can be minimized by choosing sensible alignments of memory + objects. + +Current state +============= + +The MicroBlaze platform support resides in the 'base-mb' repository. At the +current stage, core is able to successfully start multiple nested instances of +the init process. Most of the critical kernel functionality is working. This +includes inter-process communication, address-space creation, multi-threading, +thread synchronization, page-fault handling, and TLB eviction. + +This simple scenario already illustrates the vast advantage of +using different page sizes supported by the MicroBlaze CPU. If using +4KB pages only, a scenario with three nested init processes produces more than +300.000 page faults. There is an extremely high pressure on the TLB, which +only contains 64 entries. Those entries are constantly evicted so that +threshing effects are likely to occur. By making use of flexible page +sizes (4K, 16K, 64K, 256K, 1M, 4M, 16M), the number of page faults gets +slashed to only 1.800, speeding up the boot time by factor 10. + +Currently, there is no restriction of IPC communication rights. Threads are +addressed using their global thread IDs (in fact, using their respective +indices in the KTCB array). For the future, we are planning to add +capabilty-based delegation of communication rights. + +Building and using Genode on MicroBlaze +======================================= + +For building Genode for the MicroBlaze platform, you need the MicroBlaze +tool chain as it comes with the Xilinx EDK. The tool chain is typically +prefixed with 'mb-'. Please make sure that the tool chain's 'bin/' directory +is included in your 'PATH' environment variable. + +For building and starting Genode on MicroBlaze, you first need to create +a build directory using the build-directory creation tool: + +! tool/builddir/create_builddir microblaze \ +! BUILD_DIR= \ +! GENODE_DIR= + +The 'base-mb' repository comes with support for Genode's run tool. In order to +use it, you will first need to declare the location of your qemu binary using +the 'QEMU=/path/to/qemu' variable in the '/etc/microblaze.conf' +file. Then you will be able to start an example scenario by issuing the +following command from within your build directory: + +! make run/nested_init + +Thereby, the 'run' tool will attempt to start core using the microblaze version +of qemu. + +You can also find a simple hello-world example at 'base-mb/src/test/hello'. +The corresponding run script is located at 'base-mb/run/hello.run'. You can +execute it via 'make run/hello' from the build directory. + +Note that currently, all boot modules are linked against the core binary. +To change the boot modules, the file 'base-mb/src/core/boot_modules.s' must +be modified. + +For reference, we are using the following tools: + +* mb-g++ (GCC) 4.1.1 20060524 (Xilinx 11.2 Build EDK_LS2.2 + 20 Apr 2009 Xilinx 11.2 Build EDK_LS2.2 23 Apr 2009) +* GNU ld version 2.16 Xilinx 11.2 Build EDK_LS2.2 23 Apr 2009 +* GNU assembler 2.16 Xilinx 11.2 Build EDK_LS2.2 23 Apr 2009 +* QEMU emulator version 0.14.50, Copyright (c) 2003-2008 Fabrice Bellard + Petalogix linux reference design targeting Xilinx Spartan 3ADSP-1800 boards. + + +Supporting the NOVA hypervisor version 0.3 +########################################## + +NOVA is a so called microhypervisor - a modern capability-based microkernel +with special support for hardware-based virtualization and IOMMUs. Since we +incorporated the initial support for the NOVA hypervisor in Genode one year +ago, this kernel underwent multiple revisions. The latest version was released +earlier this month. To our delight, much of the features that we missed from +the initial release had been implemented during the course of the last year. We +are especially happy about the fully functional 'revoke' system call and the +support for remote kernel-object creation. + +With the Genode release 11.02, we officially support the latest NOVA version. +The update of Genode to the new version required two steps. First, because many +details of the kernel interface were changed between version 0.1 and version +0.3, we had to revisit our syscall bindings and adapting our code to changed +kernel semantics. Second, we filled our 'base-nova' code related to object +destruction and unmapping with life to benefit from NOVA's 'revoke' system +call. Consequently, we are now able to run the complete Genode software stack +including the dynamic linker on NOVA. + +Note that for using Genode on NOVA, you will need to apply a small patch to the +NOVA source code. This patch enables the re-use of user-level thread control +blocks in the kernel. The patch can be found at 'base-nova/patches/utcb.patch'. + +When executing NOVA on qemu, please specify the '-cpu coreduo' argument to the +qemu command line. When using Genode 'run' tool, you may assign this argument +to the 'QEMU_OPT' variable in '/etc/build.conf'. + +:Thanks: + + We are grateful for the ongoing very pleasant collaboration with Udo Steinberg + who is the driving force behind NOVA. Thanks for the ultra-fast responses to our + questions and for considering our suggestions regarding the feature set of + NOVA's kernel interface! + + +Base framework +############## + +Upgrading existing sessions +=========================== + +Genode enables a client of a service to lend parts of its own resources to +the service when opening a session. This way, servers do not need to allocate +own resources on behalf of their clients and become inherently robust against +resource-exhaustion-based denial-of-service attacks. + +However, there are cases when the client can not decide about the amount of +resources to lend at session-creation time. In such cases, we used to devise an +overly generous client policy. Now, we have added a new 'upgrade' function to +the 'Parent' and 'Root' interfaces that enables a client to upgrade the +resources of an existing session. + +For the 'env()->rm_session()' and 'env()->ram_session()' of processes using +the Genode 'env' library, we implemented a transparent quota upgrade that kicks in +in the event of an exceeded metadata backing store. + + +Comprehensive accounting of core resources +========================================== + +We changed all services of core to limit their respective resource usage +specifically for each individual session. For example, the number of dataspaces +that can be handled by a particular region-manager (RM) session depends on the +resource donation attached to the session. To implement this accounting scheme +throughout core, we added a generic 'Allocator_guard' utility to +'base/include/'. We recommend using this utility when implementing resource +multiplexers, in particular multi-level services. Thanks to this change in +core, the need for a slack memory reservation in core has vanished. + + +Various changes +=============== + +The remaining parts of the base API underwent no fundamental revision. The +changes are summarized as follows. + +:C++ Support: + + We removed 'libgcc' from our C++ support library ('cxx') and link + it to each individual final target and shared library instead. This change alleviates + the need to abuse the 'KEEP_SYMBOLS' mechanism that we used in 'cxx' to + keep libc-dependencies of GCC's support libraries local to the 'cxx' + library. Besides the benefit of reducing heuristics, this change improves + the compatibility with recent cross-compiling tool chains. + Furthermore, we added 'realloc' to the local libc support of the 'cxx' + library because recent ARM tool chains tend to use this function. + +:Argument handling for 'main()': + + We added a hook to the startup code to enable the implementation of + custom facilities for passing arguments to the main function. The + hook uses the global variables 'genode_argc' and 'genode_argv'. + +:Child-exit policy hook: + + We enhanced the 'Child_policy' with a new policy interface that allows + a simplified implementation of policies related to program termination. + +:Changed API of 'Range_allocator': + + We changed the return value of 'alloc_addr' to distinguish different error + conditions. Note that the boolean meaning of the return value is inverted. + Please check your uses of 'alloc_addr'! + + +Operating-system services and libraries +####################################### + +C Runtime +========= + +In conjunction with our work on Noux, we improved Genode's C runtime at many +places. First, we added libstdtime and some previously missing bits of libgdtoa +to the libc. These additions largely alleviate the need for dummy stubs, in +particular time-related functions. Second, we added the following functions to +our libc plugin interface: 'dup2', 'fchdir', 'fcntl', 'fstat', 'stat', and +'write'. This enables the creation of advanced libc plugins simulating a whole +file system as done with Noux. Still, there are a number of dummy stubs found +at 'libc/src/lib/libc/dummy.cc'. However, those stubs are now all defined as +weak symbols such that they can be overridden by libc plugins. Finally, we have +replaced the original 'exit' implementation that comes with the libc with a +Genode-specific version. The new version reports the exit code of the +application to the parent process via an 'Parent::exit()' RPC call. + +Until now, Genode's libc magically handled output to stdout and stderr by +printing messages via Genode's LOG interface. We have now replaced this +hard-wired interface by an optional libc plugin called 'libc_log'. If present, write +operations to stdout are caught at the libc plugin interface and delegated to +the plugin, which implements the output to the LOG interface. If you have an +application using Genode's libc, you might consider adding the 'libc_log' +library to your 'target.mk' file. + + +Support for big numbers by the means of libgmp and libmpfr +========================================================== + +We have now include both the GNU Multiple Precision Arithmetic Library and +(GMP) and MPFR to the 'ports' repository. This work was specifically motivated +by our port of GCC to Genode as GCC version 4.4.5 requires both libraries. +Because we intend to use those libraries primarily on x86_32, the current port +covers only this architecture. However, expanding the port to +further CPU architectures should be straight-forward if needed. + +Furthermore, you can now also find GCC's 'longlong.h' header at +'libports/include/gcc'. + + +Qt4 updated to version 4.7.1 +############################ + +The current release bumps the supported Qt4 version from 4.6.2 to 4.7.1 and the +Arora web browser (located at the ports repository) from version 0.10.2 to +version 0.11. Of course, we updated our custom additions such as our custom +Nitpicker plugin widget that enables the seamless integration of native +Nitpicker GUI clients into Qt4 applications to work with the new Qt4 version. + + +Tools +##### + +Tool chain update to GCC 4.4.5 and Binutils 2.21 +================================================ + +We upgraded the official Genode tool chain from gcc 4.2.4 to gcc 4.4.5. Please +update your tool chain by downloading the new binary archive (available for x86_32) +or building the tool chain from source using our 'tool/tool_chain' utility. + +New support for automated integration and testing +================================================= + +With the growing number of supported base platforms, the integration and testing +of Genode application scenarios across all kernels becomes +increasingly challenging. Each kernel has a different boot mechanism and +specific requirements such as the module order of multiboot modules (Fiasco's +bootstrap, Pistachio's sigma0 and kickstart), kernel parameters, or the +invocation of a single-image creation tool (OKL4's elfweaver). To make our +life supporting all those platforms easier, we have created a tool called +'run', which is tightly integrated within Genode's build system. In short 'run' +gathers the intrinsics in the form of a 'run/env' file specific for the +platform used by the current build directory from the respective +'base-' repository. It then executes a so-called run script, which +contains all steps needed to configure, build, and integrate an application +scenario. For example, a typical run script for building and running a test +case resides in a file called '/run/.run' and +looks as follows: + +! build "core init test/exception" +! create_boot_directory +! install_config { +! +! +! +! +! +! +! +! +! +! +! +! +! } +! build_boot_image "core init test-exception" +! append qemu_args "-nographic -m 64" +! run_genode_until {.*Exception \(label 0xffb0\) occured.*} 10 + +First, the build system is instructed to create the targets specified as argument +for the 'build' function. Next, for the integration part, a new boot directory is +created. On most kernel platform, the respective location of the boot directory +is '/var/run/'. Initially, this directory is empty. +It gets populated with a 'config' file specified as argument of the 'install_config' +command, and by the boot modules specified at the 'build_boot_image' command. +Now that the integration is complete, the scenario is executed via the +'run_genode_until' command. This command takes a regular expression as +argument, which determines the successful termination of the test case. The +second argument is a timeout (is seconds). In the example, the test case will +fail if its output does not match the regular expression within the execution +time of 10 seconds. + +The command 'append qemu_args' specifies run-script-specific qemu arguments in +the case that qemu is used to execute the scenario. This is the case for most +kernel platforms (except for Linux where core gets executed directly on the host). +Additional build-directory-specific qemu arguments can be specified in the +'etc/build.conf' file by defining the 'QEMU_OPT' variable. For example, to +prevent KVM being used on Ubuntu Linux, specify: + +! QEMU_OPT = -no-kvm + +To execute the run script from with build directory, you need to have Expect +installed. Typically, the Linux package is called 'expect'. Simply issue +the following command from within your build directory: + +! make run/ + +Note that you will need to have a GRUB 'stage2_eltorito' binary available +at '/tool/grub' on base platforms that use an ISO image as boot +stategy. + +Because the whole chain of actions, building, integrating, executing, and +validating an application scenario is now at the fingertips of issuing a +single command with no kernel-specific considerations needed, it has never +been easier to run the same scenario on a wide range of different kernels. +Please find further instructive examples at 'os/run/'. The 'ldso' run +script executes the test of the dynamic linker. It is completely generic. +The 'demo' run script starts Genode's default demo scenario and shows how +platform-specific considerations (e.g., which device drivers to use) can be +taken into account. + +We found that the 'run' tool significantly boosted our productivity not +only for testing purposes but also for accelerating the development-test +cycle during our day-to-day work. + +:Technical notes: + +The 'run' tool uses Expect as automation tool. Expect is a Tcl interpreter, +which is accompanied by special functionality for automating interactive +command-line applications. Technically, a run script is an Expect script +which gets included by the 'tool/run' script. For the reference of +run-specific functions, please revise the documentation in the 'tool/run' +script. Because each run script is actual Expect source code, it is possible +to use all Tcl and Expect scripting features in a run script. +In particular, a run script may issue shell commands using Tcl's 'exec' +function. This way, even complex integration tasks can be accomplished. +For example, the integration of the Genode Live CD was done via a single +run script. + + +Build system +============ + +To facilitate the integration of 3rd-party build systems into the Genode build +process, we added support for pseudo targets that do not require any 'SRC' +declaration. Such 'target.mk' may contain custom rules that will be executed +when the target is revisited by the build system. The bindings are as follows: + +! build_3rd_party: +! ...custom commands... +! +! $(TARGET): build_3rd_party +! +! clean_3rd_party: +! ...custom commands... +! +! clean_prg_objects: clean_3rd_party: + + diff --git a/doc/release_notes-11-05.txt b/doc/release_notes-11-05.txt new file mode 100644 index 000000000..1da35addc --- /dev/null +++ b/doc/release_notes-11-05.txt @@ -0,0 +1,1289 @@ + + + =============================================== + Release notes for the Genode OS Framework 11.05 + =============================================== + + Genode Labs + + + +With our work on Genode 11.05, we pursued two missions, substantiating the +support for the base platforms introduced with the last release, and +reconsidering one of the most fundamental aspects of the framework, which is +inter-process communication. Besides these two main topics, we enjoyed working +on a number of experimental features such as GDB support that will hopefully +have far-reaching effects on how our framework is used. + +Cross-kernel platform support is certainly one of the most distinctive features +that sets Genode apart from other operating-system development kits. With the +previous version 10.02, we had proudly announced having bumped the number of +supported base platforms to 8 different kernels. Since this release, the two +new base platforms received a lot of attention. We not only advanced the +support for the Fiasco.OC kernel to catch up featurewise with the other +platforms but went on with porting its most prominent application, namely +L4Linux, to Genode. L4Linux is a paravirtualized version of the Linux kernel +specifically developed to run as user-level application on top of Fiasco.OC. +Now L4Linux can be used with Genode on both x86 and ARM. The second addition to +the base platforms was our custom kernel implementation for the Xilinx +MicroBlaze architecture. For this platform, we have now activated the APIs for +creating user-level device drivers, and introduced a reference SoC for +executing Genode on the Xilinx Spartan-3A Starter Kit. + +Getting inter-process communication right is possibly the most serious concern +of microkernel-based operating systems. When Genode was started in 2006, we +disregarded the time-tested standard solution of using interface description +languages and IDL compilers. Well, we never looked back. Genode devised the use +of standard C++ features combined with simple object-oriented design patterns. +Even though we regarded our approach as a great leap forward, it had some +inherent shortcomings. These were the lack of type safety, the need for +manually maintaining communication code, and the manual estimation of +communication-buffer sizes. The current release remedies all these shortcomings +with a brand new API for implementing procedure calls across process +boundaries. This API facilitates type safety and almost eliminates any manual +labour needed when implementing remote procedure calls between processes. Yet, +the concept still relies only on the C++ compiler with no need for additional +tools. + +As the Genode developer community grows, we observe the rising need for a solid +debugging solution. The new release features our first step towards the use of +the GNU debugger within our framework. In addition to the progress on the +actual framework, we are steadily seeking ways to make Genode more easily +accessible to new developers. We have now added new ready-to-use scripts for +building, configuring, and test-driving a number of Genode features including +Qt4, lwIP, GDB, and L4Linux. + + +New API for type-safe inter-process communication +################################################# + +Efficient and easy-to-use inter-process communication (IPC) is crucial for +multi-server operating systems because on such systems, almost all of the +functionally offered by a traditional monolithic kernel is provided by a crowd +of different user-level processes talking to each other. Whereas the L4 line of +microkernels took the lead in terms of IPC performance, the development of +applications for such platforms and dealing with the kernel mechanisms in +particular is not easy. Hence, for most microkernels, there exists tooling +support to hide the peculiarities of kernel mechanisms behind higher-level +interface description languages (IDL). However, in our past experience, the +introduction of an IDL compiler into the tool chain of a multi-server OS did +not only bring comfort, but also serious headaches. The two most prominent +problems are the unfortunate mixing of abstraction levels and the complexity of +the solution. + +Even though IDL compilers are a time-tested solution for distributed systems, +we argue that applying them to kernel-level systems programming is misguided. +On the one hand, IDLs such as CORBA IDL suggest a lot power (e.g., the ability +to communicate arbitrarily complex data types), which microkernel-targeting IDL +compilers fail to deliver because of kernel interface constraints +(e.g., hard limits with regard to message sizes). On the other hand, IDL +per se misses expressions and functionality important to OS development such as +easy-to-use bindings to a systems programming language, fine-tuned resource +allocation, or the transfer of special IPC items. Therefore, most IDL compilers +used for microkernels sport various extensions or even do crazy things like +retrieving type definitions from C header files. + +With the rich feature set demanded by application developers, some IDL +compilers have become extremely complex, i.e., comprising more than 60K lines +of code. Furthermore, the integration of an IDL compiler into the tool chain +implies build-system complexity. Also the stub codes generated by an IDL +compiler must be taken into consideration and raise the question of whether +they must by regarded as part of the trusted computing base and, therefore, +become subject to human review. + +For these reasons, Genode dismissed the IDL approach in favor for a raw +C++-based alternative, fostering the use of the C++ streaming operators +combined with templates. The following paper provides a detailed discussion +on the subject: + +:[http://genode-labs.com/publications/dynrpc-2007.pdf - A Case Study on the Cost and Benefit of Dynamic RPC Marshalling for Low-Level System Components]: + _SIGOPS OSR Special Issue on Secure Small-Kernel Systems, 2007_ + +In hindsight, leaving behind the IDL approach was the right decision. From a +developer's perspective, there is no need to comprehend two levels of +abstraction - one systems programming language should be enough. Genode's IPC +framework has raw and direct semantics without hidden magic. Still the IPC +framework is abstract enough to remain extremely portable. The same API works +seamless across 8 different kernels using different flavours of IPC mechanisms. + +That said, our solution was never exempt from valid criticism, which we try +to remedy with the Genode version 11.05. + + +State of the art +================ + +Genode provides three ways of inter-process communication: signals, shared +memory, and synchronous remote procedure calls (RPC). In the following, only +remote procedure calls are discussed. An RPC in the context of Genode is a +function call to a remote process running on the same machine (contrarily to +the term RPC being used in the context of systems distributed over a network). + +The state of the art is best explained by the example interface discussed +in the [http://genode.org/documentation/developer-resources/client_server_tutorial - Hello Tutorial]. +On Genode, each RPC interface is represented by an abstract C++ class, +enriched by some bits of information shared by the caller and the callee. + +! class Session +! { +! protected: +! +! enum Opcode { SAY_HELLO = 23, ADD = 42 }; +! +! public: +! +! virtual void say_hello() = 0; +! virtual int add(int a, int b) = 0; +! }; + +On the callee side, each function is represented by a number (opcode). To let +both caller and callee talk about the same opcodes, the interface class hosts +an 'Opcode' enumeration with each value corresponding to one RPC function. + +On the callee side, the interface is inherited by a so-called 'Server' class +with the purpose of dispatching incoming RPC requests and directing them to the +respective server-side implementation of the abstract RPC interface. + +! struct Session_server : Server_object, +! Session +! { +! int dispatch(int op, Ipc_istream &is, +! Ipc_ostream &os) +! { +! switch(op) { +! +! case SAY_HELLO: +! say_hello(); +! break; +! +! case ADD: +! { +! int a = 0, b = 0; +! is >> a >> b; +! os << add(a,b); +! break; +! } +! +! default: +! return -1; +! } +! return 0; +! } +! }; + +The 'Server' class is further inherited by the actual implementation of the +callee's functions. By using this class-hierarchy convention, the 'Server' +dispatch code can be reused by multiple implementations of the same interface. + +The caller-side of the RPC interface is represented by a 'Client' class, +implementing the 'Session' interface using Genode's IPC streaming API, namely +an 'Ipc_client' object. + +! class Session_client : public Session +! { +! protected: +! +! Msgbuf<256> _sndbuf, _rcvbuf; +! Ipc_client _ipc_client; +! Lock _lock; +! +! public: +! +! Session_client(Session_capability cap) +! : _ipc_client(cap, &_sndbuf, &_rcvbuf) { } +! +! void say_hello() +! { +! Lock::Guard guard(_lock); +! _ipc_client << SAY_HELLO << IPC_CALL; +! } +! +! int add(int a, int b) +! { +! Lock::Guard guard(_lock); +! int ret = 0; +! _ipc_client << ADD << a << b << IPC_CALL >> ret; +! return ret; +! } +! }; + +Even though this scheme is relatively easy to follow and served us well over +the years, it has several drawbacks: + +:Consistency between 'Client' and 'Server' stub codes: + + The developer is responsible to manually maintain the consistency between the + 'Client' and 'Server' classes. For the mapping of opcodes to functions, the + naming convention of letting the enum names correlate to uppercase function + names is just fine. But there is no easy-to-follow convention for function + arguments. Care must be taken to let both 'Client' and 'Server' stream the + correct argument types in the same order. In practice, maintaining the + correlation between 'Client' and 'Server' stub code is not too hard because + the stub code is easy to comprehend and to test. However, in some cases, + errors slipped in and remained undetected for some time. For example, a + client inserting an 'int' value and a server extracting a 'long' value play + nicely together as long as they are executed on 32-bit machines. But on 64 + bit, the communication breaks down. + +:Manual dimensioning of message buffers: + + The 'Ipc_client' message buffers must be dimensioned correctly. Choosing them + too small may lead to corrupted RPC arguments. Too large buffers waste + memory. Because arguments are differently sized on different architectures, + numerically specified buffer sizes are always wrong. Because expressing + the buffer size with a proper accumulation of 'sizeof()' values is awkward to + do manually, message buffers tend to get over dimensioned. + +:Locking of message buffers: + + Because one 'Client' object may be concurrently accessed by multiple threads, + precautions for thread safety must be taken by protecting the message buffers + with lock guards. Of course, the implementation effort is not too high, but + a missing lock guard can take hours to spot once a weird race condition occurs. + +:Danger of using anonymous enums for defining opcodes: + + The compiler is free to optimize the size of values of anonymous enums. Small + values may be represented as 'char' whereas larger values may use 'int'. On + the callee side, the opcode is always extracted into an 'int' variable. + Hence, the client must insert an 'int' value as well, which is not guaranteed + for anonymous enums. Unfortunately, the 'Opcode' type is never explicitly + used, so that a missing type name is not detected at compile time. + +:Exception-support possible but labour intensive: + + Several of Genode's interfaces indicate error conditions via C++ exceptions. + The propagation of exceptions via IPC is pretty straight forward. On the + callee-side, the dispatch code must catch each exception known to be thrown + from the implementation and translate each exception type to a unique return + value. If such a return value is received at the caller side, the 'Client' + stub code throws the respective exception. Similar to the streaming of + function arguments, the corresponding code is easy to craft, yet it must be + maintained manually. + + +Re-approaching the problem using template meta programming +========================================================== + +When we introduced Genode's C++-stream-based dynamic RPC marshalling in 2006, +we were hinted by Michael Hohmuth to the possibility of automatic generation +of the stub code via recursive C++ templates. However, back then, neither Michael +nor we had the profound understanding of the programming technique required to +put this idea into practice. However, the idea kept spinning in our heads - until +today. + +Last year, we finally realized a prototype implementation of this idea. To our +excitement, we discovered that this technique had the potential to remedy all +of the issues pointed out above. With the current release, this powerful +technique gets introduced into the Genode API. Because this new API would break +compatibility with our existing IPC and client-server APIs, we took the chance +to closely examine the use cases of these APIs, and re-consider their feature +sets. Our findings are: + +* The distinction between the IPC API ('ipc.h') and the client-server API + ('server.h') turned out to be slightly over designed. Originally, the IPC + API was meant as a mere abstraction to the low-level IPC mechanism of the + kernel (sending and receiving messages) whereas the server API adds the + object model. However, there is no single use case for the stand-alone use of + the IPC API except for a bunch of test cases specifically developed for the + IPC API implementations. Furthermore, half of the IPC API namely send-only + IPC and wait-only IPC remained unused, and on some base platforms (e.g., + NOVA) even unsupported. Consequently, we see potential to simplify the IPC + API by sticking to raw function-call semantics. + +* The use of C++ streams for marshalling/unmarshalling suggests an enormous + flexibility. E.g., by overloading the operators for specific types, complex + nested data structures could be transferred. However, this never happened - + for the good reason that we always strive to keep the RPC interfaces of OS + services as simple and straight-forward as possible. If the payload becomes + complex, we found that the use of synchronous RPCs should be reconsidered + anyway. For such use cases, shared memory is the way to go. On the other + hand, the possibility of overloading the stream operators turned out to be + extremely useful for handling platform-specific IPC payload, most prominently + kernel-protected capabilities on NOVA and Fiasco.OC. So we will stick with + the C++-stream based marshalling/unmarshalling. + +* The inheritance of RPC interfaces is an important feature to support + platform-specific extensions to Genode's core services. For example, on + Linux, an extension to the 'Dataspace' interface provides additional + information about the file that is used as backing store. On OKL4, the + extension of core's PD services provides OKL4-specific functions that where + added to run OKLinux on Genode. Consequently, the support for interface + inheritance is a must. + +* The typed capabilities introduced with Genode 8.11 formed an inheritance + hierarchy independent from the actual interfaces. By convention, typed + capabilities were tagged with their corresponding interface classes but their + inheritance relationship was explicitly expressed by an additional template + argument. For this reason, the definition of each capability type had to + be provided via a separate header file (named 'capability.h') for each + interface. It would be much nicer to just use the class relationships between + interfaces to infer the corresponding capability-type relationships. + +* Allowing RPC functions to throw exceptions is crucial. In fact, our + goal is to design RPC interfaces in C++ style as far as possible. If + throwing an exception fits naturally into the API, the framework should + not stand in the way. Consequently, C++ exception support for the RPC + framework is a must. + +* The separation of 'Server_activation' and 'Server_entrypoint' never + paid off. The 'Server_activation' represents the thread to be used + by a 'Server_entrypoint'. The original design of the NOVA hypervisor + envisioned the use of multiple "worker" activations to serve one entry point. + Our API tried to anticipate this kernel feature. In the meanwhile, two + reasons are speaking against this idea. No other kernel supports such a + feature, so using the feature by an application would spoil it's inter-kernel + portability. Second, even the NOVA developers disregarded this feature at a + later development stage. In summary, merging both 'Server_activation' and + 'Server_entrypoint' looks like a good idea to simplify Genode's API. + +Even though the revised RPC API promised to be a vast improvement over the +original IPC and client-server APIs, the risks of such a huge overhaul must be +considered as well. We are aware of developers with reservations about the use +of C++ template meta programming. It seems to be common sense that this +technique is some kind of witch craft, the code tends to be ugly, the compiler +takes ages to cut its teeth through the recursive templates, and the resulting +binaries become bloated and large. If any of these arguments had held true, we +would not have introduced this technique into Genode. Admittedly, the syntax of +template meta code is not always easy to comprehend but we believe that +elaborative comments in the code make even these parts approachable. + + +Introduction of the new API +=========================== + +The new RPC API completely replaces the formerly known IPC ('base/ipc.h') +and client-server ('base/server.h') APIs. It consists of the following +header files: + +:'base/rpc.h': + Contains the basic type definitions and utility macros to declare RPC + interfaces. It does not depend on any other Genode API except for the + meta-programming utilities provided by 'util/meta.h'. Therefore, 'base/rpc.h' + does not pollute the namespace of the place where it is included. + +:'base/rpc_args.h': + Contains definitions of non-trivial argument types used for transferring + strings and binary buffers. Its use by a RPC interface is entirely optional. + +:'base/rpc_server.h': + Contains the interfaces of the server-side RPC API. This part of the API + consists of the 'Rpc_object' class template and the 'Rpc_entrypoint' class. + It entirely replaces the original 'base/server.h' API ('Rpc_object' + corresponds to the original 'Server_object', 'Rpc_entrypoint' corresponds to + the original 'Server_activation' and 'Server_entrypoint' classes. + +:'base/rpc_client.h': + Contains the API support for invoking RPC functions. It is complemented by + the definitions in 'base/capability.h'. The most significant elements of the + client-side RPC API are the 'Capability' class template and 'Rpc_client', + which is a convenience wrapper around 'Capability'. + +That sounds simple enough. Let's see how to use this API for the example of +Section [State of the art]. + +The RPC interface is still an abstract C++ interface, supplemented by some bits +of RPC-relevant information. + +! #include +! +! struct Session +! { +! virtual void say_hello() = 0; +! virtual int add(int a, int b) = 0; +! +! GENODE_RPC(Rpc_say_hello, void, say_hello); +! GENODE_RPC(Rpc_add, int, add, int, int); +! GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add); +! }; + +Note that the 'Opcode' enum is gone. Instead there is an RPC interface +declaration using the 'GENODE_RPC' and 'GENODE_RPC_INTERFACE' macros. These +macros are defined in 'base/rpc.h' and have the purpose to enrich the interface +with type information. They are only used at compile time and have no effect on +the run time or the size of the interface class. Each RPC function is +represented as a type. In this example, the type meta data of the 'say_hello' +function is attached to the 'Rpc_say_hello' type within the scope of 'Session'. +The macro arguments are: + +! GENODE_RPC(func_type, ret_type, func_name, arg_type ...) + +The 'func_type' argument is an arbitrary type name (except for the type name +'Rpc_functions') used to refer to the RPC function, 'ret_type' is the return +type or 'void', 'func_name' is the name of the server-side function that +implements the RPC function, and the list of 'arg_type' arguments comprises the +RPC function argument types. The 'GENODE_RPC_INTERFACE' macro defines a type +called 'Rpc_functions' that contains the list of the RPC functions provided by +the RPC interface. + +On the server side, the need for the 'Server' class has vanished. Instead, the +server-side implementation inherits 'Rpc_object' with the interface type as +arguments. + +! #include +! +! struct Component : Rpc_object +! { +! void say_hello() +! { +! ... +! } +! +! int add(int a, int b) +! { +! ... +! } +! }; + +The RPC dispatching is done by the 'Rpc_object' class template, according to +the type information that comes with the 'Session' interface. + +On the client-side, there is still a '/client.h' file, but it has +become significantly shorter. + +! #include +! +! struct Session_client : Rpc_client +! { +! Session_client(Capability cap) +! : Rpc_client(cap) { } +! +! void say_hello() { +! call(); } +! +! int add(int a, int b) { +! return call(a, b); } +! }; + +There are a few notable things. First, 'Capability' is now a template class +taking the interface type as argument. So in principle, there is no more a +pressing need to explicitly define a dedicated capability type for each +interface. Second, the message buffer declarations are gone. Message buffers +are dimensioned automatically at compile time. Third, there is no manual +application of the C++ stream operator. Instead, the 'call' function template +performs the correct marshalling and unmarshalling in a type-safe manner. Type +conversion rules correspond to the normal C++ type-conversion rules. So you can +actually pass a char value to a function taking an int value. If there is no +valid type conversion or the number of arguments is wrong, the error gets +detected at compile time. Finally, there no more any need for locking message +buffers. Very similar to the way, plain function calls work, the 'call' +mechanism allocates a correctly dimensioned message buffer on the stack of the +caller. The message buffer is like a call frame. By definition, a call frame +cannot be used by multiple thready concurrently because each thread has its own +stack. + + +Transferable argument types +=========================== + +The arguments specified to 'GENODE_RPC' behave mostly as expected by a normal +function call. But there are some notable differences to keep in mind: + +:Value types: + Value types are supported for basic types and plain-old-data types + (self-sufficient structs or classes). The object data is transferred as such. + If the type is not self sufficient (it contains pointers or references), the + pointers and references are transferred as plain data, most certainly + pointing to the wrong thing in the callee's address space. + +:Const references: + Const references behave like value types. The referenced object is + transferred to the server and a reference to the server-local copy is passed + to the server-side function. Note that in contrast to a normal function call + taking a reference argument, the size of the referenced object is accounted + for allocating the message buffer on the client side. + +:Non-const references: + Non-const references are handled similar to const references. In addition the + server-local copy gets transferred back to the caller so that server-side + modifications of the object become visible to the client. + +; Should we mention, that copy constructors/assignment opeerators of +; by-reference parameters may be called by the stream op, or do I miss +; something? + +:Capabilities: + Capabilities can be transfered as values, const references, or non-const + references. + +:Variable-length buffers: + There exists special support for passing binary buffers to RPC functions using + the 'Rpc_in_buffer' class template provided by 'base/rpc_args.h'. The maximum + size of the buffer must be specified as template argument. An 'Rpc_in_buffer' + object does not contain a copy of the data passed to the constructor, only a + pointer to the data. In contrast to a fix-sized object containing a copy of + the payload, the RPC framework does not transfer the whole object but only + the actually used payload. + +:Pointers: + Pointers and const pointers are handled similar to references. The pointed-to + argument gets transferred and the server-side function is called with a + pointer to the local copy. *Note* that the semantics of specifying pointers + as arguments for RPC interface functions is not finalized. We may decide to + remove the support for pointers to avoid misconceptions about them (i.e., + expecting 'char const *' to be handled as a null-terminated string, or + expecting pointers to be transferred as raw bits). + +; IMO 'Type *out_param' fits better than 'Type &out_param' because of +; the copy constructor issue, right? + +All types specified at RPC arguments or as return value must have a default +constructor. + +By default, all RPC arguments are input arguments, which get transferred to the +server. The return type of the RPC function, if present, is an output-only +value. To avoid a reference argument from acting as both input- and output +argument, a const reference should be used. Some interfaces may prefer to +handle certain reference arguments as output-only, e.g., to query multiple +state variables from a server. In this case, the RPC direction can be defined +specifically for the type in question by providing a custom type trait +specialization for 'Trait::Rpc_direction' (see 'base/rpc.h'). + + +Supporting advanced RPC use cases +================================= + +Two advanced use cases are important to mention, throwing exceptions across RPC +boundaries and interface inheritance. + +:C++ exceptions: + + The propagation of C++ exceptions from the server to the client is supported + by a special variant of the 'GENODE_RPC' macro: + + ! GENODE_RPC_THROW(func_type, ret_type, func_name, + ! exc_type_list, arg_type ...) + + This macro features the additional 'exc_type_list' argument, which is a type + list of exception types. To see this feature at work, please refer to + Genode's base interfaces such as 'parent/parent.h'. Exception objects are not + transferred as payload - just the information that the specific exception was + raised. Hence, information provided with the thrown object will be lost + when crossing an RPC boundary. + +:Interface inheritance: + + The inheritance of RPC interfaces comes down to a concatenation of the + 'Rpc_functions' type lists of both the base interface and the derived + interface. This use case is supported by a special version of the + 'GENODE_RPC_INTERFACE' macro: + + ! GENODE_RPC_INTERFACE_INHERIT(base_interface, + ! rpc_func ...) + + The 'base_interface' argument is the type of the inherited interface. For an + example, please refer to 'linux_dataspace/linux_dataspace.h' as contained in + the 'base-linux' repository. + +:Casting capability types: + + For typed capabilities, the same type conversion rules apply as for pointers. + In fact, a typed capability pretty much resembles a typed pointer, pointing + to a remote object. Hence, assigning a specialized capability (e.g., + 'Capability') to a base-typed capability (e.g., + 'Capability') is always valid. For the opposite case, a static cast + is needed. For capabilities, this cast is supported by + ! static_cap_cast(cap) + + In rare circumstances, mostly in platform-specific base code, a reinterpret + cast for capabilities is required. It allows to convert any capability to + another type: + ! reinterpret_cap_cast(cap) + +:Non-virtual interface functions: + + It is possible to declare RPC functions using 'GENODE_RPC', which do not + exist as virtual functions in the interface class. In this case, the function + name specified as third argument to 'GENODE_RPC' is of course not valid for + the interface class but an alternative class can be specified as second + argument to 'Rpc_object'. This way, a server-side implementation may specify + its own class to direct the RPC function to a local (possibly non-virtual) + implementation. This feature is used to allow the RPC function to have a + slightly different semantic as the actual C++ interface function. For + example, an interface may contain a function taking a 'char const *' as + argument and expecting a null-terminated string. When specifying this type as + 'GENODE_RPC' argument, the RPC framework will not know about the implied + string semantics and just transfer a single character. In this case, the + 'GENODE_RPC' function may use a 'Rpc_in_buffer' (defined in 'rpc_args.h') + instead and refer to a differently named server-side function (e.g., using a + '_' prefix). On the server side, the 'Rpc_in_buffer' argument can then be + converted to the function interface expected by the real server function. + + +Typed capabilities, typed root interfaces +========================================= + +The consistent use of typing 'Rpc_object', 'Capability', and 'Rpc_client' with +interface type has paved the way to further type-safety goodness. Since there +now is a 1:1 relationship between each 'Rpc_object' type and a 'Capability' +type, the 'Rpc_entrypoint' has become able to propagate this type information +through the 'manage' function. A capability returned by 'manage' is now +guaranteed to refer to the same interface as the 'Rpc_object' argument. If such +a capability is transferred as argument of an RPC function through the new +type-safe argument marshalling, the receiver will obtain the correct capability +type. The only current exception is the handling of session capabilities +transferred through the parent interface. But also this use case greatly +benefits from the now type-enriched capabilities. + +For the propagation of session capabilities, there are two transitions visible +to the application developer: The way a service is announced at the parent and +the way a session is requested from the parent. For announcing a service, +the parent's 'announce' function is used, which takes the service name and +a root capability as argument. +! env()->parent()->announce(Hello::Session::service_name(), +! Root_capability(ep.manage(&root))); +With Genode 11.05, is has become possible to tag 'Root' interfaces with their +respective session types using the 'Typed_root' template defined in +'root/root.h'. By combining typed capabilities with typed root interfaces, the +'Parent' class has become able to provide a simplified 'announce' function, +taking only a root capability as argument and inferring the other information +needed: +! env()->parent()->announce(ep.manage(&root)); +This way, the type of the root interface gets propagated through the 'manage' +function right into the 'Parent' interface. + +The request of sessions from the parent is almost exclusively performed by +so-called 'Connection' objects, which are already typed in the original API. + + +Migration path +============== + +The new RPC API is the most fundamental API change in Genode's history. In such +a case, breaking API compatibility is inevitable. The question is how to make +the migration path to the new API as smooth as possible. We are confident to +have found a pretty good answer to this question. + +Immediate incompatibilities +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For the time being, the new API complements the existing API so that code +relying on the IPC and client-server APIs will largely continue to work until +the old APIs will be removed with the Genode version 11.08. So the immediate +incompatibilities come down to the following: + +* 'Capability' has become a template. The original untyped 'Capability' class + interface is available as 'Untyped_capability'. Within the 'base-' + repositories, the content of 'base/capability.h' moved over to + 'base/native_types.h' and is now called 'Native_capability'. + 'Untyped_capability' and 'Native_capability' are equivalent. The latter type + is meant to be used in low-level code that interacts with the + platform-specific capability members. In contrast, 'Untyped_capability' is + used in places where the type of the capability can be left unspecified. Both + types are rare in Genode's API and their use in application code is + discouraged. For now, the old 'Typed_capability' is equivalent to the new + 'Capability'. + +* To implement the strict consistency between interface hierarchies and + capability hierarchies, all session interfaces must be derived from + 'Genode::Session' defined in 'session/session.h'. Only by adhering to this + rule, 'Capability' can be converted to 'Capability'. + +To make the transition to the API as seamless as possible, the new API reuses +(inherits) parts of the original interfaces. E.g., 'Rpc_entrypoint' has +'Server_entrypoint' as base class. Also, the original 'Server_entrypoint' can +deal with typed capabilities. + +Transition steps +~~~~~~~~~~~~~~~~ + +The steps required for the transition to the new API are almost contained +in the RPC interface's 'include/' directory. + +:Modifications in '/.h': + * Include the header 'base/rpc.h'. For a session interface, include + the header 'session/session.h' instead. + * Remove the opcode definition. + * Add the 'GENODE_RPC' and 'GENODE_RPC_INTERFACE' declarations to + the interface class. + +:Modifications in '/client.h>': + * Include the header '', remove the headers + 'base/lock.h', 'base/ipc.h'. + * Remove the member variables (message buffer, lock, ipc-client + object). Now that there are no longer any private members, you may decide + to turn the 'class' into a 'struct'. + * Inherit the client class from 'Rpc_client' + * Pass 'Capability' to the constructor of + 'Rpc_client'. + * Replace the content of each interface function with + 'call(args...)'. + +:Modifications in '/server.h>': + In most cases, this file can be deleted. + +:Modifications in the implementation: + Replace base class '_server' by base class + 'Rpc_object'. + +Because the abstract C++ interface of the RPC interface has not changed, client +code does not require any changes. + + +Migration of Genode's interfaces +================================ + +Our original plan envisioned the migration of all of the base repositories to +the new RPC API, and thereby test the concept with many representative use +cases including the application of advanced features outlined above. To our +delight, the transition to the new API went far more smoothly than anticipated, +motivating us to look at the 'os' interfaces as well - with great success. The +following interfaces have been converted to use the new API: 'Cap_session', +'Cpu_session', 'Foc_cpu_session', 'Dataspace', 'Linux_dataspace', +'Io_mem_session', 'Io_port_session', 'Irq_session', 'Log_session', 'Parent', +'Pd_session', 'Okl4_pd_session', 'Foc_pd_session', 'Ram_session', 'Rm_session', +'Rom_session', 'Root', 'Session', 'Signal_session', 'Framebuffer_session', +'Input_session', 'Loader_session', 'Nitpicker_session', 'Nitpicker_view', +'Pci_device', 'Pci_session', 'Timer_session', and 'Noux_session'. Additionally, +several process-local RPC interfaces (e.g., in core, timer, nitpicker) have been +converted. Each of those interfaces worked instantly after modification and +fixing eventual compile errors. This overly positive experience greatly +supports our confidence in the new technique. Our goal was to not change the +original C++ interfaces. For this reason, some interfaces still rely on +server-side wrappers of the 'Rpc_object' class template. Those wrappers are +called '/rpc_object.h'. With the next release, we are going to +remove them altogether. The only interfaces not yet migrated are the users of +Genode's packet stream interface such as 'Nic_session', 'Audio_out_session', +and 'Block_session'. The conversion of those is subject to the next release. + + +Limitations +=========== + +The *maximum number of RPC function arguments* is limited to 7. +If your function requires more arguments, you may consider grouping +some of them in a compound struct. + +The *maximum number of RPC functions per interface* supported by the +'GENODE_RPC_INTERFACE' macro is limited to 9. In contrast to the limitation of +the number of function arguments, this limitation is unfortunate. Even in +core's base services, there is an interface ('cpu_session.h') exceeding this +limit. However, in cases like this, the limitation can be worked-around by +manually constructing the type list of RPC functions instead of using the +convenience macro: +! typedef Meta::Type_tuple > +! Rpc_functions; + +Both limitations exist because C++ does not support templates with variable +numbers of arguments. Our type-list implementation employed by the +'GENODE_RPC_INTERFACE' macro always takes a fixed number of arguments but +allows defaults for all of them. So the maximum number of arguments is +constrained. In C++0x, type lists are better supported, which will possibly +remove these limits and simplify the template code. + + +L4Linux +####### + +L4Linux is a user-level variant of the Linux kernel that can be executed as +plain user-level program on the Fiasco.OC microkernel combined with the L4Re +userland. The L4Linux kernel uses a paravirtualization technique and provides +binary compatibility with the Linux kernel. Since 1997, L4Linux is developed +and maintained by the OS Group at the University of Technology Dresden. Thanks +to the timely tracking of the upstream Linux kernel by L4Linux main developer +Adam Lackorzynski, the L4Linux kernel is particularly valued for being up to +date with the current version of the Linux kernel. As of today, L4Linux +corresponds to the kernel version 2.6.38. + +L4Linux is often regarded as one of the prime features of the Fiasco.OC +platform. Since Genode started to support Fiasco.OC with the previous release, +we desired to bring this virtualization solution to Genode running on this +kernel. Our L4Linux port is contained in the new 'ports-foc' repository. +Details about building and running L4Linux on Genode can be found in the +top-level README file within this repository. + +To keep our changes to L4Linux as minimal as possible, most parts of our +port come in the form of a library, which emulates the subset of the L4Re +userland semantics expected by L4Linux. This library can be found at +'ports-foc/src/lib/l4lx'. At the current stage, the kernel command line is +defined at 'startup.c'. The L4Re emulation approach turned out to be very +efficient with regard to the preservation of original L4Linux code. Excluding +the Genode-specific stub drivers for input and framebuffer, our patch +('ports-foc/patches/l4lx_genode.patch') consists of merely 650 lines. + + +Base framework +############## + +New support for template meta programming +========================================= + +As part of the work on the new RPC framework, several utilities for template +meta programming have been created. These utilities are available at +'base/include/util/meta.h'. Currently, this header file comprises the following +functionality: + +* Type traits for querying reference types, non-reference types, and stripping + constness from types +* Class templates for constructing type lists, namely 'Type_tuple' and + 'Type_list' +* Template meta functions for working with type lists, e.g., 'Length', + 'Index_of', 'Append', 'Type_at' +* N-Tuples aggregating members (both reference and plain-old-data members) + specified via a type list, called 'Ref_tuple_N' and 'Pod_tuple_N' +* Helper function templates for calling member functions using arguments + supplied in a N-tuple structure +* A helper for the partial specialization of member function templates, called + 'Overload_selector' + +To differentiate the meta-programming code from normal Genode APIs, all +utilities of 'util/meta.h' reside in a nested 'Meta' name space. + + +Thread state querying +===================== + +As a prerequisite for realizing our GDB monitor experiment described in Section +[GDB monitor experiment], we implemented the 'Cpu_session::state()' function +for OKL4, L4ka::Pistachio, and Fiasco.OC. Furthermore, the CPU session +interface have been extended with the functions 'pause' and 'resume', which +allow to halt and resume the execution of threads created via the CPU session. +The 'pause' and 'resume' functions are implemented for OKL4 only. + + +Misc +==== + +* We generalized the former architecture-specific 'touch' functions for + accessing memory (ro or rw). The new version is available at + 'base/include/util/touch.h'. + +* The constructor interfaces of the 'Process' and 'Child' classes have changed + to accommodate the RM session capability for the new process as an argument. + Originally, the RM session was magically created by the 'Process' class by + acquiring a new RM session from 'env()->parent()'. With the new interface, a + parent that needs to virtualize the RM session of its child can supply a + custom RM-session capability. + + +Operating-system services and libraries +####################################### + +Dynamic linker +============== + +To support dynamic linking on all platforms including Fiasco.OC, we +revisited our dynamic loader and changed its mode of operation. In the past, +the dynamic loader was a statically linked program executed by the 'process' +library if a dynamic binary was supplied as 'Process' argument. Because, the +dynamic loader is a normal Genode process, it initialized its Genode +environment on startup, and requested the dynamic binary as well as the +required shared libraries from its parent via ROM sessions. Finally, the +dynamic linker called the startup code of the dynamically linked program. This +program, in turn, initialized again an environment. Consequently, dynamically +linked programs used to employ two 'Genode::env()' environments, each backed +with the same 'RAM', 'RM', and 'CPU' sessions. On most platforms this slightly +schizophrenic nature of dynamically linked programs worked without problems. + +However, things became tricky on Fiasco.OC because on this kernel, the +environment contains parts that must be instantiated only once, namely the +allocator for kernel-capability selectors. Therefore, a way was desired to +remove the duplicated Genode environment. The solution is a scheme as used on +Linux. The dynamic linker is both, a shared library and a program. It contains +a single instance of the Genode environment. Each dynamic binary is linked +against the dynamic linker but not against the Genode base libraries that +normally provide the Genode environment. Now, each time the Genode environment +is referenced either by the dynamically linked program or another library, the +dynamic linker resolves the reference by returning its own symbols. + +This architectural change is pretty far reaching and changes the way the +dynamic linker is handled by the build system and at runtime. The user-visible +changes are the following: + +* The dynamic linker is not anymore a separate target. So the original + location at 'os/src/ldso' is no more. + +* The new dynamic linker is called 'ld.lib.so' and resides in + 'os/lib/ldso'. + +* To ensure that the dynamic linker gets built before linking any dynamic + binary, each shared library is implicitly made dependent on 'ld.lib.so'. + The build system takes care of that during the build process. But it + is important to know that the 'ld.lib.so' must also be provided as boot + module. + +* All programs that potentially create child processes must query the + dynamic linker with the new name 'ld.lib.so' instead of 'ldso'. + +The new dynamic linker has been tested on OKL4 (both x86 and ARM), +L4ka::Pistachio, Linux (both x86_32 and x86_64), Codezero, NOVA, Fiasco.OC +(x86_32, x86_64, and ARM), and L4/Fiasco. + + +Utilities for implementing device drivers +========================================= + +As the arsenal of native Genode device drivers grows, we observe code patterns +that are repeatedly used. To foster code reuse and minimize duplicated code, we +introduce the following new utilities and skeletons to the 'os' repository: + +:'os/attached_io_mem_dataspace.h': + + is a memory-mapped I/O dataspace that is ready to use immediately after + construction. This class wraps the creation of an IO_MEM connection, the + request of the IO_MEM session's dataspace, and the attachment of the + dataspace to the local address space. Even more important, this class takes + care of releasing these resources at destruction time. + +:'os/attached_ram_dataspace.h': + + was formerly known as 'os/ram_dataspace.h' works analogously to + 'os/attached_io_mem_dataspace.h', but for RAM dataspaces. This is + very handy for allocating DMA buffers. + +:'os/irq_activation.h': + + contains a code pattern found in almost each device driver that handles + interrupts. An 'Irq_activation' is a thread that is associated with the IRQ + specified as constructor argument. Each time, an IRQ occurs, a callback + 'handle_irq' is executed. Hence, a device driver implementing the callback + interface, can easily be connected to an IRQ. + +:'nic/driver.h': + + contains a set of interfaces to be used for implementing network device + drivers. The interfaces are designed in a way that enables the strict + separation of device-specific code and Genode-specific code. Note that + the interfaces are not yet finalized and lack some functions, in + particular those related to resource accounting. + +:'nic/component.h': + + contains ready-to-use glue code for integrating a network device driver into + Genode. The code takes care about implementing the 'Nic::Session_component' + and 'Nic::Root', parses session arguments and sets up the packet stream + between the client and the device driver. Note that this code is still in + flux and not yet optimized. Currently, only the new 'lan9118' driver makes + use of 'nic/component.h' but we are planning to move all other 'Nic' session + implementations over to this skeleton. + + +Device drivers +############## + +Because of the growing number of platforms and devices supported by Genode, we +improved the consistent use of the Genode build specs mechanism. Each device +driver does now depend on a dedicated spec value, which can selectively be +enabled by each platform as needed. For example, the PCI driver does now +depend on the 'pci' spec value. This value is present in the build 'SPECS' of +the various microkernels running on x86 hardware but not on the Linux base +platform or ARM platforms. + +New and improved device drivers are: + +:PL110 display controller: + The framebuffer driver for the PL110 display controller has been moved + from 'os/src/platform/versatilepb' to 'os/src/drivers/framebuffer/pl110'. + The PL110 driver depends on the build spec 'pl110'. + +:Lan9118 network interface: + The new NIC driver for Lan9118 is located at 'os/src/drivers/nic/lan9118/'. + This driver is built as 'nic_drv' when the build specs contain the + 'lan9118' value. This is the case for the 'fiasco_pbxa9' platform. The driver + is known to work on Qemu, yet untested on real hardware. + +:PL180 MMC and SDcard: + The new block driver for the PL180 MMC and SDcard is located at + 'os/src/drivers/sdcard/'. It depends on the build specs value 'pl180'. + At the current stage, the driver contains the low-level code for the + device access but lacks the interfacing to Genode's 'Block_session' + interface. + +:PL050 PS/2 input: + The interrupt handling of the PL050 driver has been improved, + IRQs are enabled only once, the IRQ pending bits are used to check + for availability of PS/2 packets. The PL050 driver depends on the + build spec value 'pl050'. + +:VESA framebuffer: + The VESA driver has become functional on the x86_64 platform. + It depends on the build spec value 'vesa'. + + +Libraries and applications +########################## + +Ready-to-use run scripts for standard scenarios +=============================================== + +On our mailing list, questions about using certain Genode components of various +base platforms, pop up at a regular basis. For example, how to use the lwIP +stack on a specific kernel. The answer to these kind of question depends on +several properties such as the used hardware platform or, when using Qemu, the +Qemu arguments. To make the exploration of various Genode features more +attractive, we have added the following run scripts that exercise the use +cases and document the steps required to build, configure, and integrate the +respective feature: + +:'os/run/demo.run': builds and executes Genode's default demo scenario. + It should run out of the box from a fresh build directory. + +:'libports/run/lwip.run': runs the 'lwip_httpsrv' example on Qemu, downloads a + website from the HTTP server, and validates the response. Make sure to have + the 'libc' and 'libports' repositories enabled in your 'build.conf'. The + 'libports' repository must be prepared for 'lwip' ('make prepare PKG=lwip'). + Furthermore, you will need a network driver ('nic_drv') as provided by the + 'linux_drivers' repository. + +:'ports/run/gdb_monitor.run': runs a test program as child of the new GDB + monitor, executed in Qemu. It then attaches a GDB session to the GDB monitor, + allowing the user to inspect the test program. In addition to the repositories + used by 'lwip.run', this run script further depends on the 'gdb' package + provided by the 'ports' repository. + +:'qt4/run/qt4.run': runs the 'qt_launchpad' application, which allows the user + to manually start the Qt4 'textedit' program. Of course, the run script + depends on a prepared 'qt4' repository. Furthermore, Qt4 depends on the + libraries 'zlib', 'libpng', and 'freetype' provided by the 'libports' + repository. + +:'ports/run/noux.run': compiles the GNU coreutils and wraps them into a tar + archive. It then runs the Noux environment with the tar archive as file + system and instructs Noux to execute the 'ls -Rla' command. The run script + depends on the 'libc', and 'ports' repositories. The 'ports' repository must + be prepared for the 'coreutils' package. + +:'ports-okl4/run/lx_block.run': starts the OKLinux kernel on top of OKL4. + This run script must be slightly adapted to use a custom disk image. + By default, it expects a disk image called 'tinycore.img' and an initrd + called 'initrd.gz' in the '/bin/' directory. + +:'ports-foc/run/l4linux.run': starts the L4Linux kernel on top of Fiasco.OC. + + +GDB monitor experiment +====================== + +Because there are repeated requests for a debugging solution for Genode +programs, we started exploring the use of GNU debugger (GDB) with Genode. The +approach is to run the program to debug (target) as a child process of a +so-called GDB monitor process. The GDB monitor allows the observation and +manipulation of the target program via a remote GDB TCP/IP connection. Our +immediate goal was to examine the mode of interaction between the GDB monitor +and GDB, and to determine the set of requirements a base platform must deliver +to make debugging possible. + +The experiment was first conducted on OKL4 because this kernel provides an easy +access to register states of any thread using 'exregs'. Furthermore, in +contrast to most of the other base platforms, OKL4 features a way to suspend +and resume threads. Once, this initial goal was reached, we enabled parts of +the debugging facilities for other base platforms, namely L4/Fiasco, +L4ka::Pistachio, and Fiasco.OC. + +:Usage: + +To illustrate the use of GDB monitor, a ready-to-use run script is provided +at 'ports/run/gdb_monitor'. This run script executes a simple test program +within the GDB monitor. Once the program is running, a host GDB is started +in a new terminal window and connects to the target running inside Qemu. +In the run script, you will recognise the following things: + +* A NIC driver must be built and started. Please make sure to have + a repository with a 'nic_drv' target enabled. E.g., on x86 platforms, + you may use the 'linux_drivers' repository. + +* The GDB monitor reads the name of the target program from its Genode config: + ! + +* To connect a host GDB to the remote target running in Qemu, use the + following GDB command: + ! target remote localhost:8181 + +:Current state, limitations: + +First, it is important to highlight that the GDB monitor is an experiment and +not ready for real-world use. It has been tested on Fiasco.OC, L4/Fiasco, +OKL4, and L4ka::Pistachio on the x86_32 architecture. On these platforms, +GDB monitor can be used to examine the memory in the target program. However, +only on OKL4, the threads in the target program are halted. The observed memory +state may appear inconsistent on the other platforms. On all platforms, the +current stack pointer and program counter values can be inspected. On OKL4, a +backtrace can be printed. The running threads in the target program can be +listed ('info threads'), selected ('thread N'), and examined. Advanced +debugging features such as breakpoints and watchpoints as well as the access to +general-purpose registers are not implemented. + + +Platform support +################ + +Fiasco.OC +========= + +With the previous Genode version 11.02, Fiasco.OC was introduced as new +base platform. The initial support contained all functionality needed to +execute the graphical Genode demo scenario on this kernel. However, some pieces +needed for more complex scenarios were missing, most importantly support for +the dynamic linker and the signalling framework. The dynamic linker is a +prerequisite for using the C runtime and all dependent libraries such as lwIP +and Qt4. The signalling framework is used by Genode's packet stream interface, +which in turn, is the basis for the 'Nic', 'Block', and 'Audio_out' interfaces. + +The current release brings the Fiasco.OC base platform on par with the other +fully-supported platforms so that the complete Genode software stack becomes +available on this kernel. + +Furthermore, we started to take advantage of Fiasco.OC's exceptional platform +support by enabling the use of the x86_64 architecture as well as the ARM +RealView PBX-A9 platform. For the latter platform, though, some parts of Genode +such as Qt4 and Noux are not yet available. To make the ARM RealView PBX-A9 +platform usable, we introduced a number of new device drivers such as the PL050 +input driver, Lan9118 network driver, and PL110 display driver. Using these +drivers, most of Genode's components including networking and graphics are +ready to use on the PBX-A9 platform. It should be noted, however, that the +device drivers have been developed and tested on Qemu only. They are untested on +real hardware. Their main purpose for now is to showcase how to create Genode +drivers for different device classes. + +:Improved integration of 3rd-party kernel sources with Genode: + +In the spirit of other repositories that incorporate 3rd-party code, the +'base-foc' repository comes with a new top-level Makefile that takes care of +downloading all the pieces needed for deploying Genode on Fiasco.OC. All that's +needed is issuing 'make prepare' from within the 'base-foc' repository. +When using this way of incorporating Fiasco.OC, the kernel can be built right +from the Genode build directory as created with the build-directory creation +tool at 'tool/builddir/create_builddir': +! make kernel +The kernel will be configured and built according to the platform as specified +to the 'create_builddir' tool. The kernel's build directory can be found at +the '/kernel/fiasco.oc/'. + +The kernel is accompanied by two user-level components, namely sigma0 and bootstrap. +Those components can be built in a similar fashion: +! make sigma0 +! make bootstrap +For building sigma0 and bootstrap, the Genode build system invokes the L4Re +build system. The corresponding L4Re build directory can be found at +'/l4/'. The kernel interfaces of Fiasco.OC as used by Genode +are installed to '/include/'. + +Alternatively to using the new way of integrating Fiasco.OC with Genode, +the location of the kernel binary and a custom L4Re build directory can be +explicitly specified in a file called '/etc/foc.conf': +! L4_BUILD_DIR = +! KERNEL = + +With the new integration approach, the make targets 'clean' and 'cleanall' +are no longer synonymous. The 'clean' target removes all Genode-specific +files from the build directory but keeps the Fiasco.OC and L4Re build +directories. In contrast, the 'cleanall' rule wipes everything. + +:Small changes to 'base-foc': + +* Core does now export Fiasco.OC's kernel info page (KIP) as ROM module. +* The thread library takes advantage of the user-defined part of the UTCB to + store the pointer to the 'Thread_base' object instead of using the stack + pointer as a key. +* Fiasco.OC's VCPU feature has been made accessible via an Fiasco.OC-specific + extension of core's PD and CPU session interfaces. The only user of these + extension as of today is L4Linux. +* Improved IRQ support for level triggered interrupts, increasing the + maximum number of supported interrupts to 256. + + +MicroBlaze +========== + +Our custom kernel platform for the Xilinx MicroBlaze softcore CPU, which we +introduced with Genode 11.02, has been complemented with the core interfaces +needed for the implementation of user-level device drivers. Those interfaces +are the IRQ service and the IO_MEM service. + +IRQ support +~~~~~~~~~~~ + +To accommodate core's IRQ service, the interface between the kernel-level and +user-level parts of core had to be extended with syscalls for managing and +handling interrupts. These syscalls are exclusively used by the interrupt +threads of core's IRQ services. They are not accessible from other user-level +programs. + +:'irq_allocate(irq_number)': + associates the specified IRQ to the calling core thread. One thread + may associate itself with multiple IRQs by consecutive calls of this + syscall. However, the current implementation of core's IRQ service + employs one core thread per IRQ. + +:'irq_free(irq_number)': + reverts the effect of 'irq_allocate'. + +:'irq_wait()': + lets the calling thread block for any of the IRQs it is associated + with. When unblocked, the calling thread receives the information + about the occurred IRQ in its user-level thread-control block (UTCB). + +Run environment, SoC for S3A Starter Kit +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The initial version of the 'base-mb' platform was tied to a fixed work flow, +executing a predefined Genode scenario on Qemu. With the current release, the +build-system integration advanced towards the versatile usage pattern as found +on the other base platforms. + +* The improved run environment supports the inclusion of arbitrary boot modules + into core's ROM service. The underlying mechanism has not changed though. The + ROM modules are aggregated via an assembly file called 'boot_modules.s' using + the 'incbin' directive. Because this file gets linked to core, core can be + booted as single-image on a target. + +* In addition of using the MicroBlaze variant of Qemu to execute Genode, + support has been added use different targets. As a reference, a ready-to-use + SoC 'system.bit' file is provided for the Xilinx Spartan3A Starter Kit board. + +You can get further inspiration to explore the 'base-mb' platform by studying +the new documentation to be found at 'base-mb/doc/'. + + +Build system and tools +###################### + +Genode does currently support 8 different kernel platforms. For each kernel, +different steps are required to download and install the kernel and to +supply the kernel headers to the Genode build system. Furthermore, the +ways of how the result of the Genode build process has to be integrated with +the boot mechanism of respective kernel differs a lot. + +Hence, for each base platform, there exists a dedicated Wiki page describing +the manual steps to follow. In the case of Fiasco.OC, these steps are +particularly elaborative, making the use of this platform with Genode less +approachable than most of the others. + +:New work flow for integrating 3rd-party kernel code: + +To make the head start of using Fiasco.OC as simple as possible, we explored a +new way to integrate the 3rd-party kernel code with Genode. Similar to the +'make prepare' mechanism that we already use for the 'qt4', 'ports', and +'libports' repositories, we have added a top-level Makefile to 'base-foc' that +automates the preparation of all the 3rd-party code needed to use Genode with +the base platform. In the case of Fiasco.OC, this is the kernel code plus some +bits of the L4Re userland, namely sigma0, bootstrap, and l4sys. This +preparation mechanism is complemented by platform-specific pseudo targets that +enable the building of the 3rd-party code right from Genode's build directory. +To support this methodology, we added a hook into the Genode build system, +allowing a platform-specific initialization of the Genode build directory. +E.g., for creating symbolic links to kernel headers. These initial steps are +executed by a pseudo library called 'platform.mk'. This library is guaranteed to +be built prior all other libraries and targets. The new level of integration +greatly simplifies the use of Genode on Fiasco.OC. Hence, we are eager to apply +the same idea to the other base platforms as well. + +:New naming scheme for platform-specific ports repositories: + +The 'oklinux' repository is now called 'ports-okl4'. Thereby, we want to +facilitate a unified naming scheme for platform-specific 3rd party software. +E.g., the port of L4Linux resides in the new 'ports-foc' repository because it +is specific for the Fiasco.OC base platform. + +:New convenience functions for run scripts: + +To ease the creation of run scripts that are usable across different kernel and +hardware platforms, we have added new convenience functions to the 'run' +tool. The functions 'append_if' and 'lappend_if' are intended to be +used in combination with the 'have_spec' function to allow the easy +extension of the Genode config, Qemu parameters, and the list of boot +modules driven by 'SPECS' values. For a showcase, please refer to the +new 'os/run/demo.run' script. + diff --git a/doc/release_notes-11-08.txt b/doc/release_notes-11-08.txt new file mode 100644 index 000000000..fb010e52f --- /dev/null +++ b/doc/release_notes-11-08.txt @@ -0,0 +1,703 @@ + + + =============================================== + Release notes for the Genode OS Framework 11.08 + =============================================== + + Genode Labs + + + +One of Genode's most distinctive properties is its support for various +different kernels as base platforms. Each of the 8 currently supported kernels +differs with regard to features, security, hardware support, complexity, and +resource management. Even though different applications call for different +kernel properties, through Genode, those properties can be leveraged using a +unified API. The growing number of supported base platforms, however, poses two +challenges, which are the comprehension of the large diversity of tools and +boot concepts, and capturing of the semantic differences of all the kernels. + +With the version 11.08, the framework mitigates the former challenge by +introducing a unified way to download, build, and use each of the +kernels with Genode's user-level infrastructure. The new tools empower users of +the framework to instantly change the underlying kernel without the need to know +the peculiarities of the respective kernels. Using microkernels has never been +easier. + +The second challenge of translating each kernel's specific behaviour to the +framework's unified API longs for an automated testing infrastructure that +systematically exercises all the various facets of the API on all base +platforms. The new version introduces the tooling support especially designed +for conducting such quality-assurance measures. These tools largely remove the +burden of manual testing while helping us to uphold the stability and quality +of the framework as it grows in terms of functional complexity and number of +base platforms. + +Speaking of functional enhancements, the work on version 11.08 was focused +on our block-device infrastructure and ARM support. The block-device-related +work is primarily motivated by our fundamental goal to scale Genode to a +general-purpose computing platform. The additions comprise new drivers for +SD-cards, IDE, SATA, USB storage as well as a new partition server. All those +components provide Genode's generic block interface, which is meant to be used +as back end for file systems. On file-system level, a new libc plugin utilizes +libffat to enable the straight-forward use of VFAT partitions by libc-using +programs. + +The current release comes with far-reaching improvements with respect to +ARM-based platforms. The paravirtualized L4Linux kernel has been updated to +Linux version 2.6.39 running on both x86_32 and ARM. Also, Qt4 including Webkit +has become functional on ARMv6-based platforms. + +Among the further improvements are many new examples in the form of +ready-to-use run scripts as well as a comprehensive documentation update. + +Originally, we had planned to complement the Noux runtime environment to +support interactive command-line applications by the time of the current +release. However, we realized that the current users of the framework would +value the new streamlined tooling support, the enhanced documentation, and the +new quality-assurance infrastructure over such a functional addition. Hence, we +prioritized the topics accordingly. Even though you will find the first bits of +interactive GNU application support in this release, we deferred working on +this topic in full steam to the upcoming version 11.11. + + +Blurring the boundaries between different kernels +################################################# + +Before the Genode project was born, each microkernel carried along its own +userland. For example, the L4/Fiasco kernel came with the L4 environment, the +OKL4 kernel came with Iguana, or the L4ka::Pistachio kernel came with a small +set of example components. Those user-level counterparts of the kernel +complemented their respective kernels with a runtime for user-level +applications and components while exposing significant parts of the kernel +interface at API level. Consequently, most if not all applications developed +against these APIs were tied to a particular kernel. On the one hand, this +approach enabled developers to fine-tune their programs using kernel-specific +features. On the other hand, much effort was wasted by duplicating other +people's work. Eventually, all of the mentioned userlands stayed limited to +special purposes - for the most part the purposes of operating-systems +researchers. Consequently, none of the microkernels gained much attention in +general-purpose computing. Another consequence of the highly fragmented +microkernel community was the lack of a common ground to compare different +kernels in an unbiased way because each userland provided a different set of +components and libraries. + +Different application areas call for different kernel features such as +security mechanisms, scheduling, resource management, and hardware support. +Naturally, each kernel exhibits a specific profile of these parameters +depending on its primary purpose. If one microkernel attempted to accommodate +too many features, it would certainly sacrifice the fundamental idea of being +minimally complex. Consequently, kernels happen to be vastly different. During +the past three years, however, Genode has demonstrated that one carefully +crafted API can target highly diverse kernels, and thereby enables users of +the framework to select the kernel that fits best with the requirements +dictated by each application scenario individually. For us Genode developers, +it was extremely gratifying to see that kernels as different as Linux and NOVA +can be reconciled at the programming-interface level. Still, each kernel comes +with different tools, configuration mechanisms, and boot concepts. Even though +Genode programs can be developed in a kernel-independent way, the deployment of +such programs still required profound insights into the peculiarities of the +respective kernel. + +With the current release, we introduce a fundamentally new way of using +different microkernels by unifying the procedures of downloading and building +kernels as well as integrating and running Genode programs with each of them. +Existing Genode application scenarios can be ported between kernels in an +instant without the need for deep insights into the kernel's technicalities. As +a teaser, consider the following commands for building and running Genode's +graphical demo scenario on the OKL4 microkernel: + +! # check out Genode +! svn co https://genode.svn.sourceforge.net/svnroot/genode/trunk genode +! +! # download the kernel, e.g., OKL4 +! make -C genode/base-okl4 prepare +! +! # create Genode build directory +! genode/tool/create_builddir \ +! okl4_x86 BUILD_DIR=build +! +! # build everything and execute the interactive demo +! make -C build run/demo + +The same principle steps can be used for any of the OKL4, NOVA, +L4/Fiasco, Fiasco.OC, L4ka::Pistachio, or Codezero kernels. You should +nevertheless consult the documentation at 'base-/doc/' before +starting to use a specific kernel because some base platforms require +the installation of additional tools. + +Under the hood, this seamless way of dealing with different kernels is made +possible by the following considerations: + +:Repository preparation: + +Each kernel comes from a different source such as a Git/SVN/Mercurial +repository or a packaged archive. Some kernels require additional patches. For +example, OKL4 needs to be patched to overcome problems with modern tool chains. +Now, each 'base-' repository hosts a 'Makefile' that automates the +download and patch procedure. To download the source code of a kernel, +issue 'make prepare' from within the kernel's 'base-' directory. The +3rd-party source code will be located at 'base-/contrib/'. + +:Building the kernel: + +Each kernel has a different approach when it comes to configuration and +compilation. For example, NOVA comes with a simple 'Makefile', OKL4 relies on a +complex SCons-based build system, L4ka::Pistachio uses CML2 and autoconf (for +the userland tools). Furthermore, some kernels require the setting of specific +configuration values. We have streamlined all these procedures into the Genode +build process by the means of a 'kernel' pseudo target and a 'platform' pseudo +library. The kernel can be compiled directly from the Genode build directory by +issuing 'make kernel'. The 'platform' pseudo library takes care of making the +kernel headers available to Genode. For some kernels such as OKL4 and NOVA, we +replaced the original build mechanism with a Genode target. For other kernels +such as L4ka::Pistachio or Fiasco.OC, we invoke the kernel's build system. + +:Genode build directory: + +Genode build directories are created via the 'tool/create_builddir' tool. +This tool used to require certain kernel-specific arguments such as the +location of the kernel source tree. Thanks to the unified way of preparing +kernels, the need for such arguments has vanished. Now, the only remaining +arguments to 'create_builddir' are the actual platform and the location +of the build directory to create. + +:System integration and booting: + +As diverse the build systems of the kernels are, so are the boot concepts. Some +kernels rely on a multiboot-compliant boot loader whereas others have special +tools for creating boot images. Thankfully, Genode's run concept allows us to +hide the peculiarities of booting behind a neat and easy-to-use facade. For +each platform we have crafted a dedicated run environment located at +'base-/run/env', which contains the rules for system integration and +booting. Therefore, one and the same run script can be used to build and +execute one application scenario across various different kernels. For an +illustrative example, the 'os/src/run/demo.run' script can be executed on all +base platforms (except for base-mb) by issuing 'make run/demo' from within the +build directory. + + +Emerging block-device infrastructure +#################################### + +Since version 10.08, Genode is equipped with a block-session interface. Its +primary use cases so far were the supply of the paravirtualized OKLinux kernel +with backing store, and the access of the content of a bootable Live CD. +However, for our mission to use Genode as general-purpose computing platform, +disk device access is crucial. Therefore, we dedicated our attention to +various aspects of Genode's block-device infrastructure, reaching from +programming APIs for block drivers, over partition handling, to file-system +access. + +:Block session interface: + +The glue that holds all block-device-related components together is the generic +block interface 'os/include/block_session'. It is based on the framework's +packet-stream facility, which allows the communication of bulk data via shared +memory and a data-flow protocol using asynchronous notifications. The interface +supports arbitrary allocation schemes and the use of multiple outstanding +requests. Hence, it is generally suited for scatter-gather DMA and the use of +command queuing as offered by the firmware of modern block-device controllers. +(albeit the current drivers do not exploit this potential yet) + +:Block component framework: + +Our observation that components implementing the block session interface share +similar code patterns prompted us to design a framework API for implementing +this family of components. The set of classes located at 'os/include/block' +facilitate the separation of device-specific code from application logic. +Whereas 'component.h' provides the application logic needed to implement the +block service, the 'driver.h' is an abstract interface to be implemented by the +actual device driver. This new infrastructure significantly reduces code +duplication among new block-device drivers. + +:Device-driver implementations: + +The new block-device drivers introduced with the current release address +common types of block devices: + +* By adding ATA read/write support to the ATAPI driver ('os/src/drivers/atapi'), + this driver can be used to access IDE disks now. +* The new fully-functional SD-card driver ('os/src/drivers/sdcard') enables the + use of SD-cards connected via the PL180 controller. +* The USB storage driver ('linux_drivers/src/drivers/usb') has been adapted + to the block-session interface and can be used on PC hardware. +* The new AHCI driver ('os/src/drivers/ahci') enables the access of disks + connected via SATA on PC hardware. + +Because all drivers are providing the generic block-session interfaces, they +can be arbitrarily combined with components that use this interface as back +end, for example, the partition server and file systems. + +:Partition manager as resource multiplexer: + +The new partition manager ('os/src/server/part_blk') multiplexes one back-end +block session to multiple block sessions, each accessing a different partition. +Its natural role is being "plugged" between a block-device driver and a file +system. + +:File-system access: + +Even though a session interface for file systems does not exist yet, we +enabled the use of VFAT partitions through a libc plugin. This libc plugin uses +the ffat library to access files stored on a block device. An +application using this plugin can be directly connected to a block session. + + +New documentation +################# + +The new way of dealing with different kernels motivated us to revisit and +complement our exiting documentation. The following documents are new or +have received considerable attention: + +:[http://genode.org/documentation/developer-resources/getting_started - Getting started]: + The revised guide of how to explore Genode provides a quick way to + test drive Genode's graphical demo scenario with a kernel of your + choice and gives pointers to documents needed to proceed your + exploration. + +:[http://genode.org/documentation/developer-resources/build_system - Build system manual]: + The new build-system manual explains the concepts behind Genode's + build system, provides guidance with creating custom programs and + libraries, and covers the tool support for the automated integration + and testing of application scenarios. + +:[http://genode.org/documentation/components - Components overview]: + The new components-overview document explains the categorization of + Genode's components and lists all components that come with the framework. + +:[http://genode.org/documentation/developer-resources/init - Configuration of the init process]: + The document describes Genode's configuration concept, the routing of + service requests, and the expression of mandatory access-control policies. + +:[http://genode.org/community/wiki - Wiki]: + The platform-specific Wiki pages for L4/Fiasco, L4ka::Pistachio, NOVA, + Codezero, Fiasco.OC, and OKL4 have been updated to reflect the new flows of + working with the respective base platforms. + + +Base framework +############## + +The RPC API for performing procedure calls across process boundaries +introduced with the version 11.05 was the most significant API change +in Genode's history. To make the transition from the old client-server +API to the new RPC API as smooth as possible, we temporarily upheld +compatibility to the old API. Now, the time has come to put the old +API at rest. The changes that are visible at API level are as follows: + +* The old client-server API in the form of 'base/server.h' is no more. + The functionality of the original classes 'Server_entrypoint' and + 'Server_activation' is contained in the 'Rpc_entrypoint' class provided + via 'base/rpc_server.h'. + +* When introducing the RPC API, we intentionally left the actual session + interfaces as unmodified as possible to proof the versatility of the new + facility. However, it became apparent that some of the original interfaces + could profit from using a less C-ish style. For example, some interfaces used + to pass null-terminated strings as 'char const *' rather than via a dedicated + type. The methodology of using the new RPC API while leaving the original + interfaces intact was to implement such old-style functions as wrappers + around new-style RPC functions. These wrappers were contained in + 'rpc_object.h' files, e.g. for 'linux_dataspace', 'parent', 'root', + 'signal_session', 'cpu_session'. Now, we have taken the chance to modernise + the API by disposing said wrappers. Thereby, the need for 'rpc_object.h' + files has (almost) vanished. + +* The remaining users of the old client-server API have been adapted to the + new RPC API, most prominently, the packet-stream-related interfaces such as + 'block_session', 'nic_session', and 'audio_session'. + +* We removed 'Typed_capability' and the second argument of the 'Capability' + template. The latter was an artifact that was only used to support the + transition from the old to the new API. + +* The 'ipc_client' has no longer an 'operator int'. The result of an IPC can + be requested via the 'result' function. + +* We refined the accessors of 'Rpc_in_buffer' in 'base/rpc_args.h'. The + 'addr()' has been renamed to 'base()', 'is_valid_string()' considers the + buffer's capacity, and the new 'string()' function is guaranteed to return a + null-terminated string. + +* We introduced a new 'Rm_session::Local_addr' class, which serves two + purposes. It allows the transfer of the bit representation of pointers across + RPC calls and effectively removes the need for casting the return type of + 'Rm_session::attach' to the type needed at the caller side. + +* The 'Connection' class template has been simplified, taking the session + interface as template argument (rather than the capability type). This change + simplified the 'Connection' classes of most session interfaces. + +* The never-used return value of 'Parent::announce' has been removed. From the + child's perspective, an announcement always succeeds. The way of how the + announcement is treated is entirely up to the parent. The client should never + act differently depending on the parent's policy anyway. + +* The new 'Thread_base::cap()' accessor function allows obtaining the thread's + capability as used for the argument to CPU-session operations. + + +Operating-system services and libraries +####################################### + +Dynamic linker +============== + +As a follow-up to the major revision of the dynamic linker that was featured +with the previous release, we addressed several corner cases related to +exception handling and improved the handling of global symbols. + +The dynamic linker used to resolve requests for global symbols by handing out +its own symbols if present. However, in some cases, this behaviour is +undesired. For example, the dynamic linker contains a small set of libc +emulation functions specifically for the ported linker code. In the presence of +the real libc, however, these symbols should never be considered at all. To +avoid such ambiguities during symbol resolution, the set of symbols to be +exported is now explicitly declared by the white-list contained in the +'os/src/lib/ldso/symbol.map' file. + +We changed the linkage of the C++ support library ('cxx') against dynamic +binaries to be consistent with the other base libraries. Originally, the 'cxx' +library was linked to both the dynamic linker and the dynamic binary, which +resulted in subtle problems caused by the duplication of cxx-internal data +structures. By linking 'cxx' only to the dynamic linker and exporting the +'__cxa' ABI as global symbols, these issues have been resolved. As a positive +side effect, this change reduces the size of dynamic binaries. + +C++ exception handling in the presence of shared libraries turned out to be +more challenging than we originally anticipated. For example, the +'_Unwind_Resume' symbol is exported by the compiler's 'libsupc++' as a hidden +global symbol, which can only be resolved when linking this library to the +binary but is not seen by the dynamic linker. This was the actual reason of why +we used to link 'cxx' against both dynamic binaries and shared libraries +causing the problem mentioned in the previous paragraph. Normally, this problem +is addressed by a shared library called 'libgcc_s.so' that comes with the +compiler. However, this library depends on glibc, which prevents us from using +it on Genode. Our solution is renaming the hidden global symbol using a +'_cxx__' prefix and introducing a non-hidden global wrapper function +('__cxx__Unwind_Resume' in 'unwind.cc'), which is resolved at runtime by the +dynamic linker. + +Another corner case we identified is throwing exceptions from within the +dynamic linker. In contrast to the original FreeBSD version of the dynamic +linker, which is a plain C program that can never throw a C++ exception, +Genode's version relies on C++ code that makes use of exceptions. To support +C++ exceptions from within the dynamic linker, we have to relocate the +linkers's global symbols again after having loaded the dynamic binary. This +way, type information that is also present within the dynamic binary becomes +relocated to the correct positions. + + +Block partition server +====================== + +The new block-partition server uses Genode's block-session interfaces as both +front and back end, leading to the most common use case where this server will +reside between a block driver and a higher level component like a file-system +server. + +At startup, the partition server will try to parse the master boot record (MBR) +of its back-end block session. If no partition table is found, the whole block +device is exported as partition '0'. In the other case, the MBR and possible +extended boot records (EBRs) are parsed and offered as separate block sessions +to the front-end clients. The four primary partitions will receive partition +numbers '1' to '4' whereas the first logical partition will be assigned to '5'. + +The policy of which partition is exposed to which client can be expressed +in the config supplied to the 'part_blk' server. Please refer to the +documentation at 'os/src/server/part_blk/README' for further details. As an +illustration of the practical use of the 'part_blk' server, you can find a run +script at 'os/run/part_blk.run'. + + +Skeleton of text terminal +========================= + +As part of the ongoing work towards using interactive text-based GNU software +on Genode, we created the first bits of the infrastructure required for +pursuing this quest: + +The new terminal-session interface at 'os/include/terminal_session/' is the +designated interface to be implemented by terminal programs. + +After investigating the pros and cons of various terminal protocols and +terminal emulators, we settled for implementing a custom terminal emulator +implementing the Linux termcap. This termcap offers a reasonable small set of +commands while providing all essential features such as function-key support +and mouse support. Thanks to Peter Persson for pointing us to the right +direction! The preliminary code for parsing the escape sequences for the Linux +termcap is located at 'gems/include/terminal/'. + +We have created a simplistic terminal service that implements the +terminal-session interface using a built-in font. Please note that the +implementation at 'gems/src/server/terminal/' is at an early stage. It is +accompanied by a simple echo program located at 'gems/src/test/terminal_echo'. + + +Device drivers +############## + +USB HID and USB storage +======================= + +We replaced the former DDE-Linux-based USB-related driver libraries (at the +'linux_drivers/' repository) by a single USB driver server that offers the +'Input' and 'Block' services. This enables us to use both USB HID and USB +storage at the same time. The new USB driver is located at +'linux_drivers/src/drivers/usb/'. + +For using the USB driver as input service (supporting USB HID), add the +'' tag to the 'usb_drv' configuration. Analogously, for using the driver +as block service, add the '' tag. Both tags can be combined. + +For testing the USB stack, the 'linux_drivers' repository comes with the run +scripts 'usb_hid.run' and 'usb_storage.run'. + + +ATA read/write support +====================== + +The ATAPI driver has been extended to support IDE block devices for both +read and write transactions. To use the new facility, supply 'ata="yes"' +as XML attribute to the config node of 'atapi_drv'. Please note that this +driver was primarily tested on Qemu. Use it with caution. + + +SATA driver +=========== + +The new SATA driver at 'os/src/drivers/ahci/' implements the block-driver +API ('os/include/block'), thus exposing the block-session interface as +front-end. AHCI depends on Genode's PCI driver as well as the timer server. For +a usage example see: 'os/run/ahci.run'. + +Limitations and known issues +---------------------------- + +Currently, the server scans the PCI bus at startup and retrieves the first available +AHCI controller, scans the controller ports and uses the first non-ATAPI port +where a device is present. + +On real hardware and on kernels taking advantage of I/O APICs (namely NOVA and +Fiasco.OC) we still lack support for ACPI parsing and thus for interrupts, +leading to a non-working driver. + + +SD-card driver +============== + +The first fragments of our SD-card driver that we introduced with the previous +release have been complemented. The new SD-card driver located at +'os/src/drivers/sd_card/' implements the block-session interface by using +MMC/SD-cards and the PL180 controller as back end. Currently the driver +supports single-capacity SD cards. Therefore, the block file for Qemu should +not exceed 512 MB. Because the driver provides the generic block-session +interface, it can be combined with the new 'libc_ffat' plugin in a +straight-forward way. To give the driver a quick spin, you may give the +'libports/run/libc_ffat.run' script on the 'foc_pbxa9' platform a try. + + +ARM Realview PL011 UART driver +============================== + +The new PL011 UART driver at 'os/src/drivers/uart/' implements the LOG session +interface using the PL011 device. Up to 4 UARTs are supported. The assignment +of UARTs to clients can be defined via a policy supplied to the driver's config +node. For further information, please refer to the README file within the +'uart' directory. + + +Libraries and applications +########################## + +Hello tutorial +============== + +The 'hello_tutorial/' repository contains a step-by-step guide for building +a simple client-server scenario. The tutorial has been rewritten for the new +RPC API and is now complemented by a run script for testing the final scenario +on various base platforms. + +C and C++ runtimes +================== + +:Support for standard C++ headers: + +Triggered by public demand for using standard C++ headers for Genode applications, +we introduced a generally usable solution in the form of the 'stdcxx' library +to the 'libc' repository. The new 'stdcxx' library is not a real library. (you +will find the corresponding 'lib/mk/stdcxx.mk' file empty) However, it comes +with a 'lib/import/import-stdcxx.mk' file that adds the compiler's C++ includes +to the default include-search path for any target that has 'stdcxx' listed in +its 'LIBS' declaration. + +:Libc back end for accessing VFAT partitions: + +The new 'libc_ffat' libc plugin uses a block session via the ffat library. It +can be used by a Genode application to access a VFAT file system via the libc +file API. The file-system access is performed via the 'ffat' library. To +download this library and integrate it with Genode, change to the 'libports' +repository and issue the following command: +! make prepare PKG=ffat +For an example of how to use the libc-ffat plugin, please refer to the run +script 'libports/run/libc_ffat.run'. The source code of the test program can be +found at 'libports/src/test/libc_ffat/'. + +Qt4 +=== + +Qt4 version 4.7.1 has been enabled on ARMv6-based platforms, i.e., PBX-A9 on +Fiasco.OC. The support comprises the entire Qt4 framework including qt_webcore +(Webkit). + +L4Linux +======= + +L4Linux enables the use of one or multiple instances of Linux-based operating +systems as subsystems running on the Fiasco.OC kernel. The Genode version of +L4Linux has seen the following improvements: + +:Kernel version: has been updated to Linux 2.6.39. + +:ARM support: The L4Linux kernel can be used on ARM-based platforms now. + The PBX-A9 platform is supported via the 'l4linux.run' script as found + at 'ports-foc/run/'. Please find more information at 'ports-foc/README'. + +:Genode-specific stub drivers outside the kernel tree: + The stub drivers that enable the use of Genode's services as virtual + devices for L4Linux have been moved outside the kernel patch, which + makes them much easier to maintain. These stub drivers are located + under 'ports-foc/src/drivers/'. + + +Platform support +################ + +All base platforms are now handled in a unified fashion. Downloading 3rd-party +source code is performed using the 'prepare' rule of the 'Makefile' provided by +the respective kernel's 'base-' repository. Once, the platform's base +repository is prepared, the kernel can be built directly from the Genode +build directory using 'make kernel'. All base platforms are now supported by +Genode's run mechanism that automates the tasks of system integration and +testing. For more details about each specific kernel, please revisit the +updated documentation within the respective 'base-/doc/' directory. + +:L4/Fiasco: + +The kernel has been updated to revision 472, enabling the use of recent +GNU tool chains. + +:Fiasco.OC: + +The kernel as been updated to revision 36, which remedies stability problems +related to interaction of the IPC path with thread destruction. The new version +improves the stability of highly dynamic workloads that involve the frequent +creation and destruction of subsystems. However, we experienced the new kernel +version to behave instable on the x86_64 architecture. If you depend on x86_64, +we recommend to temporarily stick with Genode 11.05 and Fiasco.OC revision 31. + +:L4ka::Pistachio: + +The kernel has been updated to revision 803, enabling the use of recent +versions of binutils. + +:OKL4: + +OKL4v2 is showing its age. Apparently, the use of the original distribution +requires tools (i.e., python 2.4) that do not ship with current Linux +distributions anymore. This makes it increasingly difficult to use this kernel. +Still, we find ourselves frequently using it for our day-to-day development. To +streamline the use of OKL4v2, we have now incorporated the kernel compilation +into the Genode build system and thereby weakened the kernel's dependency on +ancient tools. However, we decided to drop support for OKL4/ARM for now. We +figured that the supported GTA01 platform is hardly used anymore and hard to +test because it is unsupported by Qemu. Newer ARM platforms are supported by +other kernels anyway. + +:Codezero: + +Even though B-Labs apparently abandoned the idea of developing the Codezero +kernel in the open, we adapted Genode to the kernel's most recent Open-Source +version that is still available at the official Git repository. Furthermore, +the kernel is now fully supported by Genode's new 'make prepare' procedure and +run environment. Therefore, run scripts such as 'run/demo' can now easily be +executed on Codezero without the need to manually configure the kernel. + +Note that, for now, we have disabled Codezero's capabilities because they do +not allow the assignment of device resources. Consequently, 'sys_map' fails for +MMIO regions when performing the capability check (calling 'cap_map_check'). +Furthermore, the current version of the kernel requires a workaround for a +current limitation regarding the definition of a thread's pager. At some point, +Codezero abandoned the facility to define the pager for a given thread via the +exregs system call. Instead, the kernel hard-wires the creator of the thread as +the thread's pager. This is conflicting with Genode's way of creating and +paging threads. In the current version of Genode for this kernel, all threads +are paged by one thread (thread 3 happens to be the global pager) within core. +As a workaround to Codezero's current limitation, we define thread 3 to be the +pager of all threads. The patch of the upstream code is automatically being +applied by the 'make prepare' mechanism. + + +Build system and tools +###################### + +In addition to the major change with respect to the integration of the various +base platforms, Genode's tool support received the following incremental +improvements: + + +Build system +============ + +:Simplification of 'create_builddir' tool: + +The 'create_builddir' tool has been relocated from +'tool/builddir/create_builddir' to 'tool/create_builddir' to make it more +readily accessible. Furthermore, we simplified the usage of the tool by +removing the mandatory 'GENODE_DIR' argument. If not explicitly specified, the +tool deduces 'GENODE_DIR' from the its known location within the Genode source +tree. + +:Booting from USB sticks: + +For most x86-based base platforms, their respective run environments execute +Genode from an ISO image via Qemu. Naturally, such an ISO image can be burned +onto a CD-ROM to be used to boot a real machine. However, booting from CD-ROM +is slow and optical drives are becoming scarce. Therefore we changed the +procedure of creating ISO images to support writing the resulting images to a +USB stick. Under the hood, the boot mechanism chain-loads GRUB via ISOLinux. +The files to implement the boot concept are located at 'tool/boot/'. + +:Support for source files in target sub directories: + +Until now, the 'SRC_*' declarations in target description files contained +a list of plain file names. The location of the files within the directory +tree had to be defined via 'vpath'. This led to inconveniences when building +3rd-party code that contains files with the same name at different subdirectories. +To resolve such an ambiguity, the target had to be decomposed into multiple +libraries each building a different set of subdirectories. To make the +build system more convenient to use, we have now added support for specifying +source codes with a relative pathname. For example, instead of using +! SRC_CC = main.cc addon.cc +! vpath addon.cc $(PRG_DIR)/contrib +we can now use +! SRC_CC = main.cc contrib/addon.cc + + +Automated testing across multiple kernels +========================================= + +To execute one or multiple test cases on more than one base platform, we +introduced a dedicated tool located at 'tool/autopilot'. Its primary purpose is +the nightly execution of test cases. The tool takes a list of platforms and a +list of run scripts as arguments and executes each run script on each platform. +The build directory for each platform is created at +'/tmp/autopilot./' and the output of each run script is +written to a file called '..log'. On stderr, autopilot +prints the statistics about whether or not each run script executed +successfully on each platform. If at least one run script failed, autopilot +returns a non-zero exit code, which makes it straight forward to include +autopilot into an automated build-and-test environment. + diff --git a/doc/release_notes-11-11.txt b/doc/release_notes-11-11.txt new file mode 100644 index 000000000..15d3b3095 --- /dev/null +++ b/doc/release_notes-11-11.txt @@ -0,0 +1,1008 @@ + + + =============================================== + Release notes for the Genode OS Framework 11.11 + =============================================== + + Genode Labs + + + +Each Genode release is themed with a predominating topic. Version 11.08 aimed +at blurring the lines between the use of different base platforms whereas the +predecessor re-addressed inter-process communication. With the current release, +we explore the various approaches to virtualization using Genode. At the first +sight, this topic sounds like riding a dead horse because virtualization is +widely regarded as commodity by now. However, because Genode has virtualization +built right in the heart of its architecture, this topic gets a quite different +spin. As described in Section [A Plethora of Levels of Virtualization], the +version 11.11 contains the results our exploration work about faithful +virtualization, paravirtualization, OS-level virtualization, and +application-level virtualization. The latter category is particularly unique to +Genode's architecture. + +Besides elaborating on virtualization, the version 11.11 comes with new +features such as support for user-level debugging by the means of the GNU +debugger and the ability to run complex interactive UNIX applications on Genode +(i.e., VIM). Furthermore, we improved device-driver support, specifically for +ARM platforms. Apart from working on features, we had been busy with +optimizing several aspects of the framework, improvements ranging from improved +build-system performance, over reduced kernel-memory footprint for NOVA, to a +new IPC implementation for Linux. + +For regular Genode developers, one of the most significant changes is the new +tool chain based on GCC 4.6.1. The background story about the tool-chain update +and its implications is described in Section [New tool chain based on GCC +4.6.1]. + + +A Plethora of Levels of Virtualization +###################################### + +Virtualization has become a commodity feature universally expected from modern +operating systems. The motivations behind employing virtualization techniques +roughly fall into two categories: the re-use of existing software and +sandboxing untrusted code. The latter category is particularly related to +the Genode architecture. The Genode process tree is essentially a tree of +nested sandboxes where each node is able to impose policy on its children. +This immediately raises the question of where Genode's design could take us +when combined with existing virtualization techniques. To explore the +possible inter-relationships between Genode and virtualization, we conducted +a series of experiments. We found that Genode's recursive architecture +paves the way to far more flexible techniques than those we know from existing +solutions. + + +Faithful x86 PC Virtualization enabled by the Vancouver VMM +=========================================================== + +Most commonly, the term virtualization refers to faithful virtualization where +an unmodified guest OS is executed on a virtual hardware platform. Examples of +such virtual machines are VMware, VirtualBox, KVM, and Xen - naming only those +that make use of hardware virtualization capabilities. Those established +technologies have an impressive track record on the first of both categories +mentioned above - software re-use. However, when it comes to sandboxing +properties, we find that another virtual machine monitor (VMM) implementation +outshines these established solutions, namely the Vancouver virtual machine +monitor executed on top of the NOVA hypervisor. Combined, NOVA and Vancouver +are able to slash the complexity of the trusted computing base (TCB) for +isolating virtual machines to a tiny fraction compared to the established +products. The key is the poly-instantiation of the relatively complex virtual +machine monitor with each virtual machine. Each VMM is executed within a +dedicated protection domain. So a problem in one VM can only affect its +respective Guest OS but not any other VM. By executing the VMM outside of the +hypervisor, the hypervisor's complexity is dramatically smaller than +traditional hypervisors. Hence, the authors of NOVA coined the term +microhypervisor. + +The NOVA virtualization architecture is detailed in the paper +[http://os.inf.tu-dresden.de/papers_ps/steinberg_eurosys2010.pdf - NOVA: A Microhypervisor-Based Secure Virtualization Architecture] +by Udo Steinberg and Bernhard Kauer. + +Since February 2010, NOVA is one of the supported base platforms of Genode. But +until now, Vancouver has been tied to a specialized user land that comes with +NOVA. For the current Genode release, we took the chance to adopt this +technology for Genode. + +[image img/vancouver] + The Vancouver virtual machine monitor executed as Genode + component + +On Genode, the Vancouver VMM is executed as a normal Genode process. +Consequently, any number of VMM instances can be started either statically via +the init process or dynamically. By bringing Vancouver to Genode we are able to +combine the high performance and secure design of Vancouver with the +flexibility of Genode's component architecture. By combining Genode's session +routing concept with resource multiplexers, virtual machines can be connected +to one another as well as to Genode components. + +That said, the current stage of development is still highly experimental. +But it clearly shows the feasibility of performing faithful virtualization +naturally integrated with a Genode-based system. For more technical details +about the porting work, please refer to Section +[Vancouver virtual machine monitor]; + + +Android paravirtualized +======================= + +Since 2009, Genode embraced the concept of paravirtualization for executing +unmodified Linux applications by the means of the OKLinux kernel. This special +variant of the Linux kernel is modified to run on top of the OKL4 microkernel. +With the added support for the Fiasco.OC kernel as base platform, the use of +L4Linux as another paravirtualized Linux variant became feasible. L4Linux has a +long history reaching back to times long before the term paravirtualization was +eventually coined. In contrast to faithful virtualization, paravirtualization +devises modifications of the kernel of the Guest OS but preserves binary +compatibility of the Guest's user-level software. The proponents of this +approach cite two advantages over faithful virtualization: better performance +and independence from virtualization-hardware support. For example, L4Linux is +available on ARM platforms with no virtualization support. + +L4Linux is the base of L4Android, a project that combines the Android software +stack with L4Linux. With the current release, we have integrated L4Android with +Genode. + +:[http://l4android.org]: + L4Android project website + +[image img/l4android] + Android, a Linux distribution, and a process tree of Genode + components running side by side + +As illustrated in the figure above, multiple Linux instances of both types +Android and plain Linux can be executed as nodes of the Genode process tree. +For their integration with the Genode environment, we extended the L4Linux +kernel with custom stub drivers that make Genode's session interfaces +available as virtual devices. These virtual devices include NIC, UART, +framebuffer, block, keyboard, and pointer devices. Our work on L4Linux is +explained in more detail in Section [L4Linux / L4Android]. + + +OS-level Virtualization using the Noux runtime environment +========================================================== + +Noux is our take on OS-level virtualization. The goal is to be able to use the +wealth of command-line-based UNIX software (in particular GNU) on Genode +without the overhead of running and maintaining a complete guest OS, and +without changing the original source code of the UNIX programs. This work is +primarily motivated by our ongoing mission to use Genode as development +environment. + +[image img/noux] + The Noux runtime environment for UNIX software. The program is linked + against a custom libc plugin that directs system calls over an RPC + interface to the Noux server. The RPC interface resembles a + traditional UNIX system-call API. + +The Noux approach is to provide the traditional UNIX system call interface as +an RPC service, which is at the same time the parent of the UNIX process(es) to +execute. The UNIX process is linked against the libc with a special back end +(libc plugin) that maps libc functionality to Noux RPC calls. This way, the +integration of the UNIX program with Noux is completely transparent to the +program. Because Noux plays the rule of a UNIX kernel, it has to implement +typical UNIX functionality such as a virtual file system (VFS). But in contrast +to a real kernel, it does not comprise any device drivers and protocol stacks. +As file system, we currently use TAR archives that Noux obtains from core's ROM +service. The content of such a TAR archive is exposed to the UNIX program as +file system. + +One of the most prevalent pieces of UNIX software we spend the most of the day +with is VIM. Hence, we set the goal for this release to execute VIM via Noux on +Genode. This particular program is interesting for several technical reasons as +well. First, in contrast to most command-line tools such as coreutils, it is +interactive and implements its event loop via the 'select' system. This +provided us with the incentive to implement 'select' in Noux. Second, with far +more than 100,000 lines of code, VIM not a toy but a highly complex and +advanced UNIX tool. Third, its user interface is based on ncurses, which +requires a fairly complete terminal emulator. Consequently, conducting the +development of Noux implied working with and understanding several components +in parallel including Noux itself, the libc, the Noux libc plugin, ncurses, +VIM, and the terminal emulator. Our undertaking was successful. We are now able +to run VIM without modifications and manual porting work on Genode. + +The novelty of Noux compared to other OS-level virtualization approaches such +OpenVZ and FreeBSD jails lies in the degree of isolation it provides and the +simplicity of implementation. Noux instances are isolated from each other +by microkernel mechanisms. Therefore the isolation between two instances does +not depend on a relatively large kernel but on an extremely small trusted +computing base of less than 35,000 lines of code. At the same time, the +implementation turned out to be strikingly simple. Thanks to the existing APIs +provided by the Genode framework, the Noux server is implemented in less than +2,000 lines of code. We are thrilled to learn that this low amount of code +suffices to bring complex UNIX software such as VIM to life. + +For more technical details about our work on this topic, please refer to +the Sections [Noux] and [Framebuffer-based virtual terminal and ncurses]. + + +GDB debugging via application-level virtualization +================================================== + +The work described in the previous sections was motivated with the desire to +re-use existing software on Genode. This section explores a creative use +of sandboxing facilitating the Genode architecture. + +User-level debugging is a feature that we get repeatedly asked about. Until +now, the answer was pretty long-winded because we use different debugging +facilities on different kernels. However, none of those facilities are +comparable to the convenient debugging tools we know from commodity OSes. +For a long time, we were hesitant to build in debugging support into Genode +because we were afraid to subvert the security of Genode by adding special +debug interfaces short-circuiting security policies. Furthermore, none +of the microkernels we love so dearly featured support for user-level +debugging. So we deferred the topic. Until now. With GDB monitor, we have +found an approach to user-level debugging that is not only completely in +line with Genode's architecture but capitalizes it. Instead of adding +special debugging interfaces to low-level components such as the kernel +and core, we use an approach that we call application-level virtualization. + +[image img/no_gdb] + A Genode process uses low-level services provided by core as well as a + higher-level service implemented as separate process component. + +Each Genode process interacts with several low-level services provided by +Genode's core, in particular the RAM service for allocating memory, the CPU +service to create and run threads, and the RM service to manage the virtual +address space of the process. However, the process does not contact core +directly for those services but requests them via its chain of parents (i.e, +the init process). This gives us the opportunity to route session requests for +those services to alternative implementations. + +[image img/gdb] + GDB monitor transparently intercepts the interaction of a Genode process with + its environment. By virtualizing fundamental core services, GDB monitor + exercises full control over the debugging target. GDB monitor, in turn, + utilizes a separate service component to establish a terminal connection with + a remote GDB. + +When running a Genode process as debugging target, we place an intermediate +component called GDB monitor between the process and its parent. Because +GDB monitor is now the parent of the debugging target, all session requests +including those for core services are issued at GDB monitor. Instead of routing +those session requests further down the tree towards core, GDB monitor +provides a local implementation of these specific services and can thereby +observe and intercept all interactions between the process and core. In +particular, GDB monitor gains access to all memory objects allocated from the +debugging target's RAM session, it knows about the address space layout, and +becomes aware of all threads created by the debugging target. Because, GDB +monitor does possess the actual capabilities to the debugging target's CPU +session, it can execute control over those threads, i.e., pausing them or +enabling single-stepping. By combining the information about the debugging +target's address space layout and the access to its memory objects, GDB monitor +is even able to transparently change arbitrary memory in the debugging target, +for example, inserting breakpoint instructions into its text segment. + +Thanks to application-level virtualization, the once deemed complicated problem +of user-level debugging has become straight forward to address. The solution +fits perfectly into the Genode concept, maintains its security, and does not +require special-purpose debugging hooks in the foundation of the operating +system. + +To get a more complete picture about our work on GDB monitor, please read on at +Section [GDB monitor]. + + +Base framework, low-level OS infrastructure +########################################### + +Handling CPU exceptions at user level +===================================== + +To support user-level policies of handling CPU exceptions such as division by +zero or breakpoint instructions, we have added support for reflecting such +exceptions to the CPU session interface. The owner of a CPU session +capability can register a signal handler for each individual thread allocated +from the session. This handler gets notified on the occurrence of an exception +caused by the respective thread similar to how page faults are reflected to +RM sessions. The CPU exception handling support has been implemented on the +Fiasco.OC and OKL4 base platforms. + + +Remote access to thread state +============================= + +Closely related to the new support for handling CPU exceptions, we have +extended the information that can be gathered for threads of a CPU session to +general-purpose register state and the type of the last exception caused by the +thread. Furthermore, the CPU session interface has been extended to allow the +pausing and resumption of threads as well as single-stepping through CPU +instructions (on x86 and ARM). + +All those additions are subject to capability-based security so that only the +one who possesses both a CPU-session capability and a thread capability can +access the thread state. This way, the extension of the CPU session interface +opens up a lot of new possibilities (especially regarding debugging facilities) +without affecting the security of the overall system. + + +Improved signaling latency +========================== + +During the time span of the last two years, we have observed a steadily growing +number of use cases for Genode's signaling framework. Originally used only for +low-frequent notifications, the API has become fundamental to important Genode +mechanisms such as the data-flow protocol employed by the packet-stream +interface, the on-demand fault handling performed by dataspace managers, and +CPU exception handling. This observation provided us with the incentive to +improve the latency of delivering signals by placing signal delivery into a +dedicated thread within core. + + +Optimization for large memory-mapping sizes +=========================================== + +Some kernels, in particular NOVA, are optimized to deal with flexible mapping +sizes that are independent of physical page sizes. The internal representation +of memory mapping independent on page sizes can greatly reduce the memory +footprint needed to keep track memory mappings, but only if large mappings are +used. In practice, mapping sizes are constrained by their sizes and their +alignment in both physical and virtual address spaces. To facilitate the use of +large mappings with NOVA, we have optimized core for the use of large mappings. +Core tries to size-align memory objects in both physical memory and virtual +memory (core-local as well as within normal user processes). This optimization +is completely transparent to the Genode API but positively affects the +page-fault overhead throughout the system, most noticeably the NPT-fault +overhead of the Vancouver VMM. + + +Standard C++ library +==================== + +To accommodate users of the standard C++ library, we used to host the 'stdcxx' +library to the 'libc' repository. Apparently, there are use cases for 'stdcxx' +without Genode's C library, in particular for hybrid Linux/Genode programs that +are linked against the host's glibc anyway. So we decided to move the 'stdcxx' +to the base repository. So if you are needing 'stdcxx' but are not using +Genode's libc, enabling the 'base' repository in your build configuration +suffices. + + +Terminal-session interface +========================== + +We revised the terminal session interface by adding a simple startup-protocol +for establishing terminal connections: At session-creation time, the terminal +session may not be ready to use. For example, a TCP terminal session needs an +established TCP connection first. However, we do not want the session-creation +to be blocked on the server side because this would render the server's +entry point unavailable for all other clients until the TCP connection is +ready. Instead, we deliver a 'connected' signal to the client emitted when the +session becomes ready to use. The Terminal::Connection waits for this signal +at construction time. Furthermore, we extended the terminal session interface +with the new 'avail()' and 'size()' functions. The 'avail()' function can be +used to query for the availability of new characters. The 'size()' function +returns the size of the terminal (number of row and columns). + + +Dynamic linker +============== + +The dynamic linker underwent several changes in its memory management. Since +the original FreeBSD implementation heavily relies on UNIX-like 'mmap' +semantics, which up to this point was poorly emulated using Genode primitives, +we abandoned this emulation completely. In the current setup the linker +utilizes Genode's managed-dataspace concept in order to handle the loading of +binaries and shared libraries and thus implements the required memory +management correctly. + +Also the linker has been adapted to Genode's new GCC 4.6.1 tool chain, which +introduced new relocation types on the ARM platform. + + +Libraries and applications +########################## + +C runtime +========= + +We have updated our FreeBSD-based C library to version 8.2.0 and thereby +changed the way how the libc is integrated with Genode. Instead of hosting +the 3rd-party code as part of the Genode source tree in the form of the 'libc' +repository, the libc has now become part of the 'libports' repository. This +repository does not contain the actual 3rd-party source code but only the +rules of how to download and integrate the code. These steps are fully +automated via the 'libports/Makefile'. Prior using the libc, please make sure +to prepare the 'libc' package within the 'libports' repository: + +! cd /libports +! make prepare PKG=libc + + +Vancouver virtual machine monitor +================================= + +Vancouver is a virtual machine monitor specifically developed for the use with +the NOVA hypervisor. It virtualizes a 32-bit x86 PC hardware including various +peripherals. + +The official project website is [http://hypervisor.org]. Vancouver relies +on hardware virtualization support such as VMX (Intel) or SVM (AMD). +With the current release, we have added the first version of our port of +Vancouver to Genode to the 'ports' repository. To download the Vancouver +source code, simply issue the following command from within the 'ports' +repository: +! make prepare PKG=vancouver + +The glue code between Vancouver and Genode is located at 'ports/src/vancouver/'. +At the current stage, the port is not practically usable but it demonstrates +well the general way of how the VMM code is meant to interact with Genode. + +In contrast to the original Vancouver, which obtains its configuration from +command-line arguments, the Genode version reads the virtual machine +description as XML data supplied via Genode's config mechanism. For each +component declared within the '' node in the config XML file, a new +instance of a respective device model or host driver is instantiated. An +example virtual machine configuration looks like this: + +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! + +The virtual BIOS of Vancouver has built-in support for loading an OS from +multiboot modules. On Genode, the list of boot modules is supplied with +the '' node: + +! +! +! +! +! +! + +This configuration tells Vancouver to fetch the declared boot modules from +Genode's ROM service. + +For the current version, we decided to support SVM-based hardware only +for the mere reason that it is well supported by recent Qemu versions. At the +time of the release, the SVM exit conditions CPUID, IOIO, MSR, NPT, INVALID, +STARTUP are handled. Of those conditions, the handling of NPT (nested page +table) faults was the most challenging part because of the tight interplay +between the NOVA hypervisor and the VMM at this point. + +With this code in place, Vancouver is already able to boot unmodified +L4ka::Pistachio or Fiasco.OC kernels. Both kernels print diagnostic output over +the virtual comport and finally wait for the first timer interrupt. + +Even though the current version has no practical use yet, it is a great +starting point to dive deeper into the topic of faithful virtualization with +Genode. For a quick example of running Vancouver, please refer to the +run script at 'ports/run/vancouver.run'. + +We want to thank Bernhard Kauer and Udo Steinberg for having been extremely +responsive to our questions, making the realization of this first version +possible at a much shorter time frame than we expected. + + +TCP terminal +============ + +The new TCP terminal located at 'gems/src/server/tcp_terminal' is a service +that provides Genode's terminal-session interface via individual TCP +connections. It supports multiple clients. The TCP port to be used for each +client is defined as session policy in the config node of the TCP terminal +server: + +! +! +! +! + +For an example of how to use the TCP terminal, please refer to the run script +at 'gems/run/tcp_terminal.run'. + + +Framebuffer-based virtual terminal and ncurses +============================================== + +We significantly advanced the implementation of our custom terminal emulator to +be found at 'gems/src/server/terminal'. The new version supports all terminal +capabilities needed to display and interact with ncurses-based applications +(e.g., VIM) including support for function keys, colors, key repeat, and +multiple keyboard layouts. The us-english keyboard layout is used by default. +The German keyboard layout can be enabled using the '' config node: + +! +! +! + +As built-in font, the terminal uses the beautiful 'Terminus' monospaced font. + +Our port of the ncurses library that comes as 'libports' package has become +fully functional when combined with the terminal emulator described above. +It is tuned to use the Linux terminal capabilities. + + +Noux +==== + +Noux is a runtime environment that imitates the behavior of a UNIX kernel +but runs as plain Genode program. Our goal for this release was to be +able to execute VIM with no source-code modifications within Noux. + +For pursuing this goal, we changed the I/O back end for the initial file +descriptors of the Noux init process to use the bidirectional terminal-session +interface rather than the unidirectional LOG session interface. Furthermore, +as VIM is an interactive program, it relies on a working 'select' call. So +this system call had to be added to the Noux session interface. Because +'select' is the first of Noux system calls that is able to block, support +for blocking Noux processes had to be implemented. Furthermore, we improved +the handling of environment variables supplied to the Noux init process, +the handling of certain 'ioctl' functions, and hard links in TAR archives +that Noux uses as file system. + +We are happy to report that thanks to Noux we have become able to run VIM on +Genode now! If you like to give it a spin, please try out the run script +'ports/run/noux_vim.run'. + + +GDB monitor +=========== + +Support of user-level debugging is a topic we get repeatedly asked about. It +is particularly hard because the debugging facilities exposed by operating +systems highly depend on the respective kernel whereas most open-source +microkernels offer no user-level debugging support at all. + +The current release will hopefully make the life of Genode application +developers much easier. Our new GDB monitor component allows GDB to be +connected to an individual Genode process as a remote target (via UART or TCP +connection). Thereby, a wide range of convenient debugging features become +available, for example + +* The use of breakpoints, +* Single-stepping through assembly instructions or at function level, +* Source-level debugging, +* Printing of backtraces and call-frame inspection, and +* Observing different threads of a Genode process + +Both statically linked programs as well as programs that use shared libraries +are supported, which we see as a significant improvement over the traditional +debugging tools. Please note, however, that, at the current stage, the feature +is only supported on a small subset of Genode's base platforms (namely +Fiasco.OC and partially OKL4). We will extend the support to other platforms +depending on public demand. + +The work on GDB monitor required us to approach the topic in a quite holistic +manner, adding general support for user-level debugging to the Fiasco.OC kernel +as well as the Genode base framework, componentizing the facility for +bidirectional communication (terminal session), and creating the actual GDB +support in the form of the GDB monitor component. To paint a complete picture +of the scene, we have added the following documentation: + +:[http://genode.org/documentation/developer-resources/gdb]: + User-level debugging on Genode + + +L4Linux / L4Android +################### + +Update to kernel version 3.0 +============================ + +We have updated our modified version of the L4Linux kernel to support the +latest L4Linux kernel version 3.0.0. Moreover, we fixed some issues with the +kernel's internal memory management under ARM. +Please don't forget to issue a 'make prepare' in the 'ports-foc' directory, +before giving the new version a try. + + +Stub-driver support +=================== + +With this release, we introduce a bunch of new stub-drivers in L4Linux. These +stubs enable the use of Genode's NIC, terminal, and block services from Linux +applications. To use a Genode session from Linux, it's sufficient to provide +an appropriate service to the Linux instance. The stub drivers for these +services simply try to connect to their service and register corresponding +devices if such a service exists. For example, if a NIC session is provided to +Linux, it will provide an ethernet device named 'eth0' to Linux applications. +For terminal sessions, it will provide a serial device named 'ttyS0'. + +In contrast to the NIC and terminal stub drivers, the block driver supports +the usage of more than one block session. Therefore, you have to state the +name of the individual block devices in the configuration. So, it is possible +to state which block device in Linux gets routed to which block session. An +example L4Linux configuration may look like follows + +! +! +! +! +! +! +! +! +! +! +! +! +! ... +! +! + +The configuration provides two block devices in Linux named 'sda' and 'sdb', +whereby the first one gets routed to a child of init named 'rootfs' and the +second to a child named 'home'. Of course, both children have to provide the +block session interface. + + +L4Android +========= + +Besides the support of L4Linux, Genode provides support for a slightly changed +L4Linux variant called L4Android, that combines L4Linux with the Android +kernel patches. To give Android on top of Genode a try, you need to prepare +your 'ports-foc' directory to contain the L4Android contrib code by issuing: + +! make prepare TARGET=l4android + +After that, a run script can be used to build all necessary components, +download the needed Android userland images, assemble everything, and start +the demo via Qemu: + +! make run/l4android + +Beforehand, the 'dde_ipxe' and 'ports-foc' repositories must be enabled in the +'etc/build.conf' file in your build-directory. + +Although, in general Android runs on top of Genode, it seems that the whole +run-time overhead of (no-kvm) full emulation, para-virtualized Linux and Java, +makes it almost unusable within Qemu. Therefore, we strongly advise you to use +the Android version for x86 and turn on KVM support (at least partially). This +can be configured in the 'etc/tools.conf' file in your build directory by +adding the following line: + +! QEMU_OPT = -enable-kvm + +Unfortunately, it hardly depends on your Qemu version and configuration, +whether Fiasco.OC runs problem-free with KVM support enabled. If you encounter +problems, try to use a limited KVM option with Qemu, like '-no-kvm-irqchip', or +'-no-kvm-pit' instead of '-enable-kvm'. + + +Device drivers +############## + +Device-driver environment for iPXE network drivers +================================================== + +Genode used network-card driver code from Linux 2.6 and gPXE. Unfortunately, +the gPXE project seems not very active since mid-2010 and the Linux DDE shows +its age with our new tool chain. Therefore, we switched to the iPXE project, +the official successor of gPXE, for our NIC drivers. Currently, drivers for +Intel E1000/E1000e and PCnet32 cards are enabled in the new 'nic_drv'. The +Linux based driver was removed. + +:[http://ipxe.org]: + iPXE open source boot firmware + + +PL110 display driver +==================== + +The PL110 driver was reworked to support both the PL110 and PL111 controller +correctly. Moreover, it now runs not only within Qemu, but on real hardware +too. At least it is tested on ARM's Versatile Express evaluation board. + + +UART driver +=========== + +The first version of the PL011 UART driver implemented the LOG interface to +cover the primary use case of directing LOG output to different serial ports. +However, with the addition of GDB support with the current release, the use for +UARTs goes beyond unidirectional output. To let GDB monitor communicate with +GDB over a serial line, bidirectional communication is needed. Luckily, there +is a Genode interface for this scenario already, namely the terminal-session +interface. Hence, we changed the PL011 UART driver to provide this interface +type instead of LOG. In addition, we added an UART driver for the i8250 +controller as found on PC hardware. + +Both UART drivers can be configured via the Genode's config mechanism to route +each communication stream to a specific port depending on the session label of +the client. The configuration concept is documented in +'os/src/drivers/uart/README'. For a ready-to-use example of the UART drivers +please refer to the run script 'os/run/uart.run'. + + +Platform support +################ + +NOVA Microhypervisor version 0.4 +================================ + +Genode closely follows the development of the NOVA hypervisor. With version 0.4, +NOVA more and more develops toward a full-featured and production ready +hypervisor. Therefore little effort was needed to enable Genode 11.11 on the +current NOVA kernel. The few changes include a new hypercall (SC control), +capability-based assignment of GSIs (Global System Interrupts), and support for +revoke filtering. Also, we improved Genode's page-mapping behavior to take +advantage of large flexpage sizes. + + +Fiasco.OC microkernel +===================== + +Update to revision 38 +~~~~~~~~~~~~~~~~~~~~~ + +With the current release we switched to the latest available Fiasco.OC kernel +version (revision 38). After having some timing issues on different ARM +platforms with Fiasco.OC in the past, we applied a small patch to correct the +timing behavior of the kernel to the current version. The patch is applied +automatically if executing 'make prepare' in the 'base-foc' repository. + +Querying and manipulating remote threads +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To support usage of GDB on top of Genode for Fiasco.OC, several enhancements +were developed. First, the thread state accessible via the CPU-session +interface has been extended to reflect the whole register set of the +corresponding thread depending to the underlying hardware platform. Moreover, +you can now stop and resume a thread's execution. On the x86 platform, it has +become possible to enable single-stepping mode for a specific thread. All +exceptions of a thread raises are now reflected to the exception handler of +this thread. The exception handler can be set via the CPU-session interface. To +support stop/resume, single stepping and breakpoints on ARM, we had to modify +the Fiasco.OC kernel slightly. All patches to the Fiasco.OC kernel can be found +in 'base-foc/patches' and are automatically applied when issuing 'make +prepare'. + +Versatile Express Cortex-A9x4 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With the current release, we introduce support for a new platform on top of +Fiasco.OC. The ARM Versatile Express evaluation board with Coretile Cortex +A9x4 now successfully executes the well known Genode demo - have a look at the +'os/run/demo.run' run-script. Unfortunately, this platform is not supported by +most Qemu versions shipped with Linux distributions. Furthermore, our tests +with a Qemu variant that supports Versatile Express and the Fiasco.OC kernel +were not successful. Nevertheless, on a real machine the demo is working +nicely. Other drivers such as PL111 and the mouse driver are untested yet. + + +Linux +===== + +New IPC implementation based on UNIX domain sockets +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On Linux, Genode processes used to communicate via UDP-Sockets. So, any IPC +message could be addressed individually to a specific receiver without +establishing a connection beforehand. However, Genode was never designed to +work transparently over the network and UDP seems overkill for our +requirements. With this release, we tackled this fact by adapting the IPC +framework to use UNIX domain sockets. + +The design uses two sockets per thread - client and server socket. The server +socket is used to receive requests from any client on 'Ipc_server::_wait' and +accessible as 'ep--server', just as the former UDP port. After +processing the request, the server replies to the requesting client and returns +to wait state. On 'Ipc_client::_call', the client socket is used to send the +request and receive the response. + +This sounds trivial but marks an important step away from IPC istream and +ostream based design to explicit handling of RPC client and server roles. Linux +(as well as other platforms) will benefit from this in planned future changes. + +During the design, we also addressed two other issues: + +Child processes in Linux inherit open file descriptors from their parents. +Therefore, children further down the Genode tree formerly had plenty of open +files. The current implementation uses the close-on-exec flag to advise the +Linux kernel to close the marked file descriptors before executing the new +program on 'execve()'. + +Genode resources appeared in the file system just under '/tmp', e.g., +dataspaces as 'ds--'. This always filled the global temp +directory with dozens of Genode-specific files. Now, Genode resources are +located in '/tmp/genode-'. + + +Support for manually managing local sub address spaces +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On microkernel-based platforms, Genode provides a powerful mechanism to manage +parts of a process' address space remotely (from another process called +dataspace manager) or locally. In contrast to those platforms, however, the +concept of managed dataspaces is not available on Linux because this kernel +lacks a mechanism to remotely manipulate address spaces. Naturally, in the +world of monolithic kernels, it is the task of the kernel to manage the virtual +address spaces of user-level programs. + +The uses of managed dataspaces roughly fall into two categories: the provision +of virtual memory objects (dataspaces) in an on-demand-paged fashion and the +manual management of a part of the local address space. An example for the +latter is the manual management of the thread context area, which must be kept +clean of arbitrary dataspace mappings. Another use case is the manual placement +of shared-library segments within the local address space. Traditionally, we +had to address those problems with special cases on Linux. To support these use +cases in a generic way, we enhanced the memory management support for the Linux +base platform to support so-called sub-RM sessions in the local address space. +A sub RM session is the special case of a managed dataspace that is used only +local to the process and populated eagerly (not on-demand paged). With local +sub-RM sessions, a large part of the virtual memory of the process can be +reserved for attaching dataspaces at manually defined offsets. + +The new example 'base/src/test/sub_rm' illustrates the use of sub RM +sessions. The example works across all platforms. It can be executed using +the run script 'base/run/sub_rm.run'. + + +Improved handling of hybrid Linux/Genode programs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Hybrid Linux/Genode programs are a special breed of programs that use both the +Genode API and Linux libraries found on the host Linux system. Examples for +such programs are the libSDL-based framebuffer driver, the ALSA-based audio +driver, and the tun/tap-based NIC driver - generally speaking, components that +interface Genode with the Linux environment. Because we expected those kind of +programs to be rare, not much attention was paid to make the creation of this +class of programs convenient. Consequently, the target-description files for +them were rather clumsy. However, we discovered that Genode can be used as a +component framework on Linux. In such a scenario, hybrid Linux/Genode programs +are the normal case rather than an exception, which prompted us to reconsider +the role of hybrid Linux/Genode programs within Genode. The changes are: + +* The new 'lx_hybrid' library takes care of the peculiarities of the + linking stage of a hybrid program. In contrast to a plain Genode component, + such a program is linked with the host's linker script and uses the host's + dynamic linker. + +* To support the common case of linking host libraries to a hybrid program, + we introduced the new 'LX_LIBS' variable. For each library listed in + 'LX_LIBS', the build system queries the library's linking requirements using + 'pkg-config'. + +* The startup procedure differs between normal Genode programs and hybrid + programs. Plain Genode programs begin their execution with Genode's startup + code, which performs the initialization of the address space, executes all + static global constructors, and calls the 'main' function. In contrast, + hybrid programs start their execution with the startup code of the host's + libc, leaving the Genode-specific initializations unexecuted. To make sure + that these initializations are performed prior any application code, the + 'lx_hybrid' library contains a highly prioritized constructor called + 'lx_hybrid_init' that performs those initializations as a side effect. + + +L4ka::Pistachio microkernel +=========================== + +The Pistachio kernel recently moved from the Mercurial to the Git (on Github) +version control system. We have updated the configuration and build process of +the kernel and user-land tools within Genode accordingly. + +Genode's 11.11 tool chain also required an adaption of the system call bindings +on x86. This was caused by a change of GCC inline assembler input constraint +handling. GCC now passes memory constraints using stack-relative addressing, +which can lead to serious problems when manipulating the stack pointer within +the assembly code. + + +Build system and tools +###################### + +New tool chain based on GCC 4.6.1 +================================= + +Early on in the genesis of Genode, we introduced a custom tool chain to +overcome several problems inherent to the use of standard tool chains installed +on Linux host platforms. + +First, GCC and binutils versions vary a lot between different Linux systems. +Testing the Genode code with all those different tool chains and constantly +adapting the code to the peculiarities of certain tool-chain versions is +infeasible and annoying. Second, Linux tool chains use certain features that +stand in the way when building low-level system components. For example, the +'-fstack-protector' option is enabled by default on some Linux distributions. +Hence, we have to turn it off when building Genode. However, some tool chains +lack this option. So the attempt to turn it off produces an error. The most +important problem with Linux tool chains is the dependency of their respective +GCC support libraries from the glibc. When not using a Linux glibc, as the case +with Genode, this leads to manifold problems, most of them subtle and extremely +hard to debug. For example, the support libraries expect the Linux way of +implementing thread-local storage (using segment registers on x86_32). This +code will simply crash on other kernels. Another example is the use of certain +C-library functions, which are not available on Genode. Hence, Genode provides +custom implementations of those functions (in the 'cxx' library). +Unfortunately, the set of functions used varies across tool-chain versions. For +these reasons, we introduced a custom configured tool chain where we mitigated +those problems by pinning the tools to certain versions and tweaking the +compiler configuration to our needs (i.e., preventing the use of Linux TLS). + +That said, the use a our custom configured tool chain was not free from +problems either. In particular, the script for creating the tool chain relied +on a libc being present on the host system. The header files of the libc would +be used to build the GCC support libraries. This introduced two problems. When +adding Genode's libc to the picture, which is based on FreeBSD's C library, the +expectations of the GCC support libraries did not match 100% with the semantics +implemented by Genode's libc (e.g., the handling of 'errno' differs). The +second problem is the limitation that the tool chain could only be built for +the platform that corresponds to the host. For example, on a Linux-x86_32 +system, it was not possible to build a x86_64 or ARM tool chain. For this +reason we used the ARM tool chains provided by CodeSourcery. + +With Genode 11.11, we addressed the root of the tool-chain problem by +completely decoupling the Genode tool chain from the host system that is used +to build it. The most important step was the removal of GCC's dependency from +a C library, which is normally needed to build the GCC support libraries. We +were able to remove the libc dependency by sneaking-in a small custom libc stub +into the GCC build process. This stub comes in the form of the single header +file 'tool/libgcc_libc_stub.h' and brings along all type definitions and +function declarations expected by the support-library code. Furthermore, we +removed all GNU-specific heuristics from the tool chain. Technically, the +Genode tool chain is a bare-metal tool chain. But in contrast to existing +bare-metal tool chains, C++ is fully supported. + +With the libc dependency out of the way, we are now free to build the tool +chain for arbitrary architectures, which brings us two immediate benefits. We +do no longer have to rely on the CodeSourcery tool chain for ARM. There is now +a 'genode-arm' tool chain using the same compiler configuration as used on x86. +The second benefit is the use of multiarch libs on the x86 platform. The +genode-x86 tool chain can be used for both x86_32 and x86_64, the latter being +the default. + +The new tool-chain script is located at 'tool/tool_chain'. It takes care of +downloading all components needed and building the tool chain. Currently, the +tool chain supports x86 and ARM. Note that the 'tool_chain' script can be +executed in an arbitrary directory. For example, to build the Genode tool chain +for x86 within your '/tmp/' directory, use the following commands: +! mkdir /tmp/tool_chain +! cd /tmp/tool_chain +! /tool/tool_chain x86 +After completing the build, you will be asked for the superuser password to +enable the installation of the result to '/usr/local/genode-gcc/'. + +Since we introduced GDB support into Genode, we added GDB in addition to GCC +and binutils to the Genode tool chain. The version is supposed to match the one +expected by Genode's GDB facility, avoiding potential problems with mismatching +protocols between GDB monitor and GDB. + + +Optimization of the library-dependency build stage +================================================== + +The Genode build process consists of two stages. The first stage determines +inter-library dependencies using non-parallel recursive make. It traverses +all targets to be built and the libraries those targets depend on, and stores +the dependency information between libraries and targets in the file +'/var/libdeps'. This generated file contains make rules to be +executed in the second build stage, which performs all compilation and linking +steps. In contrast to the first stage, the work-intensive second stage can be +executed in parallel using '-j' argument of make. Because of the serialization +of the first build stage, it naturally does not profit from multiple CPUs. +This should be no problem because the task of the first stage is simple and +supposedly executed quickly. However, for large builds including many +targets, we found the time needed for the first stage to become longer +than expected. This prompted us to do a bit of profiling. + +Tracing the 'execve' syscalls of first build stage via 'strace' evidenced that +the echo commands used for creating the 'var/libdeps' file attributed +significantly to the overall time because each echo involves the spawning of a +shell. There are two counter measures: First, we eagerly detect already visited +libraries and do not visit them again (originally, this detection was performed +later when already revisiting the library). This change improves the +performance by circa 10%. Second and more importantly, the subsequent echo +commands are now batched into one command featuring multiple echoes. This way, +only one shell is started for executing the sequence. This improvement leads to +a performance improvement of about 300%-500%, depending on the size of the +build. Lesson learned: Using make rules as shell scripts is sometimes a bad +idea. + + +Improved libports and ports package handling +============================================ + +We also enhanced the package cleanup rules in 'libports'. Now it's possible +to clean up contribution code package-sensitive analog to the preparation of +packages. If you don't want to tidy up the whole libports repository, but for +instance just the LwIP code, just issue: + +! 'make clean PKG=lwip diff --git a/gems/README b/gems/README new file mode 100644 index 000000000..37ba9c12f --- /dev/null +++ b/gems/README @@ -0,0 +1,6 @@ +This directory is a source-code repository containing Genode-specific +services and applications. In contrast to the components that come with +the 'os' repository, programs contained in 'gems' are able to leverage +the functionalities provided by higher-level repositories such as +'libports', and 'qt4'. To use the 'gems' repository, make sure to +also add those repositories to your build configuraion. diff --git a/gems/include/terminal/character_screen.h b/gems/include/terminal/character_screen.h new file mode 100644 index 000000000..f125c354f --- /dev/null +++ b/gems/include/terminal/character_screen.h @@ -0,0 +1,219 @@ +/* + * \brief Character-screen interface + * \author Norman Feske + * \date 2011-06-06 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TERMINAL__CHARACTER_SCREEN_H_ +#define _TERMINAL__CHARACTER_SCREEN_H_ + +#include + +namespace Terminal { + + /** + * Character-screen interface called by input-stream decoder + */ + struct Character_screen + { + virtual void output(Character c) = 0; + + + /******************* + ** VT Operations ** + *******************/ + + /* + * The VT operations are named according to the command names used by + * their respective terminfo definitions. See 'man 5 terminfo' for a + * thorough description of these commands. + */ + + /** + * Make cursor invisible + */ + virtual void civis() = 0; + + /** + * Make cursor normal + */ + virtual void cnorm() = 0; + + /** + * Make cursor very visible + */ + virtual void cvvis() = 0; + + /** + * Reset string + */ + virtual void cpr() = 0; + + /** + * Change region to line #1 ... line #2 + */ + virtual void csr(int, int) = 0; + + /** + * Non-destructive space - move right #1 spaces + */ + virtual void cuf(int) = 0; + + /** + * Move cursor to row #1 column #2 + */ + virtual void cup(int, int) = 0; + + /** + * Move cursor up one line + */ + virtual void cuu1() = 0; + + /** + * Delete #1 characters + */ + virtual void dch(int) = 0; + + /** + * Delete #1 lines + */ + virtual void dl(int) = 0; + + /** + * Erase #1 characters + */ + virtual void ech(int) = 0; + + /** + * Clear to end of screen + */ + virtual void ed() = 0; + + /** + * Clear to end of line + */ + virtual void el() = 0; + + /** + * Clear to beginning of line + */ + virtual void el1() = 0; + + /** + * Home cursor + */ + virtual void home() = 0; + + /** + * Horizontal position #1 absolute + */ + virtual void hpa(int) = 0; + + /** + * Set a tab in every row, current column + */ + virtual void hts() = 0; + + /** + * Insert #1 characters + */ + virtual void ich(int) = 0; + + /** + * Insert #1 lines + */ + virtual void il(int) = 0; + + /** + * Set all color pairs to the original ones + */ + virtual void oc() = 0; + + /** + * Set default pair to its original value + */ + virtual void op() = 0; + + /** + * Restore cursor to position of last save_cursor + */ + virtual void rc() = 0; + + /** + * Scroll text down + */ + virtual void ri() = 0; + + /** + * Reset string + */ + virtual void ris() = 0; + + /** + * Turn off automatic margins + */ + virtual void rmam() = 0; + + /** + * Exit insert mode + */ + virtual void rmir() = 0; + + /** + * Set background color to #1, using ANSI escape + */ + virtual void setab(int) = 0; + + /** + * Set foreground color to #1, using ANSI escape + */ + virtual void setaf(int) = 0; + + /** + * Set attribute + */ + virtual void sgr(int) = 0; + + /** + * Save current cursor position + */ + virtual void sc() = 0; + + /** + * Turn on automatic margins + */ + virtual void smam() = 0; + + /** + * Enter insert mode + */ + virtual void smir() = 0; + + /** + * Clear all tab stops + */ + virtual void tbc() = 0; + + /** + * User strings + */ + virtual void u6(int, int) = 0; + virtual void u7() = 0; + virtual void u8() = 0; + virtual void u9() = 0; + + /** + * Vertical position #1 absolute) + */ + virtual void vpa(int) = 0; + }; +} + +#endif /* _TERMINAL__CHARACTER_SCREEN_H_ */ diff --git a/gems/include/terminal/character_screen_tracer.h b/gems/include/terminal/character_screen_tracer.h new file mode 100644 index 000000000..bfa16e99a --- /dev/null +++ b/gems/include/terminal/character_screen_tracer.h @@ -0,0 +1,80 @@ +/* + * \brief Tracer of the operations performed by the input-stream decoder + * \author Norman Feske + * \date 2011-07-05 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TERMINAL__CHARACTER_SCREEN_TRACER_H_ +#define _TERMINAL__CHARACTER_SCREEN_TRACER_H_ + +#include + +/** + * Shortcut for character-screen operations with no, one, or two arguments + */ +#define CS_TRACE_OP(name) void name() { printf(#name "\n"); } +#define CS_TRACE_OP_1(name) void name(int p1) { printf(#name "(%d)\n", p1); } +#define CS_TRACE_OP_2(name) void name(int p1, int p2) { printf(#name "(%d,%d)\n", p1, p2); } + +namespace Terminal { + + /** + * Character screen implementation that prints a trace of operations + */ + struct Character_screen_tracer : Character_screen + { + void output(char c) { printf("output('%c')\n", c); } + + CS_TRACE_OP(civis); + CS_TRACE_OP(cnorm); + CS_TRACE_OP(cvvis); + CS_TRACE_OP(cpr); + CS_TRACE_OP_2(csr); + CS_TRACE_OP(cuf1); + CS_TRACE_OP_2(cup); + CS_TRACE_OP(cuu1); + CS_TRACE_OP_1(dch); + CS_TRACE_OP_1(dl); + CS_TRACE_OP_1(ech); + CS_TRACE_OP(ed); + CS_TRACE_OP(el); + CS_TRACE_OP(el1); + CS_TRACE_OP(home); + CS_TRACE_OP_1(hpa); + CS_TRACE_OP(hts); + CS_TRACE_OP_1(ich); + CS_TRACE_OP_1(il); + CS_TRACE_OP(oc); + CS_TRACE_OP(op); + CS_TRACE_OP(rc); + CS_TRACE_OP(ri); + CS_TRACE_OP(ris); + CS_TRACE_OP(rmam); + CS_TRACE_OP(rmir); + CS_TRACE_OP_1(setab); + CS_TRACE_OP_1(setaf); + CS_TRACE_OP_1(sgr); + CS_TRACE_OP(sc); + CS_TRACE_OP(smam); + CS_TRACE_OP(smir); + CS_TRACE_OP(tbc); + CS_TRACE_OP_2(u6); + CS_TRACE_OP(u7); + CS_TRACE_OP(u8); + CS_TRACE_OP(u9); + CS_TRACE_OP_1(vpa); + }; +} + +#undef CS_TRACE_OP +#undef CS_TRACE_OP_1 +#undef CS_TRACE_OP_2 + +#endif /* _TERMINAL__CHARACTER_SCREEN_TRACER_H_ */ diff --git a/gems/include/terminal/decoder.h b/gems/include/terminal/decoder.h new file mode 100644 index 000000000..2f880888f --- /dev/null +++ b/gems/include/terminal/decoder.h @@ -0,0 +1,421 @@ +/* + * \brief Escape-sequence decoder + * \author Norman Feske + * \date 2011-06-06 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TERMINAL__DECODER_H_ +#define _TERMINAL__DECODER_H_ + +#include + +namespace Terminal { + + class Decoder + { + private: + + /** + * Return digit value of character + */ + static inline int digit(char c) + { + if (c >= '0' && c <= '9') return c - '0'; + return -1; + } + + /** + * Return true if character is a digit + */ + static inline bool is_digit(char c) + { + return (digit(c) >= 0); + } + + /** + * Return true if number starts with the specified digit + * + * \param digit digit 0..9 to test for + */ + static inline bool starts_with_digit(int digit, int number) + { + for (; number > 9; number /= 10); + return (number == digit); + } + + /** + * Return number with the first digit removed + */ + static inline int remove_first_digit(int number) + { + int factor = 1; + for (; number/factor > 9; factor *= 10); + return number % factor; + } + + /** + * Buffer used for collecting escape sequences + */ + class Escape_stack + { + public: + + struct Entry + { + enum { INVALID, NUMBER, CODE }; + + int type; + int value; + }; + + struct Number_entry : Entry + { + Number_entry(int number) { type = NUMBER, value = number; } + }; + + struct Code_entry : Entry + { + Code_entry(int code) { type = CODE, value = code; } + }; + + struct Invalid_entry : Entry + { + Invalid_entry() { type = INVALID; } + }; + + private: + + enum { MAX_ENTRIES = 16 }; + Entry _entries[MAX_ENTRIES]; + int _index; + + void _dump() const + { + Genode::printf("--- escape stack follows ---\n"); + for (int i = 0; i < _index; i++) { + int type = _entries[i].type; + int value = _entries[i].value; + Genode::printf("%s %d (0x%x '%c')\n", + type == Entry::INVALID ? " INVALID" : + type == Entry::NUMBER ? " NUMBER " + : " CODE ", + value, value, value); + } + } + + public: + + Escape_stack() : _index(0) { } + + void reset() { _index = 0; } + + void push(Entry const &entry) + { + if (_index == MAX_ENTRIES - 1) { + Genode::printf("Error: escape stack overflow\n"); + _dump(); + reset(); + return; + } + _entries[_index++] = entry; + } + + /** + * Return number of stack elements + */ + unsigned num_elem() const { return _index; } + + /** + * Return Nth stack entry + * + * 'index' is relative to the bottom of the stack. + */ + Entry operator [] (int index) + { + return (index <= _index) ? _entries[index] : Invalid_entry(); + } + + } _escape_stack; + + enum State { + STATE_IDLE, + STATE_ESC_SEQ, /* read escape sequence */ + STATE_ESC_NUMBER /* read number argument within escape sequence */ + } _state; + + Character_screen &_screen; + + int _number; /* current number argument supplied in escape sequence */ + + void _append_to_number(char c) + { + _number = _number*10 + digit(c); + } + + void _enter_state_idle() + { + _state = STATE_IDLE; + _escape_stack.reset(); + } + + void _enter_state_esc_seq() + { + _state = STATE_ESC_SEQ; + _escape_stack.reset(); + } + + void _enter_state_esc_number() + { + _state = STATE_ESC_NUMBER; + _number = 0; + } + + /** + * Try to handle single-element escape sequence + * + * \return true if escape sequence was handled + */ + bool _handle_esc_seq_1() + { + switch (_escape_stack[0].value) { + case '7': return (_screen.sc(), true); + case 'c': return (_screen.ris(), true); + case 'H': return (_screen.hts(), true); + case 'M': return (_screen.ri(), true); + default: return false; + } + } + + bool _handle_esc_seq_2() + { + switch (_escape_stack[0].value) { + + case '[': + + switch (_escape_stack[1].value) { + case 'A': return (_screen.cuu1(), true); + case 'C': return (_screen.cuf(1), true); + case 'c': return (_screen.u9(), true); + case 'H': return (_screen.home(), true); + case 'J': return (_screen.ed(), true); + case 'K': return (_screen.el(), true); + case 'L': return (_screen.il(1), true); + case 'M': return (_screen.dl(1), true); + case 'P': return (_screen.dch(1), true); + case '@': return (_screen.ich(1), true); + case 'R': return (_screen.cpr(), true); + default: return false; + } + break; + + case ']': + + switch (_escape_stack[1].value) { + case 'R': return (_screen.oc(), true); + default : return false; + } + + case 8: + + return (_escape_stack[1].value == 'A') && (_screen.rc(), true); + + default: return false; + } + return false; + } + + bool _handle_esc_seq_3() + { + + /* + * All three-element sequences have the form \E[ + */ + if ((_escape_stack[0].value != '[') + || (_escape_stack[1].type != Escape_stack::Entry::NUMBER)) + return false; + + int const p1 = _escape_stack[1].value; + char const command = _escape_stack[2].value; + + switch (command) { + case 'm': + if (p1 < 30) + return (_screen.sgr(p1), true); + + /* p1 starting with digit '3' -> set foreground color */ + if (starts_with_digit(3, p1)) + return (_screen.setaf(remove_first_digit(p1)), true); + + /* p1 starting with digit '4' -> set background color */ + if (starts_with_digit(4, p1)) + return (_screen.setab(remove_first_digit(p1)), true); + + case 'd': return (_screen.vpa(p1), true); + case 'g': return (p1 == 3) && (_screen.tbc(), true); + case 'G': return (_screen.hpa(p1), true); + case 'h': return (p1 == 4) && (_screen.smir(), true); + case 'K': return (p1 == 1) && (_screen.el1(), true); + case 'l': return (p1 == 4) && (_screen.rmir(), true); + case 'L': return (_screen.il(p1), true); + case 'M': return (_screen.dl(p1), true); + case 'n': return (p1 == 6) && (_screen.u7(), true); + case 'P': return (_screen.dch(p1), true); + case '@': return (_screen.ich(p1), true); + case 'X': return (_screen.ech(p1), true); + case 'C': return (_screen.cuf(p1), true); + + default: return false; + } + } + + bool _handle_esc_seq_4() + { + /* + * All four-element escape sequences have the form + * \E[? + */ + if ((_escape_stack[0].value != '[') + || (_escape_stack[1].value != '?') + || (_escape_stack[2].type != Escape_stack::Entry::NUMBER)) + return false; + + int const p1 = _escape_stack[2].value; + char const command = _escape_stack[3].value; + + switch (command) { + case 'l': + if (p1 == 7) return (_screen.rmam(), true); + if (p1 == 25) return (_screen.civis(), true); + return false; + case 'h': + if (p1 == 7) return (_screen.smam(), true); + if (p1 == 25) return (_screen.cnorm(), true); + return false; + case 'c': + if (p1 == 0) return true; /* appended to cnorm */ + if (p1 == 1) return true; /* appended to civis */ + if (p1 == 6) return (_screen.u8(), true); + if (p1 == 8) return (_screen.cvvis(), true); + return false; + default: return false; + } + } + + bool _handle_esc_seq_5() + { + /* + * All five-element escape sequences have the form + * \E[; + */ + if ((_escape_stack[0].value != '[') + || (_escape_stack[1].type != Escape_stack::Entry::NUMBER) + || (_escape_stack[2].value != ';') + || (_escape_stack[3].type != Escape_stack::Entry::NUMBER)) + return false; + + int const p1 = _escape_stack[1].value; + int const p2 = _escape_stack[3].value; + int const command = _escape_stack[4].value; + + switch (command) { + case 'r': return (_screen.csr(p1, p2), true); + case 'H': return (_screen.cup(p1, p2), true); + case 'm': + if ((p1 == 39) && (p2 == 49)) return (_screen.op(), true); + if ((p1 == 0) && (p2 == 10)) return (_screen.sgr(0), true); + return false; + case 'R': return (_screen.u6(p1, p2), true); + default: return false; + } + } + + public: + + Decoder(Character_screen &screen) + : _state(STATE_IDLE), _screen(screen), _number(0) { } + + void insert(unsigned char c) + { + switch (_state) { + + case STATE_IDLE: + + enum { ESC_PREFIX = 0x1b }; + if (c == ESC_PREFIX) { + _enter_state_esc_seq(); + break; + } + + /* handle special characters */ + + /* handle normal characters */ + _screen.output(c); + + break; + + case STATE_ESC_SEQ: + + /* + * We received the prefix character of an escape sequence, + * collect the escape-sequence elements until we detect the + * completion of the sequence. + */ + + /* check for start of a number argument */ + if (is_digit(c) && !_number) + { + _enter_state_esc_number(); + _append_to_number(c); + break; + } + + /* non-number character of escape sequence */ + _escape_stack.push(Escape_stack::Code_entry(c)); + break; + + case STATE_ESC_NUMBER: + + /* + * We got the first character belonging to a number + * argument of an escape sequence. Keep reading digits. + */ + if (is_digit(c)) { + _append_to_number(c); + break; + } + + /* + * End of number is reached. + */ + + /* push the complete number to the escape stack */ + _escape_stack.push(Escape_stack::Number_entry(_number)); + _number = 0; + + /* push non-number character as commend entry */ + _escape_stack.push(Escape_stack::Code_entry(c)); + + break; + } + + /* + * Check for the completeness of an escape sequence. + */ + if (((_escape_stack.num_elem() == 1) && _handle_esc_seq_1()) + || ((_escape_stack.num_elem() == 2) && _handle_esc_seq_2()) + || ((_escape_stack.num_elem() == 3) && _handle_esc_seq_3()) + || ((_escape_stack.num_elem() == 4) && _handle_esc_seq_4()) + || ((_escape_stack.num_elem() == 5) && _handle_esc_seq_5())) + _enter_state_idle(); + }; + }; +} + +#endif /* _TERMINAL__DECODER_H_ */ diff --git a/gems/include/terminal/types.h b/gems/include/terminal/types.h new file mode 100644 index 000000000..f2a0e5ebc --- /dev/null +++ b/gems/include/terminal/types.h @@ -0,0 +1,127 @@ +/* + * \brief Types used by terminal interfaces + * \author Norman Feske + * \date 2011-07-05 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TERMINAL__TYPES_H_ +#define _TERMINAL__TYPES_H_ + +namespace Terminal { + + /* + * The character definition is wrapped in a compound data structure to make + * the implementation easily changeable to unicode later. + */ + struct Character + { + unsigned char c; + + Character() : c(0) { } + Character(unsigned char c) : c(c) { } + + bool is_valid() const { return c != 0; } + + unsigned char ascii() const { return c; } + }; + + + struct Boundary + { + int const width, height; + Boundary(int width, int height) : width(width), height(height) { } + }; + + + struct Offset + { + int const x, y; + + Offset(int x, int y) : x(x), y(y) { } + }; + + + struct Position + { + int x, y; + + Position() : x(0), y(0) { } + Position(int x, int y) : x(x), y(y) { } + + Position operator + (Offset const &offset) { + return Position(x + offset.x, y + offset.y); } + + bool operator == (Position const &pos) const { + return (pos.x == x) && (pos.y == y); } + + bool operator != (Position const &pos) const { + return (pos.x != x) || (pos.y != y); } + + /** + * Return true if position lies within the specified boundaries + */ + bool lies_within(Boundary const &boundary) const + { + return x >= 0 && x < boundary.width + && y >= 0 && y < boundary.height; + } + }; + + + struct Character_array + { + /** + * Assign character to specified position + */ + virtual void set(Position const &pos, Character c) = 0; + + /** + * Request character at specified position + */ + virtual Character get(Position const &pos) const = 0; + + /** + * Return array boundary + */ + virtual Boundary boundary() const = 0; + }; + + + template + class Static_character_array : public Character_array + { + private: + + Character _array[HEIGHT][WIDTH]; + Boundary const _boundary; + + public: + + Static_character_array() : _boundary(WIDTH, HEIGHT) { } + + void set(Position const &pos, Character c) + { + if (pos.lies_within(_boundary)) + _array[pos.y][pos.x] = c; + } + + Character get(Position const &pos) const + { + if (pos.lies_within(_boundary)) + return _array[pos.y][pos.x]; + else + return Character(); + } + + Boundary boundary() const { return _boundary; } + }; +} + +#endif /* _TERMINAL__TYPES_H_ */ diff --git a/gems/run/tcp_terminal.run b/gems/run/tcp_terminal.run new file mode 100644 index 000000000..f224cd17a --- /dev/null +++ b/gems/run/tcp_terminal.run @@ -0,0 +1,122 @@ +# +# \brief Test for the TCP terminal +# \author Norman Feske +# \date 2011-09-08 +# + +# +# TODO: Add support for Linux via user-level networking (using the +# tun/tap proxy driver at os/src/drivers/nic/linux) +# +if {[have_spec linux]} { + puts "Run script does not support Linux."; exit 0 } + +# +# XXX: Currently, we have no NIC driver for 64-bit +# +if {[have_spec 64bit]} { + puts "Run script does not support 64-bit platforms."; exit 0 } + +# +# Build +# + +build { + core init + drivers/pci drivers/timer drivers/nic + server/tcp_terminal + test/terminal_echo +} + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + +append_if [have_spec pci] config { + + + + } + +append config { + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init timer + nic_drv + ld.lib.so libc.lib.so lwip.lib.so libc_log.lib.so libc_lock_pipe.lib.so + tcp_terminal + test-terminal_echo +} + +# platform-specific modules +lappend_if [have_spec pci] boot_modules pci_drv + +build_boot_image $boot_modules + +# +# Execute test +# + +# qemu config +append qemu_args " -m 128 -nographic " + +append_if [have_spec x86] qemu_args " -net nic,model=pcnet " +append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 " + +append qemu_args " -net user -redir tcp:5555::8888 " + +run_genode_until forever + +# +# Now, connect via 'telnet localhost 5555' +# + +# vi: set ft=tcl : diff --git a/gems/run/terminal_decoder.run b/gems/run/terminal_decoder.run new file mode 100644 index 000000000..ff2bc16e6 --- /dev/null +++ b/gems/run/terminal_decoder.run @@ -0,0 +1,25 @@ +build "core init test/terminal_decoder" + +create_boot_directory + +install_config { + + + + + + + + + + + + + +} + +build_boot_image "core init test-terminal_decoder" + +run_genode_until "--- finished test ---" 10 + +puts "Test succeeded" diff --git a/gems/run/terminal_echo.run b/gems/run/terminal_echo.run new file mode 100644 index 000000000..0d6ce3a09 --- /dev/null +++ b/gems/run/terminal_echo.run @@ -0,0 +1,104 @@ +if {[have_spec linux]} { + puts "\nLinux not supported because of missing UART driver\n" + exit 0 +} + +build { + core init drivers/timer + server/terminal test/terminal_echo + drivers/framebuffer drivers/pci drivers/input +} + +create_boot_directory + +append config { + + + + + + + + + + + + + + + + + +} + +append_if [have_spec sdl] config { + + + + + + + } + +append_if [have_spec pci] config { + + + + } + +append_if [have_spec vesa] config { + + + + } + +append_if [have_spec pl11x] config { + + + + } + +append_if [have_spec ps2] config { + + + + } + +append config { + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init timer terminal test-terminal_echo +} + +# platform-specific modules +lappend_if [have_spec linux] boot_modules fb_sdl +lappend_if [have_spec pci] boot_modules pci_drv +lappend_if [have_spec vesa] boot_modules vesa_drv +lappend_if [have_spec ps2] boot_modules ps2_drv +lappend_if [have_spec pl11x] boot_modules pl11x_drv + + +build_boot_image $boot_modules + +run_genode_until forever + diff --git a/gems/src/server/http_block/README b/gems/src/server/http_block/README new file mode 100644 index 000000000..4e332d370 --- /dev/null +++ b/gems/src/server/http_block/README @@ -0,0 +1,26 @@ +This directory contains a HTTP client that implements Genode's block session +interface as a front-end. This way you can incorporate arbitrary files via. +HTTP requests and export them as a block device within Genode. + + +Usage +----- + +Config file snippet: + +! +! +! +! +! +! +! http://kc86.genode.labs:80/file.iso +! +! +! 2048 +! +! +! + diff --git a/gems/src/server/http_block/http.cc b/gems/src/server/http_block/http.cc new file mode 100644 index 000000000..81aaf2a98 --- /dev/null +++ b/gems/src/server/http_block/http.cc @@ -0,0 +1,305 @@ +/* + * \brief HTTP protocol parts + * \author Sebastian Sumpf + * \date 2010-08-19 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include + +extern "C" { +#include +} + +#include "http.h" + +using namespace Genode; + +/* + * Debugging output + */ +static const int verbose = 0; + +enum { + /* HTTP status codes */ + HTTP_SUCC_OK = 200, + HTTP_SUCC_PARTIAL = 206, + + /* Size of our local buffer */ + HTTP_BUF = 2048, +}; + +/* Tokenizer policy */ +struct Scanner_policy_file +{ + static bool identifier_char(char c, unsigned /* i */) + { + return c != ':' && c != 0 && c != ' ' && c != '\n'; + } +}; + +typedef ::Genode::Token Http_token; + + +void Http::cmd_head() +{ + const char *http_templ = "%s %s HTTP/1.1\r\n" + "Host: %s\r\n" + "\r\n"; + + int length = snprintf(_http_buf, HTTP_BUF, http_templ, "HEAD", _path, _host); + + if (lwip_write(_fd, _http_buf, length) != length) { + PERR("Write error"); + throw Http::Socket_error(); + } +} + + +void Http::connect() +{ + _fd = lwip_socket(AF_INET, SOCK_STREAM, 0); + if (_fd < 0) { + PERR("No socket avaiable"); + throw Http::Socket_error(); + } + + if (lwip_connect(_fd, _info->ai_addr, sizeof(*(_info->ai_addr))) < 0) { + PERR("Connect failed"); + throw Http::Socket_error(); + } +} + + +void Http::reconnect(){ lwip_close(_fd); connect(); } + + +void Http::resolve_uri() +{ + struct addrinfo *info; + if (lwip_getaddrinfo(_host, _port, 0, &info)) { + PERR("Error: Host %s not found", _host); + throw Http::Uri_error(); + } + + env()->heap()->alloc(sizeof(struct addrinfo), &_info); + Genode::memcpy(_info, info, sizeof(struct addrinfo)); +} + + +Genode::size_t Http::read_header() +{ + bool header = true; size_t i = 0; + + while (header) { + if (!lwip_read(_fd, &_http_buf[i], 1)) + throw Http::Socket_closed(); + + /* DEBUG: Genode::printf("%c", _http_buf[i]); */ + + if (i >= 3 && _http_buf[i - 3] == '\r' && _http_buf[i - 2] == '\n' + && _http_buf[i - 1] == '\r' && _http_buf[i - 0] == '\n') + header = false; + + if (++i >= HTTP_BUF) { + PERR("Buffer overflow"); + throw Http::Socket_error(); + } + } + + /* scan for status code */ + Http_token t(_http_buf, i); + for (int count = 0;; t = t.next()) { + + if (t.type() != Http_token::IDENT) + continue; + + if (count) { + ascii_to(t.start(), &_http_ret); + break; + } + + count++; + } + + return i; +} + + +void Http::get_capacity() +{ + cmd_head(); + size_t len = read_header(); + char buf[32]; + Http_token t(_http_buf, len); + + bool key = false; + while (t) { + + if (t.type() != Http_token::IDENT) { + t = t.next(); + continue; + } + + if (key) { + ascii_to(t.start(), &_size); + + if (verbose) + PDBG("File size: %zu bytes", _size); + + break; + } + + t.string(buf, 32); + + if (!Genode::strcmp(buf, "Content-Length", 32)) + key = true; + + t = t.next(); + } +} + + +void Http::do_read(void * buf, size_t size) +{ + size_t buf_fill = 0; + + while (buf_fill < size) { + + int part; + if ((part = lwip_read(_fd, (void *)((addr_t)buf + buf_fill), + size - buf_fill)) <= 0) { + PERR("Error: Reading data (%d)", errno); + throw Http::Socket_error(); + } + + buf_fill += part; + } + + if (verbose) + PDBG("Read %zu/%zu", buf_fill, size); +} + + +Http::Http(char *uri, size_t length) : _port((char *)"80") +{ + env()->heap()->alloc(HTTP_BUF, &_http_buf); + + /* parse URI */ + parse_uri(uri, length); + + /* search for host */ + resolve_uri(); + + /* connect to host */ + connect(); + + /* retrieve file info */ + get_capacity(); +} + + +Http::~Http() +{ + env()->heap()->free(_host, Genode::strlen(_host) + 1); + env()->heap()->free(_path, Genode::strlen(_path) + 2); + env()->heap()->free(_http_buf, HTTP_BUF); + env()->heap()->free(_info, sizeof(struct addrinfo)); +} + + +void Http::parse_uri(char *uri, size_t length) +{ + /* strip possible http prefix */ + const char *http = "http://"; + size_t http_len = Genode::strlen(http); + if (!strcmp(http, uri, http_len)) { + uri += http_len; + length -= http_len; + } + + /* set host and file path */ + size_t i; + for (i = 0; i < length && uri[i] != '/'; i++) ; + + env()->heap()->alloc(i + 1, &_host); + Genode::strncpy(_host, uri, i + 1); + + env()->heap()->alloc(length - i + 1, &_path); + Genode::strncpy(_path, uri + i, length - i + 1); + + /* look for port */ + size_t len = Genode::strlen(_host); + for (i = 0; i < len && _host[i] != ':'; i++) ; + if (i < len) { + _port = &_host[i + 1]; + _host[i] = '\0'; + } + + if (verbose) + PDBG("Port: %s", _port); + +} + + +void Http::cmd_get(size_t file_offset, size_t size, off_t offset) +{ + if (verbose) + PDBG("Read: offs %zu size: %zu I/O offs: %lx", file_offset, size, offset); + + while (true) { + + const char *http_templ = "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Range: bytes=%lu-%lu\r\n" + "\r\n"; + + int length = snprintf(_http_buf, HTTP_BUF, http_templ, _path, _host, + file_offset, file_offset + size - 1); + + if (lwip_write(_fd, _http_buf, length) < 0) { + + if (errno == ESHUTDOWN) + reconnect(); + + if (lwip_write(_fd, _http_buf, length) < 0) + throw Http::Socket_error(); + } + + try { + read_header(); + } catch (Http::Socket_closed) { + reconnect(); + continue; + } + + if (_http_ret != HTTP_SUCC_PARTIAL) { + PERR("Error: Server returned %u", _http_ret); + throw Http::Server_error(); + } + + do_read((void *)(_base_addr + offset), size); + return; + } +} + + +void __attribute__((constructor)) init() +{ + lwip_tcpip_init(); + + if (lwip_nic_init(0, 0, 0)) { + PERR("DHCP failed"); + throw -1; + } +} + diff --git a/gems/src/server/http_block/http.h b/gems/src/server/http_block/http.h new file mode 100644 index 000000000..113f48e6f --- /dev/null +++ b/gems/src/server/http_block/http.h @@ -0,0 +1,122 @@ +/* + * \brief HTTP back-end interface + * \author Sebastian Sumpf + * \date 2010-08-24 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _HTTP_H_ +#define _HTTP_H_ + +#include + +struct addrinfo; + +class Http +{ + typedef Genode::size_t size_t; + typedef Genode::addr_t addr_t; + typedef Genode::off_t off_t; + + private: + + size_t _size; /* number of bytes in file */ + char *_host; /* host name */ + char *_port; /* host port */ + char *_path; /* absolute file path on host */ + char *_http_buf; /* internal data buffer */ + unsigned _http_ret; /* HTTP status code */ + struct addrinfo *_info; /* Resolved address info for host */ + int _fd; /* Socket file handle */ + addr_t _base_addr; /* Address of I/O dataspace */ + + /* + * Send 'HEAD' command + */ + void cmd_head(); + + /* + * Connect to host + */ + void connect(); + + /* + * Re-connect to host + */ + void reconnect(); + + /* + * Set URI of remote file + */ + void parse_uri(char *uri, size_t length); + + /* + * Resolve host + */ + void resolve_uri(); + + /* + * Read HTTP header and parse server-status code + */ + size_t read_header(); + + /* + * Determine remote-file size + */ + void get_capacity(); + + /* + * Read 'size' bytes into buffer + */ + void do_read(void * buf, size_t size); + + public: + + /* + * Constructor (default block size is 512 Bytes, default host port is 80 + */ + Http(char *uri, size_t length); + + /* + * Destructor + */ + ~Http(); + + /** + * Read remote file size + * + * \return Remote file size in bytes + */ + size_t file_size() { return _size; } + + /** + * Set base address of I/O dataspace + * + * \param base_addr Base of dataspace + */ + void base_addr(addr_t base_addr) { _base_addr = base_addr; } + + /** + * Send 'GET' command + * + * \param file_offset Read from offset of remote file + * \param size Number of byts to transfer + * \param offset Offset in I/O dataspace + */ + void cmd_get(size_t file_offset, size_t size, off_t offset); + + /* Exceptions */ + class Exception : public ::Genode::Exception { }; + class Uri_error : public Exception { }; + class Socket_error : public Exception { }; + class Socket_closed : public Exception { }; + class Server_error : public Exception { }; +}; + +#endif /* _HTTP_H_ */ diff --git a/gems/src/server/http_block/main.cc b/gems/src/server/http_block/main.cc new file mode 100644 index 000000000..401bfdcf1 --- /dev/null +++ b/gems/src/server/http_block/main.cc @@ -0,0 +1,289 @@ +/* + * \brief Block interface for HTTP block driver + * \author Sebastian Sumpf + * \date 2010-08-24 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include "http.h" + +using namespace Genode; + +namespace Block { + + class Http_interface + { + private: + + size_t _block_size; + Http *_http; + + public: + + Http_interface() : _block_size(512), _http(0) {} + + static Http_interface* obj() + { + static Http_interface _obj; + return &_obj; + } + + void block_size(size_t block_size) + { + _block_size = block_size; + } + + size_t block_size() { return _block_size; } + + void base_addr(addr_t base_addr) + { + _http->base_addr(base_addr); + } + + void read(size_t block_nr, size_t block_count, off_t offset) + { + _http->cmd_get(block_nr * _block_size, block_count * _block_size, offset); + } + + size_t block_count() + { + return _http->file_size() / _block_size; + } + + void uri(char *uri, size_t length) + { + _http = new(env()->heap()) Http(uri, length); + } + + Http * http_blk() { return _http; } + }; + + class Session_component : public Session_rpc_object + { + private: + + class Tx_thread : public Genode::Thread<8192> + { + private: + + Session_component *_session; + + public: + + Tx_thread(Session_component *session) + : Genode::Thread<8192>("worker"), _session(session) { } + + void entry() + { + using namespace Genode; + + Session_component::Tx::Sink *tx_sink = _session->tx_sink(); + Block::Packet_descriptor packet; + + _session->tx_ready(); + + /* handle requests */ + while (true) { + + /* blocking-get packet from client */ + packet = tx_sink->get_packet(); + if (!packet.valid()) { + PWRN("received invalid packet"); + continue; + } + + packet.succeeded(false); + + switch (packet.operation()) { + + case Block::Packet_descriptor::READ: + + try { + Http_interface::obj()->read(packet.block_number(), + packet.block_count(), + packet.offset()); + packet.succeeded(true); + } + catch (Http::Socket_error) { PERR("socket error"); } + catch (Http::Server_error) { PERR("server error"); } + + break; + + case Block::Packet_descriptor::WRITE: + break; + + default: + PWRN("received invalid packet"); + continue; + } + + /* acknowledge packet to the client */ + if (!tx_sink->ready_to_ack()) + PDBG("need to wait until ready-for-ack"); + tx_sink->acknowledge_packet(packet); + } + } + }; + + private: + + Genode::Dataspace_capability _tx_ds; /* buffer for tx channel */ + Genode::Semaphore _startup_sema; /* thread startup sync */ + Tx_thread _tx_thread; + + public: + + /** + * Constructor + * + * \param tx_ds dataspace used for tx channel + */ + Session_component(Genode::Dataspace_capability tx_ds, + Genode::Rpc_entrypoint &ep) + : Session_rpc_object(tx_ds, ep), _tx_ds(tx_ds), + _startup_sema(0), _tx_thread(this) + { + /* + * Map packet stream + */ + addr_t base = env()->rm_session()->attach(tx_ds); + + Http_interface::obj()->base_addr(base); + + _tx_thread.start(); + _startup_sema.down(); + } + + void info(Genode::size_t *blk_count, Genode::size_t *blk_size, + Operations *ops) + { + *blk_count = Http_interface::obj()->block_count(); + *blk_size = Http_interface::obj()->block_size(); + ops->set_operation(Packet_descriptor::READ); + } + + /** + * Signal indicating that transmit thread is ready + */ + void tx_ready() { _startup_sema.up(); } + }; + + /* + * Allow one client only + */ + + /* + * Shortcut for single-client root component + */ + typedef Genode::Root_component Root_component; + + /** + * Root component, handling new session requests + */ + class Root : public Root_component + { + protected: + + /** + * Always returns the singleton block-session component + */ + Session_component *_create_session(const char *args) + { + using namespace Genode; + + Genode::size_t ram_quota = + Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); + Genode::size_t tx_buf_size = + Arg_string::find_arg(args, "tx_buf_size").ulong_value(0); + + /* delete ram quota by the memory needed for the session */ + Genode::size_t session_size = max((Genode::size_t)4096, + sizeof(Session_component) + + sizeof(Allocator_avl)); + if (ram_quota < session_size) + throw Root::Quota_exceeded(); + + /* + * Check if donated ram quota suffices for both + * communication buffers. Also check both sizes separately + * to handle a possible overflow of the sum of both sizes. + */ + if (tx_buf_size > ram_quota - session_size) { + PERR("insufficient 'ram_quota', got %zd, need %zd", + ram_quota, tx_buf_size + session_size); + throw Root::Quota_exceeded(); + } + + return new (md_alloc()) + Session_component(env()->ram_session()->alloc(tx_buf_size), *ep()); + } + + public: + + Root(Genode::Rpc_entrypoint *session_ep, + Genode::Allocator *md_alloc) + : Root_component(session_ep, md_alloc) { } + }; + + +} + + +static void process_config() +{ + using namespace Genode; + + Xml_node config_node = config()->xml_node(); + + bool uri = false; + for (unsigned i = 0; i < config_node.num_sub_nodes(); ++i) { + + Xml_node file_node = config_node.sub_node(i); + + if (file_node.has_type("uri")) { + Block::Http_interface::obj()->uri(file_node.content_addr(), file_node.content_size()); + uri = true; + } + + if (file_node.has_type("block-size")) { + size_t blk_size; + file_node.value(&blk_size); + Block::Http_interface::obj()->block_size(blk_size); + } + } + + if (!uri) + throw Http::Uri_error(); +} + + +int main() +{ + enum { STACK_SIZE = 4*1024 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "http_block_ep"); + + process_config(); + + static Block::Root block_root(&ep, env()->heap()); + env()->parent()->announce(ep.manage(&block_root)); + sleep_forever(); + + return 0; +} diff --git a/gems/src/server/http_block/target.mk b/gems/src/server/http_block/target.mk new file mode 100644 index 000000000..067a07263 --- /dev/null +++ b/gems/src/server/http_block/target.mk @@ -0,0 +1,5 @@ +TARGET = http_block +SRC_CC = main.cc http.cc + +LIBS = signal server cxx env lwip libc + diff --git a/gems/src/server/tcp_terminal/README b/gems/src/server/tcp_terminal/README new file mode 100644 index 000000000..ad10bd530 --- /dev/null +++ b/gems/src/server/tcp_terminal/README @@ -0,0 +1,12 @@ +TCP terminal is a service that provides Genode's terminal-session interface +via individual TCP connections. It supports multiple clients. The TCP port +to be used for each client is defined in as session policy in the config node +of the TCP server: + +! +! +! +! + +For an example of how to use the TCP terminal, please refer to the run script +at 'gems/run/tcp_terminal.run'. diff --git a/gems/src/server/tcp_terminal/main.cc b/gems/src/server/tcp_terminal/main.cc new file mode 100644 index 000000000..c84a6d004 --- /dev/null +++ b/gems/src/server/tcp_terminal/main.cc @@ -0,0 +1,514 @@ +/* + * \brief Service providing the 'Terminal_session' interface via TCP + * \author Norman Feske + * \date 2011-09-07 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* socket API */ +#include +#include +#include +#include +#include +#include +#include + +static bool const verbose = true; + + +class Open_socket : public Genode::List::Element +{ + private: + + /** + * Socket descriptor for listening to a new TCP connection + */ + int _listen_sd; + + /** + * Socket descriptor for open TCP connection + */ + int _sd; + + /** + * Signal handler to be informed about the established connection + */ + Genode::Signal_context_capability _connected_sigh; + + /** + * Signal handler to be informed about data available to read + */ + Genode::Signal_context_capability _read_avail_sigh; + + /** + * Buffer for incoming data + * + * This buffer is used for synchronizing the reception of data in the + * main thread ('watch_sockets_for_incoming_data') and the entrypoint + * thread ('read'). The main thread fills the buffer if its not already + * occupied and the entrypoint thread consumes the buffer. When the + * buffer becomes occupied, the corresponding socket gets removed from + * the 'rfds' set of 'select()' until a read request from the terminal + * client comes in. + */ + enum { READ_BUF_SIZE = 4096 }; + char _read_buf[READ_BUF_SIZE]; + Genode::size_t _read_buf_bytes_used; + + /** + * Establish remote connection + * + * \return socket descriptor used for the remote TCP connection + */ + static int _remote_listen(int tcp_port) + { + int listen_sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_sd == -1) { + PERR("socket creation failed"); + return -1; + } + + sockaddr_in sockaddr; + sockaddr.sin_family = PF_INET; + sockaddr.sin_port = htons (tcp_port); + sockaddr.sin_addr.s_addr = INADDR_ANY; + + if (bind(listen_sd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) { + PERR("bind to port %d failed", tcp_port); + return -1; + } + + if (listen(listen_sd, 1)) { + PERR("listen failed"); + return -1; + } + + Genode::printf("listening on port %d...\n", tcp_port); + return listen_sd; + } + + public: + + Open_socket(int tcp_port); + + ~Open_socket(); + + /** + * Return socket descriptor for listening to new connections + */ + int listen_sd() const { return _listen_sd; } + + /** + * Return true if all steps of '_remote_listen' succeeded + */ + bool listen_sd_valid() const { return _listen_sd != -1; } + + /** + * Return socket descriptor of the connection + */ + int sd() const { return _sd; } + + /** + * Register signal handler to be notified once we accepted the TCP + * connection + */ + void connected_sigh(Genode::Signal_context_capability sigh) { _connected_sigh = sigh; } + + /** + * Register signal handler to be notified when data is available for + * reading + */ + void read_avail_sigh(Genode::Signal_context_capability sigh) + { + _read_avail_sigh = sigh; + + /* if read data is available right now, deliver signal immediately */ + if (!read_buffer_empty() && _read_avail_sigh.valid()) + Genode::Signal_transmitter(_read_avail_sigh).submit(); + } + + /** + * Accept new connection, defining the connection's socket descriptor + * + * This function is called by the 'select()' thread when a new + * connection is pending. + */ + void accept_remote_connection() + { + struct sockaddr addr; + socklen_t len = sizeof(addr); + _sd = accept(_listen_sd, &addr, &len); + + if (_sd > 0) + Genode::printf("connection established\n"); + + /* + * Inform client about the finished initialization of the terminal + * session + */ + if (_connected_sigh.valid()) + Genode::Signal_transmitter(_connected_sigh).submit(); + } + + /** + * Return true if TCP connection is established + * + * If the return value is false, we are still in listening more + * and have not yet called 'accept()'. + */ + bool connection_established() const { return _sd != -1; } + + /** + * Fetch data from socket into internal read buffer + */ + void fill_read_buffer_and_notify_client() + { + if (_read_buf_bytes_used) { + PWRN("read buffer already in use"); + return; + } + + /* read from socket */ + _read_buf_bytes_used = ::read(_sd, _read_buf, sizeof(_read_buf)); + + /* notify client about bytes available for reading */ + if (_read_avail_sigh.valid()) + Genode::Signal_transmitter(_read_avail_sigh).submit(); + } + + /** + * Flush internal read buffer into destination buffer + */ + Genode::size_t flush_read_buffer(char *dst, Genode::size_t dst_len) + { + Genode::size_t num_bytes = Genode::min(dst_len, _read_buf_bytes_used); + Genode::memcpy(dst, _read_buf, num_bytes); + _read_buf_bytes_used = 0; + return num_bytes; + } + + /** + * Return true if the internal read buffer is ready to receive data + */ + bool read_buffer_empty() const { return _read_buf_bytes_used == 0; } +}; + + +class Open_socket_pool +{ + private: + + /** + * Protection for '_list' + */ + Genode::Lock _lock; + + /** + * List of open sockets + */ + Genode::List _list; + + /** + * Number of currently open sockets + */ + int _count; + + /** + * Pipe used to synchronize 'select()' loop with the entrypoint thread + */ + int sync_pipe_fds[2]; + + /** + * Intercept the blocking state of the current 'select()' call + */ + void _wakeup_select() + { + char c = 0; + ::write(sync_pipe_fds[1], &c, sizeof(c)); + } + + public: + + Open_socket_pool() : _count(0) + { + pipe(sync_pipe_fds); + } + + void insert(Open_socket *s) + { + Genode::Lock::Guard guard(_lock); + _list.insert(s); + _count++; + _wakeup_select(); + } + + void remove(Open_socket *s) + { + Genode::Lock::Guard guard(_lock); + _list.remove(s); + _count--; + _wakeup_select(); + } + + void update_sockets_to_watch() + { + _wakeup_select(); + } + + void watch_sockets_for_incoming_data() + { + /* prepare arguments for 'select()' */ + fd_set rfds; + int nfds = 0; + { + Genode::Lock::Guard guard(_lock); + + /* collect file descriptors of all open sessions */ + FD_ZERO(&rfds); + for (Open_socket *s = _list.first(); s; s = s->next()) { + + /* + * If one of the steps of creating the listen socket + * failed, skip the session. + */ + if (!s->listen_sd_valid()) + continue; + + /* + * If the connection is not already established, tell + * 'select' to notify us about a new connection. + */ + if (!s->connection_established()) { + nfds = Genode::max(nfds, s->listen_sd()); + FD_SET(s->listen_sd(), &rfds); + continue; + } + + /* + * The connection is established. We watch the connection's + * file descriptor for reading, but only if our buffer can + * take new data. Otherwise, we let the incoming data queue + * up in the TCP/IP stack. + */ + nfds = Genode::max(nfds, s->sd()); + if (s->read_buffer_empty()) + FD_SET(s->sd(), &rfds); + } + + /* add sync pipe to set of file descriptors to watch */ + FD_SET(sync_pipe_fds[0], &rfds); + nfds = Genode::max(nfds, sync_pipe_fds[0]); + } + + /* block for incoming data or sync-pipe events */ + select(nfds + 1, &rfds, NULL, NULL, NULL); + + /* check if we were woken up via the sync pipe */ + if (FD_ISSET(sync_pipe_fds[0], &rfds)) { + char c = 0; + ::read(sync_pipe_fds[0], &c, 1); + return; + } + + /* read pending data from sockets */ + { + Genode::Lock::Guard guard(_lock); + + for (Open_socket *s = _list.first(); s; s = s->next()) { + + /* look for new connection */ + if (!s->connection_established()) { + if (FD_ISSET(s->listen_sd(), &rfds)) + s->accept_remote_connection(); + continue; + } + + /* connection is established, look for incoming data */ + if (FD_ISSET(s->sd(), &rfds)) + s->fill_read_buffer_and_notify_client(); + } + } + } +}; + + +Open_socket_pool *open_socket_pool() +{ + static Open_socket_pool inst; + return &inst; +} + + +Open_socket::Open_socket(int tcp_port) +: _listen_sd(_remote_listen(tcp_port)), _sd(-1) +{ + open_socket_pool()->insert(this); +} + + +Open_socket::~Open_socket() +{ + close(_sd); + open_socket_pool()->remove(this); +} + + +namespace Terminal { + + class Session_component : public Genode::Rpc_object, + public Open_socket + { + private: + + Genode::Attached_ram_dataspace _io_buffer; + + public: + + Session_component(Genode::size_t io_buffer_size, int tcp_port) + : + Open_socket(tcp_port), + _io_buffer(Genode::env()->ram_session(), io_buffer_size) + { } + + /******************************** + ** Terminal session interface ** + ********************************/ + + Size size() { return Size(0, 0); } + + bool avail() + { + return !read_buffer_empty(); + } + + Genode::size_t _read(Genode::size_t dst_len) + { + Genode::size_t num_bytes = + flush_read_buffer(_io_buffer.local_addr(), + Genode::min(_io_buffer.size(), dst_len)); + + /* + * If read buffer was in use, look if more data is buffered in + * the TCP/IP stack. + */ + if (num_bytes) + open_socket_pool()->update_sockets_to_watch(); + + return num_bytes; + } + + void _write(Genode::size_t num_bytes) + { + /* sanitize argument */ + num_bytes = Genode::min(num_bytes, _io_buffer.size()); + + /* write data to socket, assuming that it won't block */ + if (::write(sd(), _io_buffer.local_addr(), num_bytes) < 0) + PERR("write error, dropping data"); + } + + Genode::Dataspace_capability _dataspace() + { + return _io_buffer.cap(); + } + + void read_avail_sigh(Genode::Signal_context_capability sigh) + { + Open_socket::read_avail_sigh(sigh); + } + + void connected_sigh(Genode::Signal_context_capability sigh) + { + Open_socket::connected_sigh(sigh); + } + + Genode::size_t read(void *buf, Genode::size_t) { return 0; } + Genode::size_t write(void const *buf, Genode::size_t) { return 0; } + }; + + + class Root_component : public Genode::Root_component + { + protected: + + Session_component *_create_session(const char *args) + { + /* + * XXX read I/O buffer size from args + */ + Genode::size_t io_buffer_size = 4096; + + try { + Genode::Session_policy policy(args); + + unsigned tcp_port = 0; + policy.attribute("port").value(&tcp_port); + return new (md_alloc()) + Session_component(io_buffer_size, tcp_port); + + } catch (Genode::Xml_node::Nonexistent_attribute) { + PERR("Missing \"port\" attribute in policy definition"); + throw Genode::Root::Unavailable(); + } catch (Genode::Session_policy::No_policy_defined) { + PERR("Invalid session request, no matching policy"); + throw Genode::Root::Unavailable(); + } + } + + public: + + /** + * Constructor + */ + Root_component(Genode::Rpc_entrypoint *ep, + Genode::Allocator *md_alloc) + : + Genode::Root_component(ep, md_alloc) + { } + }; +} + + +int main() +{ + using namespace Genode; + + Genode::printf("--- TCP terminal started ---\n"); + + /* initialize entry point that serves the root interface */ + enum { STACK_SIZE = 4*4096 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "terminal_ep"); + + static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session()); + + /* create root interface for service */ + static Terminal::Root_component root(&ep, &sliced_heap); + + /* announce service at our parent */ + env()->parent()->announce(ep.manage(&root)); + + for (;;) + open_socket_pool()->watch_sockets_for_incoming_data(); + + return 0; +} diff --git a/gems/src/server/tcp_terminal/target.mk b/gems/src/server/tcp_terminal/target.mk new file mode 100644 index 000000000..fe60b844c --- /dev/null +++ b/gems/src/server/tcp_terminal/target.mk @@ -0,0 +1,3 @@ +TARGET = tcp_terminal +SRC_CC = main.cc +LIBS = env cxx server libc libc_lwip_nic_dhcp libc_log libc_lock_pipe diff --git a/gems/src/server/terminal/main.cc b/gems/src/server/terminal/main.cc new file mode 100644 index 000000000..092090700 --- /dev/null +++ b/gems/src/server/terminal/main.cc @@ -0,0 +1,1367 @@ +/* + * \brief Terminal service + * \author Norman Feske + * \date 2011-08-11 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +static bool const verbose = false; + +enum { READ_BUFFER_SIZE = 4096 }; + +class Read_buffer : public Ring_buffer +{ + private: + + Genode::Signal_context_capability _sigh_cap; + + public: + + /** + * Register signal handler for read-avail signals + */ + void sigh(Genode::Signal_context_capability cap) + { + _sigh_cap = cap; + } + + /** + * Add element into read buffer and emit signal + */ + void add(unsigned char c) + { + Ring_buffer::add(c); + + if (_sigh_cap.valid()) + Genode::Signal_transmitter(_sigh_cap).submit(); + } + + void add(char const *str) + { + while (*str) + add(*str++); + } +}; + + +static int do_refresh; + + +inline Pixel_rgb565 blend(Pixel_rgb565 src, int alpha) +{ + Pixel_rgb565 res; + res.pixel = ((((alpha >> 3) * (src.pixel & 0xf81f)) >> 5) & 0xf81f) + | ((( alpha * (src.pixel & 0x07c0)) >> 8) & 0x07c0); + return res; +} + + +inline Pixel_rgb565 mix(Pixel_rgb565 p1, Pixel_rgb565 p2, int alpha) +{ + Pixel_rgb565 res; + + /* + * We substract the alpha from 264 instead of 255 to + * compensate the brightness loss caused by the rounding + * error of the blend function when having only 5 bits + * per channel. + */ + res.pixel = blend(p1, 264 - alpha).pixel + blend(p2, alpha).pixel; + return res; +} + +/* + * Font initialization + */ +extern char _binary_mono_tff_start; + +static Font mono_font (&_binary_mono_tff_start); + +static Font *fonts[4] = { &mono_font, &mono_font, + &mono_font, &mono_font }; + +enum Font_face { FONT_REGULAR, FONT_ITALIC, FONT_BOLD, FONT_BOLD_ITALIC }; + + +static Color color_palette[2*8]; + + +struct Char_cell +{ + unsigned char attr; + unsigned char ascii; + + enum { ATTR_COLIDX_MASK = 0x07, + ATTR_CURSOR = 0x10, + ATTR_INVERSE = 0x20, + ATTR_HIGHLIGHT = 0x40 }; + + Char_cell() : attr(0), ascii(0) { } + + Char_cell(unsigned char c, Font_face f, int colidx, bool inv, bool highlight) + : + attr(f | (colidx & ATTR_COLIDX_MASK) + | (inv ? ATTR_INVERSE : 0) + | (highlight ? ATTR_HIGHLIGHT : 0)), + ascii(c) + { } + + Font_face font_face() const { return (Font_face)(attr & 0x3); } + + int colidx() const { return attr & ATTR_COLIDX_MASK; } + bool inverse() const { return attr & ATTR_INVERSE; } + bool highlight() const { return attr & ATTR_HIGHLIGHT; } + + Color fg_color() const + { + Color col = color_palette[colidx() + (highlight() ? 8 : 0)]; + + if (inverse()) + col = Color(col.r/2, col.g/2, col.b/2); + + return col; + } + + Color bg_color() const + { + Color col = color_palette[colidx() + (highlight() ? 8 : 0)]; + + if (inverse()) + return Color((col.r + 255)/2, (col.g + 255)/2, (col.b + 255)/2); + else + return Color(0, 0, 0); + } + + void set_cursor() { attr |= ATTR_CURSOR; } + void clear_cursor() { attr &= ~ATTR_CURSOR; } + + bool has_cursor() const { return attr & ATTR_CURSOR; } +}; + + +class Char_cell_array +{ + private: + + unsigned _num_cols; + unsigned _num_lines; + Genode::Allocator *_alloc; + Char_cell **_array; + bool *_line_dirty; + + typedef Char_cell *Char_cell_line; + + void _clear_line(Char_cell_line line) + { + for (unsigned col = 0; col < _num_cols; col++) + *line++ = Char_cell(); + } + + void _mark_lines_as_dirty(int start, int end) + { + for (int line = start; line <= end; line++) + _line_dirty[line] = true; + } + + void _scroll_vertically(int start, int end, bool up) + { + /* rotate lines of the scroll region */ + Char_cell_line yanked_line = _array[up ? start : end]; + + if (up) { + for (int line = start; line <= end - 1; line++) + _array[line] = _array[line + 1]; + } else { + for (int line = end; line >= start + 1; line--) + _array[line] = _array[line - 1]; + } + + _clear_line(yanked_line); + + _array[up ? end: start] = yanked_line; + + _mark_lines_as_dirty(start, end); + } + + public: + + Char_cell_array(unsigned num_cols, unsigned num_lines, + Genode::Allocator *alloc) + : + _num_cols(num_cols), + _num_lines(num_lines), + _alloc(alloc) + { + _array = new (alloc) Char_cell_line[num_lines]; + + _line_dirty = new (alloc) bool[num_lines]; + for (unsigned i = 0; i < num_lines; i++) + _line_dirty[i] = false; + + for (unsigned i = 0; i < num_lines; i++) + _array[i] = new (alloc) Char_cell[num_cols]; + } + + void set_cell(int column, int line, Char_cell cell) + { + _array[line][column] = cell; + _line_dirty[line] = true; + } + + Char_cell get_cell(int column, int line) + { + return _array[line][column]; + } + + bool line_dirty(int line) { return _line_dirty[line]; } + + void mark_line_as_clean(int line) + { + _line_dirty[line] = false; + } + + void scroll_up(int region_start, int region_end) + { + _scroll_vertically(region_start, region_end, true); + } + + void scroll_down(int region_start, int region_end) + { + _scroll_vertically(region_start, region_end, false); + } + + void clear(int region_start, int region_end) + { + for (int line = region_start; line <= region_end; line++) + _clear_line(_array[line]); + + _mark_lines_as_dirty(region_start, region_end); + } + + void cursor(Terminal::Position pos, bool enable, bool mark_dirty = false) + { + Char_cell &cell = _array[pos.y][pos.x]; + + if (enable) + cell.set_cursor(); + else + cell.clear_cursor(); + + if (mark_dirty) + _line_dirty[pos.y] = true; + } + + unsigned num_cols() { return _num_cols; } + unsigned num_lines() { return _num_lines; } +}; + + +class Char_cell_array_character_screen : public Terminal::Character_screen +{ + private: + + enum Cursor_visibility { CURSOR_INVISIBLE, + CURSOR_VISIBLE, + CURSOR_VERY_VISIBLE }; + + Char_cell_array &_char_cell_array; + Terminal::Boundary _boundary; + Terminal::Position _cursor_pos; + int _color_index; + bool _inverse; + bool _highlight; + Cursor_visibility _cursor_visibility; + int _region_start; + int _region_end; + + enum { DEFAULT_COLOR_INDEX = 4 }; + + struct Cursor_guard + { + Char_cell_array_character_screen &cs; + + Terminal::Position old_cursor_pos; + + Cursor_guard(Char_cell_array_character_screen &cs) + : + cs(cs), old_cursor_pos(cs._cursor_pos) + { + /* temporarily remove cursor */ + cs._char_cell_array.cursor(old_cursor_pos, false); + } + + ~Cursor_guard() + { + /* restore original cursor */ + cs._char_cell_array.cursor(old_cursor_pos, true); + + /* if cursor position changed, move cursor */ + Terminal::Position &new_cursor_pos = cs._cursor_pos; + if (old_cursor_pos != new_cursor_pos) { + + cs._char_cell_array.cursor(old_cursor_pos, false, true); + cs._char_cell_array.cursor(new_cursor_pos, true, true); + } + } + }; + + public: + + Char_cell_array_character_screen(Char_cell_array &char_cell_array) + : + _char_cell_array(char_cell_array), + _boundary(_char_cell_array.num_cols(), _char_cell_array.num_lines()), + _color_index(DEFAULT_COLOR_INDEX), + _inverse(false), + _highlight(false), + _cursor_visibility(CURSOR_VISIBLE), + _region_start(0), + _region_end(_boundary.height - 1) + { } + + + void _line_feed() + { + Cursor_guard guard(*this); + + _cursor_pos.y++; + + if (_cursor_pos.y > _region_end) { + _char_cell_array.scroll_up(_region_start, _region_end); + _cursor_pos.y = _region_end; + } + } + + void _carriage_return() + { + Cursor_guard guard(*this); + + _cursor_pos.x = 0; + } + + + /******************************** + ** Character_screen interface ** + ********************************/ + + Terminal::Position cursor_pos() const { return _cursor_pos; } + + void output(Terminal::Character c) + { + if (c.ascii() > 0x10) { + Cursor_guard guard(*this); + _char_cell_array.set_cell(_cursor_pos.x, _cursor_pos.y, + Char_cell(c.ascii(), FONT_REGULAR, + _color_index, _inverse, _highlight)); + _cursor_pos.x++; + } + + switch (c.ascii()) { + + case '\r': /* 13 */ + _carriage_return(); + break; + + case '\n': /* 10 */ + _line_feed(); + break; + + case 8: /* backspace */ + { + Cursor_guard guard(*this); + + if (_cursor_pos.x > 0) + _cursor_pos.x--; + + break; + } + + default: + break; + } + + if (_cursor_pos.x >= _boundary.width) { + _carriage_return(); + _line_feed(); + } + } + + void civis() + { + _cursor_visibility = CURSOR_INVISIBLE; + } + + void cnorm() + { + _cursor_visibility = CURSOR_VISIBLE; + } + + void cvvis() + { + _cursor_visibility = CURSOR_VERY_VISIBLE; + } + + void cpr() + { + PDBG("not implemented"); + } + + void csr(int start, int end) + { + /* the arguments are specified use coordinate origin (1, 1) */ + start--; + end--; + + _region_start = Genode::max(start, 0); + _region_end = Genode::min(end, _boundary.height - 1); + + /* preserve invariant of region size >= 0 */ + _region_end = Genode::max(_region_end, _region_start); + } + + void cuf(int dx) + { + Cursor_guard guard(*this); + + _cursor_pos.x += dx; + _cursor_pos.x = Genode::min(_boundary.width - 1, _cursor_pos.x); + } + + void cup(int y, int x) + { + Cursor_guard guard(*this); + + /* top-left cursor position is reported as (1, 1) */ + x--; + y--; + + using namespace Genode; + x = max(0, min(x, _boundary.width - 1)); + y = max(0, min(y, _boundary.height - 1)); + + _cursor_pos = Terminal::Position(x, y); + } + + void cuu1() + { + PWRN("not implemented"); + } + + void dch(int) + { + PDBG("not implemented"); + } + + void dl(int num_lines) + { + /* delete number of lines */ + for (int i = 0; i < num_lines; i++) + _char_cell_array.scroll_up(_cursor_pos.y, _region_end); + } + + void ech(int) + { + PDBG("not implemented"); + } + + void ed() + { + /* clear to end of screen */ + _char_cell_array.clear(_cursor_pos.y, _boundary.height - 1); + } + + void el() + { + /* clear to end of line */ + for (int x = _cursor_pos.x; x < _boundary.width; x++) + _char_cell_array.set_cell(x, _cursor_pos.y, Char_cell()); + } + + void el1() + { + PDBG("not implemented"); + } + + void home() + { + Cursor_guard guard(*this); + + _cursor_pos = Terminal::Position(0, 0); + } + + void hpa(int x) + { + PDBG("not implemented - hpa %d", x); + } + + void hts() + { + PDBG("not implemented"); + } + + void ich(int) + { + PDBG("not implemented"); + } + + void il(int value) + { + Cursor_guard guard(*this); + + if (_cursor_pos.y > _region_end) + return; + + _char_cell_array.cursor(_cursor_pos, false); + + for (int i = 0; i < value; i++) + _char_cell_array.scroll_down(_cursor_pos.y, _region_end); + + _char_cell_array.cursor(_cursor_pos, true); + } + + void oc() + { + PDBG("not implemented"); + } + + void op() + { + PDBG("not implemented"); + } + + void rc() + { + PDBG("not implemented"); + } + + void ri() + { + PDBG("not implemented"); + } + + void ris() + { + PDBG("not implemented"); + } + + void rmam() + { + PDBG("not implemented"); + } + + void rmir() + { + PDBG("not implemented"); + } + + void setab(int value) + { + _inverse = (value != 0); + } + + void setaf(int value) + { + _color_index = value; + } + + void sgr(int value) + { + _highlight = (value & 0x1) != 0; + _inverse = (value & 0x2) != 0; + + /* sgr 0 is the command to reset all attributes, including color */ + if (value == 0) + _color_index = DEFAULT_COLOR_INDEX; + } + + void sc() + { + PDBG("not implemented"); + } + + void smam() + { + PDBG("not implemented"); + } + + void smir() + { + PDBG("not implemented"); + } + + void tbc() + { + PDBG("not implemented"); + } + + void u6(int, int) + { + PDBG("not implemented"); + } + + void u7() + { + PDBG("not implemented"); + } + + void u8() + { + PDBG("not implemented"); + } + + void u9() + { + PDBG("not implemented"); + } + + void vpa(int y) + { + PDBG("not implemented - vpa %d", y); + } +}; + + +template +inline void draw_glyph(Color fg_color, + Color bg_color, + const unsigned char *glyph_base, + unsigned glyph_width, + unsigned glyph_img_width, + unsigned glyph_img_height, + unsigned cell_width, + PT *fb_base, + unsigned fb_width) +{ + PT fg_pixel(fg_color.r, fg_color.g, fg_color.b); + PT bg_pixel(bg_color.r, bg_color.g, bg_color.b); + + unsigned const horizontal_gap = cell_width + - Genode::min(glyph_width, cell_width); + + unsigned const left_gap = horizontal_gap / 2; + unsigned const right_gap = horizontal_gap - left_gap; + + /* + * Clear gaps to the left and right of the character if the character's + * with is smaller than the cell width. + */ + if (horizontal_gap) { + + PT *line = fb_base; + for (unsigned y = 0 ; y < glyph_img_height; y++, line += fb_width) { + + for (unsigned x = 0; x < left_gap; x++) + line[x] = bg_pixel; + + for (unsigned x = cell_width - right_gap; x < cell_width; x++) + line[x] = bg_pixel; + } + } + + /* center glyph horizontally within its cell */ + fb_base += left_gap; + + for (unsigned y = 0 ; y < glyph_img_height; y++) { + for (unsigned x = 0; x < glyph_width; x++) + fb_base[x] = mix(bg_pixel, fg_pixel, glyph_base[x]); + + fb_base += fb_width; + glyph_base += glyph_img_width; + } +} + + +template +static void convert_char_array_to_pixels(Char_cell_array *cell_array, + PT *fb_base, + unsigned fb_width, + unsigned fb_height) +{ + unsigned glyph_height = mono_font.img_h, + glyph_step_x = mono_font.wtab['m']; + + unsigned y = 0; + for (unsigned line = 0; line < cell_array->num_lines(); line++) { + + if (cell_array->line_dirty(line)) { + + if (verbose) + Genode::printf("convert line %d\n", line); + + unsigned x = 0; + for (unsigned column = 0; column < cell_array->num_cols(); column++) { + + Char_cell cell = cell_array->get_cell(column, line); + Font *font = fonts[cell.font_face()]; + unsigned char ascii = cell.ascii; + + if (ascii == 0) + ascii = ' '; + + unsigned char *glyph_base = font->img + font->otab[ascii]; + + unsigned glyph_width = mono_font.wtab[ascii]; + + if (x + glyph_width >= fb_width) break; + + Color fg_color = cell.fg_color(); + Color bg_color = cell.bg_color(); + + if (cell.has_cursor()) { + fg_color = Color( 63, 63, 63); + bg_color = Color(255, 255, 255); + } + + draw_glyph(fg_color, bg_color, + glyph_base, glyph_width, + (unsigned)font->img_w, (unsigned)font->img_h, + glyph_step_x, fb_base + x, fb_width); + + x += glyph_step_x; + } + } + y += glyph_height; + fb_base += fb_width*glyph_height; + + if (y + glyph_height > fb_height) break; + } +} + + +namespace Terminal { + + class Session_component : public Genode::Rpc_object + { + private: + + Read_buffer *_read_buffer; + Framebuffer::Session *_framebuffer; + + Genode::Attached_ram_dataspace _io_buffer; + + int _fb_width; + int _fb_height; + Genode::Dataspace_capability _fb_ds_cap; + unsigned _char_width; + unsigned _char_height; + unsigned _columns; + unsigned _lines; + Framebuffer::Session::Mode _fb_mode; + void *_fb_addr; + + Char_cell_array _char_cell_array; + Char_cell_array_character_screen _char_cell_array_character_screen; + Terminal::Decoder _decoder; + + Terminal::Position _last_cursor_pos; + + /** + * Initialize framebuffer-related attributes + */ + Genode::Dataspace_capability _init_fb() + { + _framebuffer->info(&_fb_width, &_fb_height, &_fb_mode); + + if (_fb_mode != Framebuffer::Session::RGB565) { + PERR("Color mode %d not supported", _fb_mode); + return Genode::Dataspace_capability(); + } + + return _framebuffer->dataspace(); + } + + public: + + /** + * Constructor + */ + Session_component(Read_buffer *read_buffer, + Framebuffer::Session *framebuffer, + Genode::size_t io_buffer_size) + : _read_buffer(read_buffer), _framebuffer(framebuffer), + _io_buffer(Genode::env()->ram_session(), io_buffer_size), + _fb_ds_cap(_init_fb()), + + /* take size of space character as character cell size */ + _char_width(mono_font.str_w("m")), + _char_height(mono_font.str_h("m")), + + /* compute number of characters fitting on the framebuffer */ + _columns(_fb_width/_char_width), + _lines(_fb_height/_char_height), + + _fb_addr(Genode::env()->rm_session()->attach(_fb_ds_cap)), + _char_cell_array(_columns, _lines, Genode::env()->heap()), + _char_cell_array_character_screen(_char_cell_array), + _decoder(_char_cell_array_character_screen) + { + using namespace Genode; + + printf("new terminal session:\n"); + printf(" framebuffer has %dx%d pixels\n", _fb_width, _fb_height); + printf(" character size is %dx%d pixels\n", _char_width, _char_height); + printf(" terminal size is %dx%d characters\n", _columns, _lines); + + framebuffer->refresh(0, 0, _fb_width, _fb_height); + } + + void flush() + { + convert_char_array_to_pixels(&_char_cell_array, + (Pixel_rgb565 *)_fb_addr, + _fb_width, + _fb_height); + + + int first_dirty_line = 10000, + last_dirty_line = -10000; + + for (int line = 0; line < (int)_char_cell_array.num_lines(); line++) { + if (!_char_cell_array.line_dirty(line)) continue; + + first_dirty_line = Genode::min(line, first_dirty_line); + last_dirty_line = Genode::max(line, last_dirty_line); + + _char_cell_array.mark_line_as_clean(line); + } + + int num_dirty_lines = last_dirty_line - first_dirty_line + 1; + + _framebuffer->refresh(0, first_dirty_line*_char_height, + _fb_width, num_dirty_lines*_char_height); + } + + + /******************************** + ** Terminal session interface ** + ********************************/ + + Size size() { return Size(_columns, _lines); } + + bool avail() { return !_read_buffer->empty(); } + + Genode::size_t _read(Genode::size_t dst_len) + { + /* read data, block on first byte if needed */ + unsigned num_bytes = 0; + unsigned char *dst = _io_buffer.local_addr(); + Genode::size_t dst_size = Genode::min(_io_buffer.size(), dst_len); + do { + dst[num_bytes++] = _read_buffer->get();; + } while (!_read_buffer->empty() && num_bytes < dst_size); + + return num_bytes; + } + + void _write(Genode::size_t num_bytes) + { + unsigned char *src = _io_buffer.local_addr(); + + for (unsigned i = 0; i < num_bytes; i++) { + if (verbose) + Genode::printf("%c (%d)\n", src[i], (int)src[i]); + + /* submit character to sequence decoder */ + _decoder.insert(src[i]); + } + + flush(); + } + + Genode::Dataspace_capability _dataspace() + { + return _io_buffer.cap(); + } + + void connected_sigh(Genode::Signal_context_capability sigh) + { + /* + * Immediately reflect connection-established signal to the + * client because the session is ready to use immediately after + * creation. + */ + Genode::Signal_transmitter(sigh).submit(); + } + + void read_avail_sigh(Genode::Signal_context_capability cap) + { + _read_buffer->sigh(cap); + } + + Genode::size_t read(void *buf, Genode::size_t) { return 0; } + Genode::size_t write(void const *buf, Genode::size_t) { return 0; } + }; + + + class Root_component : public Genode::Root_component + { + private: + + Read_buffer *_read_buffer; + Framebuffer::Session *_framebuffer; + + protected: + + Session_component *_create_session(const char *args) + { + Genode::printf("create terminal session\n"); + + /* + * XXX read I/O buffer size from args + */ + Genode::size_t io_buffer_size = 4096; + + return new (md_alloc()) Session_component(_read_buffer, + _framebuffer, + io_buffer_size); + } + + public: + + /** + * Constructor + */ + Root_component(Genode::Rpc_entrypoint *ep, + Genode::Allocator *md_alloc, + Read_buffer *read_buffer, + Framebuffer::Session *framebuffer) + : + Genode::Root_component(ep, md_alloc), + _read_buffer(read_buffer), _framebuffer(framebuffer) + { } + }; +} + + +enum { + BS = 8, + ESC = 27, + TAB = 9, + LF = 10, + UE = 252, /* 'ü' */ + AE = 228, /* 'ä' */ + OE = 246, /* 'ö' */ + PAR = 167, /* '§' */ + DEG = 176, /* '°' */ + SS = 223, /* 'ß' */ +}; + + +static unsigned char usenglish_keymap[128] = { + 0 ,ESC,'1','2','3','4','5','6','7','8','9','0','-','=', BS,TAB, + 'q','w','e','r','t','y','u','i','o','p','[','}', LF, 0 ,'a','s', + 'd','f','g','h','j','k','l',';','\'','`', 0 , 0 ,'z','x','c','v', + 'b','n','m',',','.','/', 0 , 0 , 0 ,' ', 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'7','8','9','-','4','5','6','+','1', + '2','3','0',',', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + LF, 0 ,'/', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +}; + + +/** + * Mapping from ASCII value to another ASCII value when shift is pressed + * + * The table does not contain mappings for control characters. The table + * entry 0 corresponds to ASCII value 32. + */ +static unsigned char usenglish_shift[256 - 32] = { + /* 32 */ ' ', 0 , 0, 0 , 0 , 0 , 0 ,'"', 0 , 0 , 0 , 0 ,'<','_','>','?', + /* 48 */ ')','!','@','#','$','%','^','&','*','(', 0 ,':', 0 ,'+', 0 , 0 , + /* 64 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 80 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'{', 0 ,'}', 0 , 0 , + /* 96 */ '~','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', + /* 112 */ 'P','Q','R','S','T','U','V','W','X','Y','Z', 0 ,'\\', 0 , 0 , 0 , +}; + + +static unsigned char german_keymap[128] = { + 0 ,ESC,'1','2','3','4','5','6','7','8','9','0', SS, 39, BS,TAB, + 'q','w','e','r','t','z','u','i','o','p', UE,'+', LF, 0 ,'a','s', + 'd','f','g','h','j','k','l', OE, AE,'^', 0 ,'#','y','x','c','v', + 'b','n','m',',','.','-', 0 ,'*', 0 ,' ', 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'7','8','9','-','4','5','6','+','1', + '2','3','0',',', 0 , 0 ,'<', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + LF, 0 ,'/', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +}; + + +/** + * Mapping from ASCII value to another ASCII value when shift is pressed + * + * The table does not contain mappings for control characters. The table + * entry 0 corresponds to ASCII value 32. + */ +static unsigned char german_shift[256 - 32] = { + /* 32 */ ' ', 0 , 0, 39 , 0 , 0 , 0 ,'`', 0 , 0 , 0 ,'*',';','_',':', 0 , + /* 48 */ '=','!','"',PAR,'$','%','&','/','(',')', 0 , 0 ,'>', 0 , 0 , 0 , + /* 64 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 80 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,DEG, 0 , + /* 96 */ 0 ,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', + /* 112 */ 'P','Q','R','S','T','U','V','W','X','Y','Z', 0 , 0 , 0 , 0 , 0 , + /* 128 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 144 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 160 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 176 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 192 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 208 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'?', +}; + + +/** + * Mapping from ASCII value to another ASCII value when altgr is pressed + */ +static unsigned char german_altgr[256 - 32] = { + /* 32 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'~', 0 , 0 , 0 , 0 , + /* 48 */'}', 0 ,178,179, 0 , 0 , 0 ,'{','[',']', 0 , 0 ,'|', 0 , 0 , 0 , + /* 64 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 80 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 96 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 112 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 128 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 144 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 160 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 176 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 192 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + /* 208 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'\\', +}; + + +/** + * Mapping from ASCII value to value reported when control is pressed + * + * The table starts with ASCII value 32. + */ +static unsigned char control[256 - 32] = { + /* 32 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 48 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 64 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 96 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + /* 112 */ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, +}; + + +/** + * State machine that translates keycode sequences to terminal characters + */ +class Scancode_tracker +{ + private: + + /** + * Tables containing the scancode-to-character mapping + */ + unsigned char const *_keymap; + unsigned char const *_shift; + unsigned char const *_altgr; + + /** + * Current state of modifer keys + */ + bool _mod_shift; + bool _mod_control; + bool _mod_altgr; + + /** + * Currently pressed key, or 0 if no normal key (one that can be + * encoded in a single 'char') is pressed + */ + unsigned char _last_character; + + /** + * Currently pressed special key (a key that corresponds to an escape + * sequence), or no if no special key is pressed + */ + char const *_last_sequence; + + /** + * Convert keycode to terminal character + */ + unsigned char _keycode_to_latin1(int keycode) + { + if (keycode >= 112) return 0; + + unsigned ch = _keymap[keycode]; + + if (ch < 32) + return ch; + + /* all ASCII-to-ASCII table start at index 32 */ + if (_mod_shift || _mod_control || _mod_altgr) { + ch -= 32; + + /* + * 'ch' is guaranteed to be in the range 0..223. So it is safe to + * use it as index into the ASCII-to-ASCII tables. + */ + + if (_mod_shift) + return _shift[ch]; + + if (_mod_control) + return control[ch]; + + if (_altgr && _mod_altgr) + return _altgr[ch]; + } + + return ch; + } + + public: + + /** + * Constructor + * + * \param keymap table for keycode-to-character mapping + * \param shift table for character-to-character mapping used when + * Shift is pressed + * \param altgr table for character-to-character mapping with AltGr + * is pressed + */ + Scancode_tracker(unsigned char const *keymap, + unsigned char const *shift, + unsigned char const *altgr) + : + _keymap(keymap), + _shift(shift), + _altgr(altgr), + _mod_shift(false), + _mod_control(false), + _mod_altgr(false), + _last_character(0), + _last_sequence(0) + { }; + + /** + * Submit key event to state machine + * + * \param press true on press event, false on release event + */ + void submit(int keycode, bool press) + { + /* track modifier keys */ + switch (keycode) { + case Input::KEY_LEFTSHIFT: + case Input::KEY_RIGHTSHIFT: + _mod_shift = press; + break; + + case Input::KEY_LEFTCTRL: + case Input::KEY_RIGHTCTRL: + _mod_control = press; + break; + + case Input::KEY_RIGHTALT: + _mod_altgr = press; + + default: + break; + } + + /* reset information about the currently pressed key */ + _last_character = 0; + _last_sequence = 0; + + if (!press) return; + + /* convert key codes to ASCII */ + _last_character = _keycode_to_latin1(keycode); + + /* handle special key to be represented by an escape sequence */ + if (!_last_character) { + switch (keycode) { + case Input::KEY_DOWN: _last_sequence = "\E[B"; break; + case Input::KEY_UP: _last_sequence = "\E[A"; break; + case Input::KEY_RIGHT: _last_sequence = "\E[C"; break; + case Input::KEY_LEFT: _last_sequence = "\E[D"; break; + case Input::KEY_HOME: _last_sequence = "\E[1~"; break; + case Input::KEY_INSERT: _last_sequence = "\E[2~"; break; + case Input::KEY_DELETE: _last_sequence = "\E[3~"; break; + case Input::KEY_END: _last_sequence = "\E[4~"; break; + case Input::KEY_PAGEUP: _last_sequence = "\E[5~"; break; + case Input::KEY_PAGEDOWN: _last_sequence = "\E[6~"; break; + case Input::KEY_F1: _last_sequence = "\E[[A"; break; + case Input::KEY_F2: _last_sequence = "\E[[B"; break; + case Input::KEY_F3: _last_sequence = "\E[[C"; break; + case Input::KEY_F4: _last_sequence = "\E[[D"; break; + case Input::KEY_F5: _last_sequence = "\E[[E"; break; + case Input::KEY_F6: _last_sequence = "\E[17~"; break; + case Input::KEY_F7: _last_sequence = "\E[18~"; break; + case Input::KEY_F8: _last_sequence = "\E[19~"; break; + case Input::KEY_F9: _last_sequence = "\E[20~"; break; + case Input::KEY_F10: _last_sequence = "\E[21~"; break; + case Input::KEY_F11: _last_sequence = "\E[23~"; break; + case Input::KEY_F12: _last_sequence = "\E[24~"; break; + } + } + } + + /** + * Output currently pressed key to read buffer + */ + void emit_current_character(Read_buffer &read_buffer) + { + if (_last_character) + read_buffer.add(_last_character); + + if (_last_sequence) + read_buffer.add(_last_sequence); + } + + /** + * Return true if there is a currently pressed key + */ + bool valid() const + { + return (_last_sequence || _last_character); + } +}; + + +int main(int, char **) +{ + using namespace Genode; + + PDBG("--- terminal service started ---"); + + static Framebuffer::Connection framebuffer; + static Input::Connection input; + static Timer::Connection timer; + static Cap_connection cap; + + Dataspace_capability ev_ds_cap = input.dataspace(); + + Input::Event *ev_buf = static_cast + (env()->rm_session()->attach(ev_ds_cap)); + + /* initialize entry point that serves the root interface */ + enum { STACK_SIZE = 4096 }; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "terminal_ep"); + + static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session()); + + /* input read buffer */ + static Read_buffer read_buffer; + + /* initialize color palette */ + color_palette[0] = Color(100, 100, 100); + color_palette[1] = Color(0, 0, 224); + color_palette[2] = Color(0,255, 0); + color_palette[3] = Color(0,255,255); + color_palette[4] = Color(224, 224, 224); + color_palette[5] = Color(255, 0, 255); + color_palette[6] = Color(255, 255, 0); + color_palette[7] = Color(255, 0, 0); + + /* the upper portion of the palette contains highlight colors */ + for (int i = 0; i < 8; i++) { + Color col = color_palette[i]; + col = Color((col.r*2)/3, (col.g*2)/3, (col.b*2)/3); + color_palette[i + 8] = col; + } + + /* create root interface for service */ + static Terminal::Root_component root(&ep, &sliced_heap, + &read_buffer, &framebuffer); + + /* announce service at our parent */ + env()->parent()->announce(ep.manage(&root)); + + /* state needed for key-repeat handling */ + static int const repeat_delay = 170; + static int const repeat_rate = 25; + static int repeat_cnt = 0; + + unsigned char *keymap = usenglish_keymap; + unsigned char *shift = usenglish_shift; + unsigned char *altgr = 0; + + /* + * Read keyboard layout from config file + */ + try { + using namespace Genode; + + if (config()->xml_node().sub_node("keyboard") + .attribute("layout").has_value("de")) { + + keymap = german_keymap; + shift = german_shift; + altgr = german_altgr; + } + } catch (...) { } + + static Scancode_tracker scancode_tracker(keymap, shift, altgr); + + while (1) { + + while (!input.is_pending()) { + enum { PASSED_MSECS = 10 }; + timer.msleep(PASSED_MSECS); + do_refresh = 1; + + if (scancode_tracker.valid()) { + repeat_cnt -= PASSED_MSECS; + + if (repeat_cnt < 0) { + + /* repeat current character or sequence */ + scancode_tracker.emit_current_character(read_buffer); + + /* reset repeat counter according to repeat rate */ + repeat_cnt = repeat_rate; + } + } + } + + unsigned num_events = input.flush(); + + for (Input::Event *event = ev_buf; num_events--; event++) { + + bool press = (event->type() == Input::Event::PRESS ? true : false); + bool release = (event->type() == Input::Event::RELEASE ? true : false); + int keycode = event->keycode(); + + if (press || release) + scancode_tracker.submit(keycode, press); + + if (press) + scancode_tracker.emit_current_character(read_buffer); + + /* setup first key repeat */ + repeat_cnt = repeat_delay; + } + } + return 0; +} diff --git a/gems/src/server/terminal/mono.tff b/gems/src/server/terminal/mono.tff new file mode 100644 index 0000000000000000000000000000000000000000..dcf78949b4c334048588a07b300709b8140f5076 GIT binary patch literal 34824 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 DsbgEv literal 0 HcmV?d00001 diff --git a/gems/src/server/terminal/target.mk b/gems/src/server/terminal/target.mk new file mode 100644 index 000000000..17761a610 --- /dev/null +++ b/gems/src/server/terminal/target.mk @@ -0,0 +1,4 @@ +TARGET = terminal +LIBS = cxx env server signal +SRC_CC = main.cc +SRC_BIN = mono.tff diff --git a/gems/src/test/terminal_decoder/main.cc b/gems/src/test/terminal_decoder/main.cc new file mode 100644 index 000000000..9c27c49df --- /dev/null +++ b/gems/src/test/terminal_decoder/main.cc @@ -0,0 +1,275 @@ +/* + * \brief Test for decoding terminal input + * \author Norman Feske + * \date 2011-07-05 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* Terminal includes */ +#include + + +class Static_character_screen : public Terminal::Character_screen +{ + private: + + Terminal::Character_array &_char_array; + Terminal::Boundary _boundary; + Terminal::Position _cursor_pos; + + public: + + Static_character_screen(Terminal::Character_array &char_array) + : _char_array(char_array), _boundary(_char_array.boundary()) { } + + void dump() const; + + + /******************************** + ** Character_screen interface ** + ********************************/ + + void output(Terminal::Character c) + { +// Genode::printf("output '%c'\n", c.ascii()); + + if (c.ascii() > 0x10) + _char_array.set(_cursor_pos, c); + _cursor_pos.x++; + if (_cursor_pos.x >= _boundary.width) { + _cursor_pos.x = 0; + _cursor_pos.y++; + } + if (_cursor_pos.y >= _boundary.height) { +// _char_array.newline(); + _cursor_pos.y = _boundary.height - 1; + } + } + + void civis() + { + } + + void cnorm() + { + } + + void cvvis() + { + } + + void cpr() + { + } + + void csr(int, int) + { + } + + void cuf(int) + { + if (_cursor_pos.x >= _boundary.width) + _cursor_pos.x++; +// +// _cursor_pos = _cursor_pos + Terminal::Offset(1, 0); + } + + void cup(int y, int x) + { + using namespace Genode; + x = max(0, min(x, _boundary.width - 1)); + y = max(0, min(y, _boundary.height - 1)); + _cursor_pos = Terminal::Position(x, y); + } + + void cuu1() + { +// if (_cursor_pos.y > 0) +// _cursor_pos.y--; + } + + void dch(int) + { + } + + void dl(int) + { + } + + void ech(int) + { + } + + void ed() + { + } + + void el() + { + } + + void el1() + { + } + + void home() + { + _cursor_pos = Terminal::Position(0, 0); + } + + void hpa(int x) + { + PDBG("hpa %d", x); + } + + void hts() + { + } + + void ich(int) + { + } + + void il(int) + { + } + + void oc() + { + } + + void op() + { + } + + void rc() + { + } + + void ri() + { + } + + void ris() + { + } + + void rmam() + { + } + + void rmir() + { + } + + void setab(int) + { + } + + void setaf(int) + { + } + + void sgr(int) + { + } + + void sc() + { + } + + void smam() + { + } + + void smir() + { + } + + void tbc() + { + } + + void u6(int, int) + { + } + + void u7() + { + } + + void u8() + { + } + + void u9() + { + } + + void vpa(int y) + { + PDBG("vpa %d", y); + } +}; + + +void Static_character_screen::dump() const +{ + using namespace Terminal; + + Genode::printf("--- screen dump follows ---\n"); + + Boundary const boundary = _char_array.boundary(); + for (int y = 0; y < boundary.height; y++) { + + char line[boundary.width + 1]; + + for (int x = 0; x < boundary.width; x++) { + + Character c = _char_array.get(Position(x, y)); + if (c.is_valid()) + line[x] = c.ascii(); + else + line[x] = ' '; + } + + line[boundary.width] = 0; + Genode::printf("%s\n", line); + } + + Genode::printf("--- end of screen dump ---\n"); +} + + +extern "C" char _binary_vim_vt_start; +extern "C" char _binary_vim_vt_end; + + +int main(int argc, char **argv) +{ + using namespace Terminal; + + Static_character_array<81, 26> char_array; + + Static_character_screen screen(char_array); + + Decoder decoder(screen); + for (char *c = &_binary_vim_vt_start; c < &_binary_vim_vt_end; c++) { + decoder.insert(*c); + } + + screen.dump(); + + return 0; +} diff --git a/gems/src/test/terminal_decoder/target.mk b/gems/src/test/terminal_decoder/target.mk new file mode 100644 index 000000000..0a87601f7 --- /dev/null +++ b/gems/src/test/terminal_decoder/target.mk @@ -0,0 +1,4 @@ +TARGET = test-terminal_decoder +SRC_CC = main.cc +SRC_BIN = vim.vt +LIBS = cxx env diff --git a/gems/src/test/terminal_decoder/vim.vt b/gems/src/test/terminal_decoder/vim.vt new file mode 100644 index 000000000..3bbc6a66d --- /dev/null +++ b/gems/src/test/terminal_decoder/vim.vt @@ -0,0 +1 @@ +[?25h[?8c[?25h[?0c[?25l[?1c~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ [No Name] 0,0-1 AllVIM - Vi IMprovedversion 7.2.330by Bram Moolenaar et al.Vim is open source and freely distributableHelp poor children in Uganda!type :help iccf for information type :q to exit type :help or  for on-line helptype :help version7 for version info[?25h[?0c[?25l[?1c::[?25h[?0cq[?25l[?1c[?25h[?0c [?25l[?1c[?25h[?0c \ No newline at end of file diff --git a/hello_tutorial/README b/hello_tutorial/README new file mode 100644 index 000000000..1e1639cb4 --- /dev/null +++ b/hello_tutorial/README @@ -0,0 +1,2 @@ +This repository contains the source code of the 'Hello' client-server tutorial +written by Björn Döbel. Please find the document at 'doc/hello_tutorial.txt'. diff --git a/hello_tutorial/config/config b/hello_tutorial/config/config new file mode 100644 index 000000000..80a9c9a61 --- /dev/null +++ b/hello_tutorial/config/config @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/hello_tutorial/doc/hello_tutorial.txt b/hello_tutorial/doc/hello_tutorial.txt new file mode 100644 index 000000000..151ec7e2c --- /dev/null +++ b/hello_tutorial/doc/hello_tutorial.txt @@ -0,0 +1,406 @@ + + Creating your first Genode application + + Björn Döbel and Norman Feske + +Abstract +######## + +This section will give you a step-by-step introduction for writing your first +little client-server application using the Genode OS Framework. We will create +a server that provides two functions to its clients and a client that uses +these functions. The code samples in this section are not necessarily complete. +You can download the complete tutorial source code from the link at the bottom +of this page. + + +Prerequisites +############# + +We assume that you know how to write code and have read: + +Norman Feske and Christian Helmuth: +"Design of the Genode OS Architecture", +_TU Dresden technical report TUD-FI06-07, Dresden, Germany, December 2006_. + +[http://genode-labs.com/publications/bastei-design-2006.pdf] + +so that you have a basic understanding of what Genode is and how things work. +Of course, you will also need to check out Genode before going any further. + + +Setting up the build environment +################################ + +The Genode build system enables developers to create software in different +repositories that don't need to interfere with the rest of the Genode tree. We +will do this for our example now. In the Genode root directory, we create the +following subdirectory structure: + +! hello_tutorial +! hello_tutorial/include +! hello_tutorial/include/hello_session +! hello_tutorial/src +! hello_tutorial/src/hello +! hello_tutorial/src/hello/server +! hello_tutorial/src/hello/client + +In the remaining document when referring to non-absolute directories, these are +local to 'hello_tutorial'. +Now we tell the Genode build system, that there is a new repository. Therefore +we add the path to our new repository to 'build/etc/build.conf': + +! REPOSITORIES += /path/to/your/hello_tutorial + +Later we will place build description files into the tutorial subdirectories so +that the build system can figure out what is needed to build your applications. +You can then build these apps from the 'build' directory using one of the +following commands: + +! make hello +! make hello/server +! make hello/client + +The first command builds both the client and the server whereas the latter two +commands build only the specific target respectively. + +Defining an interface +##################### + +In our example we are going to implement a server providing two functions: +:'void say_hello()': makes the server print "Hello world." +:'int add(int a, int b)': adds two integers and returns the result. + +The interface of a Genode service is called a _session_. We will define it as a +C++ class in 'include/hello_session/hello_session.h' + +!#include +!#include +! +!namespace Hello { +! +! struct Session : public Genode::Session +! { +! static const char *service_name() { return "Hello"; } +! +! virtual void say_hello() = 0; +! virtual int add(int a, int b) = 0; +! +! GENODE_RPC(Rpc_say_hello, void, say_hello); +! GENODE_RPC(Rpc_add, int, add, int, int); +! GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add); +! }; +!} + +As a good practice, we place the Hello service into a dedicated namespace. The +_Hello::Session_ class defines the public interface for our service as well as +the meta information that Genode needs to perform remote procedure calls (RPC) +accross process boundaries. +Furthermore, we use the interface to specify the name of the +service by using the 'service_name' function. This function will later +be used by both the server for announcing the service at its parent and +the client for requesting the creation of a "Hello" session. + +The 'GENODE_RPC' macro is used to declare an RPC function. Its first argument +is a type name that is used to refer to the RPC function. The type name can +be choosen freely. However, it is a good practice to prefix the type name +with 'Rpc_'. The remaining arguments are the return type of the RPC function, +the server-side name of the RPC implementation, and the function arguments. +The 'GENODE_RPC_INTERFACE' macros declares the list of RPC functions that the +RPC interface is comprised of. Under the hood, the 'GENODE_RPC*' macros enrich +the compound class with the type information used to automatically generate the +RPC communication code at compile time. They do not add any members to the +'Session' struct. + + +Writing server code +################### + +Now let's write a server providing the interface defined by _Hello::Session_. + + +Implementing the server side +============================ + +We place the implementation of the session interface into a class called +'Session_component' derived from the 'Rpc_object' class template. By +instantiating this template class with the session interface as argument, the +'Session_component' class gets equipped with the communication code that +will make the server's functions accessible via RPC. + +!#include +!#include +!#include +! +!namespace Hello { +! +! struct Session_component : Genode::Rpc_object +! { +! void say_hello() { +! PDBG("I am here... Hello."); } +! +! int add(int a, int b) { +! return a + b; } +! }; +!} + + +Getting ready to start +====================== + +The server component won't help us much as long as we don't use it in a server +application. Starting a service with Genode works as follows: +* Open a CAP session to our parent, so that we are able to create capabilities. +* Create and announce a root capability to our parent. +* When a client requests our service, the parent invokes the root capability to + create session objects and session capabilities. These are then used by the + client to communicate with the server. + +The class 'Hello::Root_component' is derived from Genode's 'Root_component' +class template. This class defines a '_create_session' method which is called +each time a client wants to establish a connection to the server. This function +is responsible for parsing the parameter string the client hands over to the +server and creating a 'Hello::Session_component' object from these parameters. + +!#include +!#include +! +!namespace Hello { +! +! class Root_component : public Genode::Root_component +! { +! protected: +! +! Session_component *_create_session(const char *args) +! { +! PDBG("creating hello session."); +! return new (md_alloc()) Session_component(); +! } +! +! public: +! +! Root_component(Genode::Rpc_entrypoint *ep, +! Genode::Allocator *allocator) +! : Genode::Root_component(ep, allocator) +! { +! PDBG("Creating root component."); +! } +! }; +!} + +Now we only need a main method that announces the service to our parent: + +!#include +!#include +! +!using namespace Genode; +! +!int main(void) +!{ +! /* +! * Get a session for the parent's capability service, so that we +! * are able to create capabilities. +! */ +! Cap_connection cap; +! +! /* +! * A sliced heap is used for allocating session objects - thereby we +! * can release objects separately. +! */ +! static Sliced_heap sliced_heap(env()->ram_session(), +! env()->rm_session()); +! +! /* +! * Create objects for use by the framework. +! * +! * An 'Rpc_entrypoint' is created to announce our service's root +! * capability to our parent, manage incoming session creation +! * requests, and dispatch the session interface. The incoming RPC +! * requests are dispatched via a dedicated thread. The 'STACK_SIZE' +! * argument defines the size of the thread's stack. The additional +! * string argument is the name of the entry point, used for +! * debugging purposes only. +! */ +! enum { STACK_SIZE = 4096 }; +! static Rpc_entrypoint ep(&cap, STACK_SIZE, "hello_ep"); +! +! static Hello::Root_component hello_root(&ep, &sliced_heap); +! env()->parent()->announce(ep.manage(&hello_root)); +! +! /* +! * We are done with this and only act upon client requests now. +! */ +! sleep_forever(); +! +! return 0; +!} + + +Making it fly +============= + +In order to run our application, we need to perform two more steps: + +Tell the Genode build system that we want to build 'hello_server'. Therefore we +create a 'target.mk' file in 'src/hello/server': + +! TARGET = hello_server +! SRC_CC = main.cc +! LIBS = cxx env server + +To tell the init process to start the new program, we have to add the following +entry to Init's 'config' file, which is located at 'build/bin/config'. + +! +! +! +! +! +! + +Now rebuild 'hello/server', go to 'build/bin', run './core'. + + +Writing client code +################### + +In the next part we are going to have a look at the client-side implementation. +The most basic steps here are: + +* Get a capability for the "Hello" service from our parent +* Invoke RPCs via the obtained capability + + +A client object +=============== + +We will encapsulate the Genode IPC interface in a 'Hello::Session_client' class. +This class derives from 'Hello:Session' and implements a client-side object. +Therefore edit 'include/hello_session/client.h': + +!#include +!#include +!#include +! +!namespace Hello { +! +! struct Session_client : Genode::Rpc_client +! { +! Session_client(Genode::Capability cap) +! : Genode::Rpc_client(cap) { } +! +! void say_hello() +! { +! PDBG("Saying Hello."); +! call(); +! } +! +! int add(int a, int b) +! { +! return call(a, b); +! } +! }; +!} + + +A 'Hello::Session_client' object takes a 'Capability' as constructor argument. +This capability is tagged with the session type and gets passed to the +inherited 'Rpc_client' class. This class contains the client-side communication +code via the 'call' template function. The template argument for 'call' is the +RPC type as declared in the session interface. + + +Client implementation +===================== + +The client-side implementation using the 'Hello::Session_client' object is pretty +straightforward. We request a capability for the Hello service from our parent. +This call blocks as long as the service has not been registered at the parent. +Afterwards, we create a 'Hello::Session_client' object with it and invoke calls. In +addition, we use the Timer service that comes with Genode. This server +enables us to sleep for a certain amount of milliseconds. + +!#include +!#include +!#include +!#include +! +!using namespace Genode; +! +!int main(void) +!{ +! Capability h_cap = +! env()->parent()->session("foo, ram_quota=4K"); +! +! Hello::Session_client h(h_cap); +! +! Timer::Connection timer; +! +! while (1) { +! h.say_hello(); +! timer.msleep(1000); +! +! int foo = h.add(2,5); +! PDBG("Added 2 + 5 = %d", foo); +! timer.msleep(1000); +! } +! +! return 0; +!} + +Compared to the creation of the Timer session, the creation of "Hello" session +looks rather inconvenient and takes multiple lines of code. For this reason, it +is a good practice to supply a convenience wrapper for creating sessions as +used for the timer session. This wrapper is also the right place to for +documenting session-construction arguments and assembling the argument string. +By convention, the wrapper is called 'connection.h' and placed in the directory +of the session interface. For our case, the file +'include/hello_session/connection.h' looks like this: + +!#include +!#include +! +!namespace Hello { +! +! struct Connection : Genode::Connection, Session_client +! { +! Connection() +! : +! /* create session */ +! Genode::Connection(session("foo, ram_quota=4K")), +! +! /* initialize RPC interface */ +! Session_client(cap()) { } +! }; +!} + +With the 'Connection' class in place, we can now use Hello sessions +by just instantiating 'Hello::Connection' objects and invoke +functions directly on such an object. For example: + +!Hello::Connection hello; +!int foo = hello.add(2, 5); + + +Ready, set, go... +================= + +Add a 'target.mk' file with the following content to 'src/hello/client/': + +! TARGET = hello_client +! SRC_CC = main.cc +! LIBS = cxx env + +Add the following entries to your 'config' file: + +! +! +! +! +! +! + +Build 'drivers/timer', and 'hello/client', go to 'build/bin', and run './core' +again. You have now successfully implemented your first Genode client-server +scenario. + diff --git a/hello_tutorial/include/hello_session/client.h b/hello_tutorial/include/hello_session/client.h new file mode 100644 index 000000000..521a5ebf4 --- /dev/null +++ b/hello_tutorial/include/hello_session/client.h @@ -0,0 +1,41 @@ +/* + * \brief Client-side interface of the Hello service + * \author Björn Döbel + * \date 2008-03-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__HELLO_SESSION_H__CLIENT_H_ +#define _INCLUDE__HELLO_SESSION_H__CLIENT_H_ + +#include +#include +#include + +namespace Hello { + + struct Session_client : Genode::Rpc_client + { + Session_client(Genode::Capability cap) + : Genode::Rpc_client(cap) { } + + void say_hello() + { + PDBG("Saying Hello."); + call(); + } + + int add(int a, int b) + { + return call(a, b); + } + }; +} + +#endif /* _INCLUDE__HELLO_SESSION_H__CLIENT_H_ */ diff --git a/hello_tutorial/include/hello_session/connection.h b/hello_tutorial/include/hello_session/connection.h new file mode 100644 index 000000000..357d60f30 --- /dev/null +++ b/hello_tutorial/include/hello_session/connection.h @@ -0,0 +1,34 @@ +/* + * \brief Connection to Hello service + * \author Norman Feske + * \date 2008-11-10 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__HELLO_SESSION__CONNECTION_H_ +#define _INCLUDE__HELLO_SESSION__CONNECTION_H_ + +#include +#include + +namespace Hello { + + struct Connection : Genode::Connection, Session_client + { + Connection() + : + /* create session */ + Genode::Connection(session("foo, ram_quota=4K")), + + /* initialize RPC interface */ + Session_client(cap()) { } + }; +} + +#endif /* _INCLUDE__HELLO_SESSION__CONNECTION_H_ */ diff --git a/hello_tutorial/include/hello_session/hello_session.h b/hello_tutorial/include/hello_session/hello_session.h new file mode 100644 index 000000000..d861a7a1d --- /dev/null +++ b/hello_tutorial/include/hello_session/hello_session.h @@ -0,0 +1,41 @@ +/* + * \brief Interface definition of the Hello service + * \author Björn Döbel + * \date 2008-03-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__HELLO_SESSION__HELLO_SESSION_H_ +#define _INCLUDE__HELLO_SESSION__HELLO_SESSION_H_ + +#include +#include + +namespace Hello { + + struct Session : Genode::Session + { + static const char *service_name() { return "Hello"; } + + virtual void say_hello() = 0; + virtual int add(int a, int b) = 0; + + + /******************* + ** RPC interface ** + *******************/ + + GENODE_RPC(Rpc_say_hello, void, say_hello); + GENODE_RPC(Rpc_add, int, add, int, int); + + GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add); + }; +} + +#endif /* _INCLUDE__HELLO_SESSION__HELLO_SESSION_H_ */ diff --git a/hello_tutorial/run/hello.run b/hello_tutorial/run/hello.run new file mode 100644 index 000000000..b3db48a16 --- /dev/null +++ b/hello_tutorial/run/hello.run @@ -0,0 +1,48 @@ +# +# Build +# + +build { core init hello drivers/timer } + +create_boot_directory + +# +# Generate config +# + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + +} + +# +# Boot image +# + +build_boot_image { core init hello_client hello_server timer } + +append qemu_args " -nographic " + +run_genode_until forever diff --git a/hello_tutorial/src/hello/client/main.cc b/hello_tutorial/src/hello/client/main.cc new file mode 100644 index 000000000..8045cad67 --- /dev/null +++ b/hello_tutorial/src/hello/client/main.cc @@ -0,0 +1,38 @@ +/* + * \brief Test client for the Hello RPC interface + * \author Björn Döbel + * \date 2008-03-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +using namespace Genode; + +int main(void) +{ + Hello::Connection h; + + Timer::Connection timer; + + while (1) { + h.say_hello(); + timer.msleep(1000); + + int foo = h.add(2, 5); + PDBG("Added 2 + 5 = %d", foo); + timer.msleep(1000); + } + + return 0; +} diff --git a/hello_tutorial/src/hello/client/target.mk b/hello_tutorial/src/hello/client/target.mk new file mode 100644 index 000000000..173d307fb --- /dev/null +++ b/hello_tutorial/src/hello/client/target.mk @@ -0,0 +1,3 @@ +TARGET = hello_client +SRC_CC = main.cc +LIBS = cxx env diff --git a/hello_tutorial/src/hello/server/main.cc b/hello_tutorial/src/hello/server/main.cc new file mode 100644 index 000000000..9ccead749 --- /dev/null +++ b/hello_tutorial/src/hello/server/main.cc @@ -0,0 +1,93 @@ +/* + * \brief Main program of the Hello server + * \author Björn Döbel + * \date 2008-03-20 + */ + +/* + * Copyright (C) 2008-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Hello { + + struct Session_component : Genode::Rpc_object + { + void say_hello() { + PDBG("I am here... Hello."); } + + int add(int a, int b) { + return a + b; } + }; + + class Root_component : public Genode::Root_component + { + protected: + + Hello::Session_component *_create_session(const char *args) + { + PDBG("creating hello session."); + return new (md_alloc()) Session_component(); + } + + public: + + Root_component(Genode::Rpc_entrypoint *ep, + Genode::Allocator *allocator) + : Genode::Root_component(ep, allocator) + { + PDBG("Creating root component."); + } + }; +} + + +using namespace Genode; + +int main(void) +{ + /* + * Get a session for the parent's capability service, so that we + * are able to create capabilities. + */ + Cap_connection cap; + + /* + * A sliced heap is used for allocating session objects - thereby we + * can release objects separately. + */ + static Sliced_heap sliced_heap(env()->ram_session(), + env()->rm_session()); + + /* + * Create objects for use by the framework. + * + * An 'Rpc_entrypoint' is created to announce our service's root + * capability to our parent, manage incoming session creation + * requests, and dispatch the session interface. The incoming RPC + * requests are dispatched via a dedicated thread. The 'STACK_SIZE' + * argument defines the size of the thread's stack. The additional + * string argument is the name of the entry point, used for + * debugging purposes only. + */ + enum { STACK_SIZE = 4096 }; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "hello_ep"); + + static Hello::Root_component hello_root(&ep, &sliced_heap); + env()->parent()->announce(ep.manage(&hello_root)); + + /* We are done with this and only act upon client requests now. */ + sleep_forever(); + + return 0; +} diff --git a/hello_tutorial/src/hello/server/target.mk b/hello_tutorial/src/hello/server/target.mk new file mode 100644 index 000000000..a005e0afa --- /dev/null +++ b/hello_tutorial/src/hello/server/target.mk @@ -0,0 +1,3 @@ +TARGET = hello_server +SRC_CC = main.cc +LIBS = cxx env server diff --git a/libports/Makefile b/libports/Makefile new file mode 100644 index 000000000..ccfe6accd --- /dev/null +++ b/libports/Makefile @@ -0,0 +1,69 @@ +# +# \brief Download and unpack upstream library source codes +# \author Norman Feske +# \date 2009-10-16 +# + +# +# Print help information by default +# +help:: + +VERBOSE ?= @ +ECHO = @echo +DOWNLOAD_DIR = download +CONTRIB_DIR = contrib +GNU_FIND = find +SHELL = bash + +# +# Create download and contrib directory so that '.mk' files +# do not need to care for them. +# +prepare: $(DOWNLOAD_DIR) $(CONTRIB_DIR) + +# +# Include information about available ports +# +# Each '.mk' file in the 'ports/' directory extends the following +# variables: +# +# PORTS - list names of the available ports, e.g., 'freetype-2.3.9' +# GEN_DIRS - list of automatically generated directories +# GEN_FILES - list of automatically generated files +# +# Furthermore, each '.mk' file extends the 'prepare' rule for +# downloading and unpacking the corresponding upstream sources. +# +PKG ?= $(patsubst ports/%.mk,%,$(wildcard ports/*.mk)) +include $(addprefix ports/,$(addsuffix .mk,$(PKG))) + +help:: + $(ECHO) + $(ECHO) "Download and unpack upstream source codes:" + @for i in $(PORTS); do echo " $$i"; done + $(ECHO) + $(ECHO) "Downloads will be placed into the '$(DOWNLOAD_DIR)/' directory." + $(ECHO) "Source codes will be unpacked in the '$(CONTRIB_DIR)/' directory." + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - download and unpack upstream source codes" + $(ECHO) "clean - remove upstream source codes" + $(ECHO) "cleanall - remove upstream source codes and downloads" + $(ECHO) + $(ECHO) "--- available arguments ---" + $(ECHO) "PKG= - prepare only the specified packages," + $(ECHO) " each package specified w/o version number" + +prepare: $(addprefix prepare-,$(PKG)) + +$(DOWNLOAD_DIR) $(CONTRIB_DIR): + $(VERBOSE)mkdir -p $@ + +clean: $(addprefix clean-,$(PKG)) + $(VERBOSE)if [ -d $(CONTRIB_DIR) ]; then \ + $(GNU_FIND) $(CONTRIB_DIR) -depth -type d -empty -delete; fi + +cleanall: clean + $(VERBOSE)rm -rf $(DOWNLOAD_DIR) + diff --git a/libports/README b/libports/README new file mode 100644 index 000000000..92ab1bdb9 --- /dev/null +++ b/libports/README @@ -0,0 +1,45 @@ +This directory contains ports of popular 3rd-party software to Genode. + + +Usage +----- + +At the root of the 'libports' repository, there is 'Makefile' automating the +task of downloading and preparing the library source codes. By just typing +'make', you get an overview of the available libraries and further +instructions. + +In the common case, you might just want to prepare all packages by issuing: +! make prepare + +Alternatively, you can select individual packages to prepare by specifying +their base names (without the version number) as command-line argument. For +example, the following command prepares both the C library and the Freetype +library: +! make prepare PKG="libc freetype" + +After having prepared the 'libports' repository, you are ready to include the +repository into the build process by appending it to the 'REPOSITORIES' +declaration of your '/etc/build.conf' file. + + +Under the hood +-------------- + +For each library, there is a file contained in the 'libports/ports/' +subdirectory. The file is named after the library and contains the +library-specific rules for downloading the source code and installing header +files. + + +How does 'libports' relate to the other repositories? +----------------------------------------------------- + +Most libraries hosted in the 'libports' repository expect a complete C library, +which is provided with the 'libc' package. Please do not forget to prepare the +libc package when using any of the other libports packages. The libc, in turn, +depends on the 'os' repository for its back end. Because the 'os' repository is +the home of the dynamic linker, libraries contained in 'libports' are safe to +assume the presence of the dynamic linker and, thus, should be built as shared +libraries. + diff --git a/libports/doc/libc.txt b/libports/doc/libc.txt new file mode 100644 index 000000000..fdb991f6e --- /dev/null +++ b/libports/doc/libc.txt @@ -0,0 +1,72 @@ +The C library of the Genode OS Framework is based on the code of FreeBSD. The +original code is available at the official FreeBSD website. + +:FreeBSD website: [http://www.freebsd.org] + +Currently, the libc supports the x86_32, x86_64 and ARM architectures. Support +for other architectures is planned as future addition. + +Usage +----- + +Before the libc is ready to use, the original FreeBSD source codes must be +downloaded and integrated with the Genode build system. The Makefile found +at the top level of the 'libports' repository automates this task. Please make +sure to have Subversion installed. Then issue the following command from +within the 'libports/' directory: + +! make prepare PKG=libc + +To use the libc in your application, add 'libc' to the 'LIBS' declaration in +your build-description file. This declaration will make the libc headers +available for the include path of your target and link the C library. When +building, make sure that the 'libports' repository is included in your build +configuration ('/etc/build.conf'). + +Limitations +----------- + +The current version of the C library is not thread-safe. For most string and +math functions, this is not a problem (as these functions do not modify global +state) but be careful with using more complex functions such as 'malloc' from +multiple threads. Also, 'errno' may become meaningless when calling libc +functions from multiple threads. + +We have left out the following files from the Genode port of the FreeBSD libc: +:gdtoa libary: 'strtodnrp.c' +:gen library: 'getosreldate.c' +:string libary: 'strcoll.c', 'strxfrm.c', 'wcscoll.c', 'wcsxfrm.c' +:math library: 's_exp2l.c' + +In the ARM version, the following additional files are excluded: +:libm: 'e_acosl.c', 'e_asinl.c', 'e_atan2l.c', 'e_hypotl.c', 's_atanl.c', + 's_cosl.c', 's_frexpl.c', 's_nextafterl.c', 's_nexttoward.c', + 's_rintl.c', s_scalbnl.c', 's_sinl.c', 's_tanl.c', 's_fmal.c' + +Atomic operation on ARM are not supported. Although these operations are +defined in 'machine/atomic.h', their original FreeBSD implementations are not +functional because we do not emulate the required FreeBSD environment (see: +'sysarch.h'). However, these functions are not a regular part of the libc +anyway and are not referenced from any other libc code. + +The current back end is quite simplistic and it may help you to revisit the +current state of the implementation in the 'src/lib/libc' directory. If +one of the functions in 'dummies.c' is called, you will see an message in your +debug output: +! called, not yet implemented! +However, some of the back-end function implemented in the other files have +dummy semantics but have to remain quiet because they are called from low-level +libc code. + +Genode-specific additions +------------------------- + +The back end of the libc is tailored to Genode via a flexible plugin concept. +This concept enables the combination of a variety of Genode-specific interfaces +with libc-based programs. For example, one program may want to use Genode's LOG +service as standard output whereas interactive programs might prefer the use of +Terminal sessions. Further available (entirely optional) back ends include a +BSD socket implementation based on the lwIP stack and a VFAT file-system back +end based on libffat. The interfaces used between plugins and the libc are +located at 'include/libc-plugin/'. + diff --git a/libports/include/EGL/eglplatform.h b/libports/include/EGL/eglplatform.h new file mode 100644 index 000000000..0388d624c --- /dev/null +++ b/libports/include/EGL/eglplatform.h @@ -0,0 +1,34 @@ +/* + * \brief Genode-specific EGL platform definitions + * \author Norman Feske + * \date 2010-07-01 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __eglplatform_h_ /* include-guard named as on the other platforms */ +#define __eglplatform_h_ + +#include + +#ifndef EGLAPI +#define EGLAPI KHRONOS_APICALL +#endif + +#ifndef EGLAPIENTRY +#define EGLAPIENTRY KHRONOS_APIENTRY +#endif +#define EGLAPIENTRYP EGLAPIENTRY* + +typedef int EGLNativeDisplayType; +typedef void *EGLNativeWindowType; +typedef void *EGLNativePixmapType; + +typedef khronos_int32_t EGLint; + +#endif /* __eglplatform_h_ */ diff --git a/libports/include/freetype-genode/ftconfig.h b/libports/include/freetype-genode/ftconfig.h new file mode 100644 index 000000000..3c0b8b164 --- /dev/null +++ b/libports/include/freetype-genode/ftconfig.h @@ -0,0 +1,500 @@ +/***************************************************************************/ +/* */ +/* ftconfig.h */ +/* */ +/* ANSI-specific configuration file (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007, 2008 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This header file contains a number of macro definitions that are used */ + /* by the rest of the engine. Most of the macros here are automatically */ + /* determined at compile time, and you should not need to change it to */ + /* port FreeType, except to compile the library with a non-ANSI */ + /* compiler. */ + /* */ + /* Note however that if some specific modifications are needed, we */ + /* advise you to place a modified copy in your build directory. */ + /* */ + /* The build directory is usually `freetype/builds/', and */ + /* contains system-specific files that are always included first when */ + /* building the library. */ + /* */ + /* This ANSI version should stay in `include/freetype/config'. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTCONFIG_H__ +#define __FTCONFIG_H__ + +#include +#include FT_CONFIG_OPTIONS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* PLATFORM-SPECIFIC CONFIGURATION MACROS */ + /* */ + /* These macros can be toggled to suit a specific system. The current */ + /* ones are defaults used to compile FreeType in an ANSI C environment */ + /* (16bit compilers are also supported). Copy this file to your own */ + /* `freetype/builds/' directory, and edit it to port the engine. */ + /* */ + /*************************************************************************/ + + + /* There are systems (like the Texas Instruments 'C54x) where a `char' */ + /* has 16 bits. ANSI C says that sizeof(char) is always 1. Since an */ + /* `int' has 16 bits also for this system, sizeof(int) gives 1 which */ + /* is probably unexpected. */ + /* */ + /* `CHAR_BIT' (defined in limits.h) gives the number of bits in a */ + /* `char' type. */ + +#ifndef FT_CHAR_BIT +#define FT_CHAR_BIT CHAR_BIT +#endif + + + /* The size of an `int' type. */ +#if FT_UINT_MAX == 0xFFFFUL +#define FT_SIZEOF_INT (16 / FT_CHAR_BIT) +#elif FT_UINT_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_INT (32 / FT_CHAR_BIT) +#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_INT (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `int' type!" +#endif + + /* The size of a `long' type. A five-byte `long' (as used e.g. on the */ + /* DM642) is recognized but avoided. */ +#if FT_ULONG_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_LONG (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `long' type!" +#endif + + + /* Preferred alignment of data */ +#define FT_ALIGNMENT 8 + + + /* FT_UNUSED is a macro used to indicate that a given parameter is not */ + /* used -- this is only used to get rid of unpleasant compiler warnings */ +#ifndef FT_UNUSED +#define FT_UNUSED( arg ) ( (arg) = (arg) ) +#endif + + + /*************************************************************************/ + /* */ + /* AUTOMATIC CONFIGURATION MACROS */ + /* */ + /* These macros are computed from the ones defined above. Don't touch */ + /* their definition, unless you know precisely what you are doing. No */ + /* porter should need to mess with them. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Mac support */ + /* */ + /* This is the only necessary change, so it is defined here instead */ + /* providing a new configuration file. */ + /* */ +#if ( defined( __APPLE__ ) && !defined( DARWIN_NO_CARBON ) ) || \ + ( defined( __MWERKS__ ) && defined( macintosh ) ) + /* no Carbon frameworks for 64bit 10.4.x */ +#include "AvailabilityMacros.h" +#if defined( __LP64__ ) && \ + ( MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 ) +#define DARWIN_NO_CARBON 1 +#else +#define FT_MACINTOSH 1 +#endif + +#elif defined( __SC__ ) || defined( __MRC__ ) + /* Classic MacOS compilers */ +#include "ConditionalMacros.h" +#if TARGET_OS_MAC +#define FT_MACINTOSH 1 +#endif + +#endif + + + /*************************************************************************/ + /* */ + /*
*/ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int16 */ + /* */ + /* */ + /* A typedef for a 16bit signed integer type. */ + /* */ + typedef signed short FT_Int16; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt16 */ + /* */ + /* */ + /* A typedef for a 16bit unsigned integer type. */ + /* */ + typedef unsigned short FT_UInt16; + + /* */ + + + /* this #if 0 ... #endif clause is for documentation purposes */ +#if 0 + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int32 */ + /* */ + /* */ + /* A typedef for a 32bit signed integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef signed XXX FT_Int32; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt32 */ + /* */ + /* A typedef for a 32bit unsigned integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef unsigned XXX FT_UInt32; + + /* */ + +#endif + +#if FT_SIZEOF_INT == (32 / FT_CHAR_BIT) + + typedef signed int FT_Int32; + typedef unsigned int FT_UInt32; + +#elif FT_SIZEOF_LONG == (32 / FT_CHAR_BIT) + + typedef signed long FT_Int32; + typedef unsigned long FT_UInt32; + +#else +#error "no 32bit type found -- please check your configuration files" +#endif + + + /* look up an integer type that is at least 32 bits */ +#if FT_SIZEOF_INT >= (32 / FT_CHAR_BIT) + + typedef int FT_Fast; + typedef unsigned int FT_UFast; + +#elif FT_SIZEOF_LONG >= (32 / FT_CHAR_BIT) + + typedef long FT_Fast; + typedef unsigned long FT_UFast; + +#endif + + + /* determine whether we have a 64-bit int type for platforms without */ + /* Autoconf */ +#if FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) + + /* FT_LONG64 must be defined if a 64-bit type is available */ +#define FT_LONG64 +#define FT_INT64 long + +#elif defined( _MSC_VER ) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 + +#elif defined( __BORLANDC__ ) /* Borland C++ */ + + /* XXXX: We should probably check the value of __BORLANDC__ in order */ + /* to test the compiler version. */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 + +#elif defined( __WATCOMC__ ) /* Watcom C++ */ + + /* Watcom doesn't provide 64-bit data types */ + +#elif defined( __MWERKS__ ) /* Metrowerks CodeWarrior */ + +#define FT_LONG64 +#define FT_INT64 long long int + +#elif defined( __GNUC__ ) + + /* GCC provides the `long long' type */ +#define FT_LONG64 +#define FT_INT64 long long int + +#endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */ + + + /*************************************************************************/ + /* */ + /* A 64-bit data type will create compilation problems if you compile */ + /* in strict ANSI mode. To avoid them, we disable its use if __STDC__ */ + /* is defined. You can however ignore this rule by defining the */ + /* FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */ + /* */ +#if defined( FT_LONG64 ) && !defined( FT_CONFIG_OPTION_FORCE_INT64 ) + +#ifdef __STDC__ + + /* undefine the 64-bit macros in strict ANSI compilation mode */ +#undef FT_LONG64 +#undef FT_INT64 + +#endif /* __STDC__ */ + +#endif /* FT_LONG64 && !FT_CONFIG_OPTION_FORCE_INT64 */ + + +#define FT_BEGIN_STMNT do { +#define FT_END_STMNT } while ( 0 ) +#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT + + +#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER + /* Provide assembler fragments for performance-critical functions. */ + /* These must be defined `static __inline__' with GCC. */ + +#ifdef __GNUC__ + +#if defined( __arm__ ) && !defined( __thumb__ ) +#define FT_MULFIX_ASSEMBLER FT_MulFix_arm + + /* documentation is in freetype.h */ + + static __inline__ FT_Int32 + FT_MulFix_arm( FT_Int32 a, + FT_Int32 b ) + { + register FT_Int32 t, t2; + + + asm __volatile__ ( + "smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */ + "mov %0, %2, asr #31\n\t" /* %0 = (hi >> 31) */ + "add %0, %0, #0x8000\n\t" /* %0 += 0x8000 */ + "adds %1, %1, %0\n\t" /* %1 += %0 */ + "adc %2, %2, #0\n\t" /* %2 += carry */ + "mov %0, %1, lsr #16\n\t" /* %0 = %1 >> 16 */ + "orr %0, %2, lsl #16\n\t" /* %0 |= %2 << 16 */ + : "=r"(a), "=&r"(t2), "=&r"(t) + : "r"(a), "r"(b) ); + return a; + } + +#endif /* __arm__ && !__thumb__ */ + +#if defined( i386 ) +#define FT_MULFIX_ASSEMBLER FT_MulFix_i386 + + /* documentation is in freetype.h */ + + static __inline__ FT_Int32 + FT_MulFix_i386( FT_Int32 a, + FT_Int32 b ) + { + register FT_Int32 result; + + + __asm__ __volatile__ ( + "imul %%edx\n" + "movl %%edx, %%ecx\n" + "sarl $31, %%ecx\n" + "addl $0x8000, %%ecx\n" + "addl %%ecx, %%eax\n" + "adcl $0, %%edx\n" + "shrl $16, %%eax\n" + "shll $16, %%edx\n" + "addl %%edx, %%eax\n" + : "=a"(result), "=d"(b) + : "a"(a), "d"(b) + : "%ecx", "cc" ); + return result; + } + +#endif /* i386 */ + +#endif /* __GNUC__ */ + +#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ + + +#ifdef FT_CONFIG_OPTION_INLINE_MULFIX +#ifdef FT_MULFIX_ASSEMBLER +#define FT_MULFIX_INLINED FT_MULFIX_ASSEMBLER +#endif +#endif + + +#ifdef FT_MAKE_OPTION_SINGLE_OBJECT + +#define FT_LOCAL( x ) static x +#define FT_LOCAL_DEF( x ) static x + +#else + +#ifdef __cplusplus +#define FT_LOCAL( x ) extern "C" x +#define FT_LOCAL_DEF( x ) extern "C" x +#else +#define FT_LOCAL( x ) extern x +#define FT_LOCAL_DEF( x ) x +#endif + +#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */ + + +#ifndef FT_BASE + +#ifdef __cplusplus +#define FT_BASE( x ) extern "C" x +#else +#define FT_BASE( x ) extern x +#endif + +#endif /* !FT_BASE */ + + +#ifndef FT_BASE_DEF + +#ifdef __cplusplus +#define FT_BASE_DEF( x ) x +#else +#define FT_BASE_DEF( x ) x +#endif + +#endif /* !FT_BASE_DEF */ + + +#ifndef FT_EXPORT + +#ifdef __cplusplus +#define FT_EXPORT( x ) extern "C" x +#else +#define FT_EXPORT( x ) extern x +#endif + +#endif /* !FT_EXPORT */ + + +#ifndef FT_EXPORT_DEF + +#ifdef __cplusplus +#define FT_EXPORT_DEF( x ) extern "C" x +#else +#define FT_EXPORT_DEF( x ) extern x +#endif + +#endif /* !FT_EXPORT_DEF */ + + +#ifndef FT_EXPORT_VAR + +#ifdef __cplusplus +#define FT_EXPORT_VAR( x ) extern "C" x +#else +#define FT_EXPORT_VAR( x ) extern x +#endif + +#endif /* !FT_EXPORT_VAR */ + + /* The following macros are needed to compile the library with a */ + /* C++ compiler and with 16bit compilers. */ + /* */ + + /* This is special. Within C++, you must specify `extern "C"' for */ + /* functions which are used via function pointers, and you also */ + /* must do that for structures which contain function pointers to */ + /* assure C linkage -- it's not possible to have (local) anonymous */ + /* functions which are accessed by (global) function pointers. */ + /* */ + /* */ + /* FT_CALLBACK_DEF is used to _define_ a callback function. */ + /* */ + /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */ + /* contains pointers to callback functions. */ + /* */ + /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */ + /* that contains pointers to callback functions. */ + /* */ + /* */ + /* Some 16bit compilers have to redefine these macros to insert */ + /* the infamous `_cdecl' or `__fastcall' declarations. */ + /* */ +#ifndef FT_CALLBACK_DEF +#ifdef __cplusplus +#define FT_CALLBACK_DEF( x ) extern "C" x +#else +#define FT_CALLBACK_DEF( x ) static x +#endif +#endif /* FT_CALLBACK_DEF */ + +#ifndef FT_CALLBACK_TABLE +#ifdef __cplusplus +#define FT_CALLBACK_TABLE extern "C" +#define FT_CALLBACK_TABLE_DEF extern "C" +#else +#define FT_CALLBACK_TABLE extern +#define FT_CALLBACK_TABLE_DEF /* nothing */ +#endif +#endif /* FT_CALLBACK_TABLE */ + + +FT_END_HEADER + + +#endif /* __FTCONFIG_H__ */ + + +/* END */ diff --git a/libports/include/freetype-genode/ftmodule.h b/libports/include/freetype-genode/ftmodule.h new file mode 100644 index 000000000..76d271a74 --- /dev/null +++ b/libports/include/freetype-genode/ftmodule.h @@ -0,0 +1,32 @@ +/* + * This file registers the FreeType modules compiled into the library. + * + * If you use GNU make, this file IS NOT USED! Instead, it is created in + * the objects directory (normally `/objs/') based on information + * from `/modules.cfg'. + * + * Please read `docs/INSTALL.ANY' and `docs/CUSTOMIZE' how to compile + * FreeType without GNU make. + * + */ + +FT_USE_MODULE( FT_Module_Class, autofit_module_class ) +FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, cff_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t1cid_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, pfr_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t42_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, winfnt_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, pcf_driver_class ) +FT_USE_MODULE( FT_Module_Class, psaux_module_class ) +FT_USE_MODULE( FT_Module_Class, psnames_module_class ) +FT_USE_MODULE( FT_Module_Class, pshinter_module_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class ) +FT_USE_MODULE( FT_Module_Class, sfnt_module_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class ) +FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class ) + +/* EOF */ diff --git a/libports/include/gcc/README b/libports/include/gcc/README new file mode 100644 index 000000000..da551a271 --- /dev/null +++ b/libports/include/gcc/README @@ -0,0 +1,2 @@ +This header is required for compiling the gmp library. +Normally, it comes with gcc. diff --git a/libports/include/gcc/longlong.h b/libports/include/gcc/longlong.h new file mode 100644 index 000000000..11e701399 --- /dev/null +++ b/libports/include/gcc/longlong.h @@ -0,0 +1,1525 @@ +/* longlong.h -- definitions for mixed size 32/64 bit arithmetic. + Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + In addition to the permissions in the GNU Lesser General Public + License, the Free Software Foundation gives you unlimited + permission to link the compiled version of this file into + combinations with other programs, and to distribute those + combinations without any restriction coming from the use of this + file. (The Lesser General Public License restrictions do apply in + other respects; for example, they cover modification of the file, + and distribution when not linked into a combine executable.) + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* You have to define the following before including this file: + + UWtype -- An unsigned type, default type for operations (typically a "word") + UHWtype -- An unsigned type, at least half the size of UWtype. + UDWtype -- An unsigned type, at least twice as large a UWtype + W_TYPE_SIZE -- size in bits of UWtype + + UQItype -- Unsigned 8 bit type. + SItype, USItype -- Signed and unsigned 32 bit types. + DItype, UDItype -- Signed and unsigned 64 bit types. + + On a 32 bit machine UWtype should typically be USItype; + on a 64 bit machine, UWtype should typically be UDItype. */ + +#define __BITS4 (W_TYPE_SIZE / 4) +#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) +#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1)) +#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2)) + +#ifndef W_TYPE_SIZE +#define W_TYPE_SIZE 32 +#define UWtype USItype +#define UHWtype USItype +#define UDWtype UDItype +#endif + +/* Used in glibc only. */ +#ifndef attribute_hidden +#define attribute_hidden +#endif + +extern const UQItype __clz_tab[256] attribute_hidden; + +/* Define auxiliary asm macros. + + 1) umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two + UWtype integers MULTIPLIER and MULTIPLICAND, and generates a two UWtype + word product in HIGH_PROD and LOW_PROD. + + 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a + UDWtype product. This is just a variant of umul_ppmm. + + 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, + denominator) divides a UDWtype, composed by the UWtype integers + HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient + in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less + than DENOMINATOR for correct operation. If, in addition, the most + significant bit of DENOMINATOR must be 1, then the pre-processor symbol + UDIV_NEEDS_NORMALIZATION is defined to 1. + + 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, + denominator). Like udiv_qrnnd but the numbers are signed. The quotient + is rounded towards 0. + + 5) count_leading_zeros(count, x) counts the number of zero-bits from the + msb to the first nonzero bit in the UWtype X. This is the number of + steps X needs to be shifted left to set the msb. Undefined for X == 0, + unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value. + + 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts + from the least significant end. + + 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, + high_addend_2, low_addend_2) adds two UWtype integers, composed by + HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 + respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow + (i.e. carry out) is not stored anywhere, and is lost. + + 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, + high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, + composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and + LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE + and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, + and is lost. + + If any of these macros are left undefined for a particular CPU, + C macros are used. */ + +/* The CPUs come in alphabetical order below. + + Please add support for more CPUs here, or improve the current support + for the CPUs below! + (E.g. WE32100, IBM360.) */ + +#if defined (__GNUC__) && !defined (NO_ASM) + +/* We sometimes need to clobber "cc" with gcc2, but that would not be + understood by gcc1. Use cpp to avoid major code duplication. */ +#if __GNUC__ < 2 +#define __CLOBBER_CC +#define __AND_CLOBBER_CC +#else /* __GNUC__ >= 2 */ +#define __CLOBBER_CC : "cc" +#define __AND_CLOBBER_CC , "cc" +#endif /* __GNUC__ < 2 */ + +#if defined (__alpha) && W_TYPE_SIZE == 64 +#define umul_ppmm(ph, pl, m0, m1) \ + do { \ + UDItype __m0 = (m0), __m1 = (m1); \ + (ph) = __builtin_alpha_umulh (__m0, __m1); \ + (pl) = __m0 * __m1; \ + } while (0) +#define UMUL_TIME 46 +#ifndef LONGLONG_STANDALONE +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { UDItype __r; \ + (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \ + (r) = __r; \ + } while (0) +extern UDItype __udiv_qrnnd (UDItype *, UDItype, UDItype, UDItype); +#define UDIV_TIME 220 +#endif /* LONGLONG_STANDALONE */ +#ifdef __alpha_cix__ +#define count_leading_zeros(COUNT,X) ((COUNT) = __builtin_clzl (X)) +#define count_trailing_zeros(COUNT,X) ((COUNT) = __builtin_ctzl (X)) +#define COUNT_LEADING_ZEROS_0 64 +#else +#define count_leading_zeros(COUNT,X) \ + do { \ + UDItype __xr = (X), __t, __a; \ + __t = __builtin_alpha_cmpbge (0, __xr); \ + __a = __clz_tab[__t ^ 0xff] - 1; \ + __t = __builtin_alpha_extbl (__xr, __a); \ + (COUNT) = 64 - (__clz_tab[__t] + __a*8); \ + } while (0) +#define count_trailing_zeros(COUNT,X) \ + do { \ + UDItype __xr = (X), __t, __a; \ + __t = __builtin_alpha_cmpbge (0, __xr); \ + __t = ~__t & -~__t; \ + __a = ((__t & 0xCC) != 0) * 2; \ + __a += ((__t & 0xF0) != 0) * 4; \ + __a += ((__t & 0xAA) != 0); \ + __t = __builtin_alpha_extbl (__xr, __a); \ + __a <<= 3; \ + __t &= -__t; \ + __a += ((__t & 0xCC) != 0) * 2; \ + __a += ((__t & 0xF0) != 0) * 4; \ + __a += ((__t & 0xAA) != 0); \ + (COUNT) = __a; \ + } while (0) +#endif /* __alpha_cix__ */ +#endif /* __alpha */ + +#if defined (__arc__) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("add.f %1, %4, %5\n\tadc %0, %2, %3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%r" ((USItype) (ah)), \ + "rIJ" ((USItype) (bh)), \ + "%r" ((USItype) (al)), \ + "rIJ" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("sub.f %1, %4, %5\n\tsbc %0, %2, %3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "r" ((USItype) (ah)), \ + "rIJ" ((USItype) (bh)), \ + "r" ((USItype) (al)), \ + "rIJ" ((USItype) (bl))) +/* Call libgcc routine. */ +#define umul_ppmm(w1, w0, u, v) \ +do { \ + DWunion __w; \ + __w.ll = __umulsidi3 (u, v); \ + w1 = __w.s.high; \ + w0 = __w.s.low; \ +} while (0) +#define __umulsidi3 __umulsidi3 +UDItype __umulsidi3 (USItype, USItype); +#endif + +#if defined (__arm__) && !defined (__thumb__) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("adds %1, %4, %5\n\tadc %0, %2, %3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%r" ((USItype) (ah)), \ + "rI" ((USItype) (bh)), \ + "%r" ((USItype) (al)), \ + "rI" ((USItype) (bl)) __CLOBBER_CC) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subs %1, %4, %5\n\tsbc %0, %2, %3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "r" ((USItype) (ah)), \ + "rI" ((USItype) (bh)), \ + "r" ((USItype) (al)), \ + "rI" ((USItype) (bl)) __CLOBBER_CC) +#define umul_ppmm(xh, xl, a, b) \ +{register USItype __t0, __t1, __t2; \ + __asm__ ("%@ Inlined umul_ppmm\n" \ + " mov %2, %5, lsr #16\n" \ + " mov %0, %6, lsr #16\n" \ + " bic %3, %5, %2, lsl #16\n" \ + " bic %4, %6, %0, lsl #16\n" \ + " mul %1, %3, %4\n" \ + " mul %4, %2, %4\n" \ + " mul %3, %0, %3\n" \ + " mul %0, %2, %0\n" \ + " adds %3, %4, %3\n" \ + " addcs %0, %0, #65536\n" \ + " adds %1, %1, %3, lsl #16\n" \ + " adc %0, %0, %3, lsr #16" \ + : "=&r" ((USItype) (xh)), \ + "=r" ((USItype) (xl)), \ + "=&r" (__t0), "=&r" (__t1), "=r" (__t2) \ + : "r" ((USItype) (a)), \ + "r" ((USItype) (b)) __CLOBBER_CC );} +#define UMUL_TIME 20 +#define UDIV_TIME 100 +#endif /* __arm__ */ + +#if defined(__arm__) +/* Let gcc decide how best to implement count_leading_zeros. */ +#define count_leading_zeros(COUNT,X) ((COUNT) = __builtin_clz (X)) +#define COUNT_LEADING_ZEROS_0 32 +#endif + +#if defined (__CRIS__) && __CRIS_arch_version >= 3 +#define count_leading_zeros(COUNT, X) ((COUNT) = __builtin_clz (X)) +#if __CRIS_arch_version >= 8 +#define count_trailing_zeros(COUNT, X) ((COUNT) = __builtin_ctz (X)) +#endif +#endif /* __CRIS__ */ + +#if defined (__hppa) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("add %4,%5,%1\n\taddc %2,%3,%0" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%rM" ((USItype) (ah)), \ + "rM" ((USItype) (bh)), \ + "%rM" ((USItype) (al)), \ + "rM" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("sub %4,%5,%1\n\tsubb %2,%3,%0" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "rM" ((USItype) (ah)), \ + "rM" ((USItype) (bh)), \ + "rM" ((USItype) (al)), \ + "rM" ((USItype) (bl))) +#if defined (_PA_RISC1_1) +#define umul_ppmm(w1, w0, u, v) \ + do { \ + union \ + { \ + UDItype __f; \ + struct {USItype __w1, __w0;} __w1w0; \ + } __t; \ + __asm__ ("xmpyu %1,%2,%0" \ + : "=x" (__t.__f) \ + : "x" ((USItype) (u)), \ + "x" ((USItype) (v))); \ + (w1) = __t.__w1w0.__w1; \ + (w0) = __t.__w1w0.__w0; \ + } while (0) +#define UMUL_TIME 8 +#else +#define UMUL_TIME 30 +#endif +#define UDIV_TIME 40 +#define count_leading_zeros(count, x) \ + do { \ + USItype __tmp; \ + __asm__ ( \ + "ldi 1,%0\n" \ +" extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \ +" extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n"\ +" ldo 16(%0),%0 ; Yes. Perform add.\n" \ +" extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \ +" extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n"\ +" ldo 8(%0),%0 ; Yes. Perform add.\n" \ +" extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \ +" extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n"\ +" ldo 4(%0),%0 ; Yes. Perform add.\n" \ +" extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \ +" extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n"\ +" ldo 2(%0),%0 ; Yes. Perform add.\n" \ +" extru %1,30,1,%1 ; Extract bit 1.\n" \ +" sub %0,%1,%0 ; Subtract it.\n" \ + : "=r" (count), "=r" (__tmp) : "1" (x)); \ + } while (0) +#endif + +#if (defined (__i370__) || defined (__s390__) || defined (__mvs__)) && W_TYPE_SIZE == 32 +#define smul_ppmm(xh, xl, m0, m1) \ + do { \ + union {DItype __ll; \ + struct {USItype __h, __l;} __i; \ + } __x; \ + __asm__ ("lr %N0,%1\n\tmr %0,%2" \ + : "=&r" (__x.__ll) \ + : "r" (m0), "r" (m1)); \ + (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ + } while (0) +#define sdiv_qrnnd(q, r, n1, n0, d) \ + do { \ + union {DItype __ll; \ + struct {USItype __h, __l;} __i; \ + } __x; \ + __x.__i.__h = n1; __x.__i.__l = n0; \ + __asm__ ("dr %0,%2" \ + : "=r" (__x.__ll) \ + : "0" (__x.__ll), "r" (d)); \ + (q) = __x.__i.__l; (r) = __x.__i.__h; \ + } while (0) +#endif + +#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("add{l} {%5,%1|%1,%5}\n\tadc{l} {%3,%0|%0,%3}" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%0" ((USItype) (ah)), \ + "g" ((USItype) (bh)), \ + "%1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("sub{l} {%5,%1|%1,%5}\n\tsbb{l} {%3,%0|%0,%3}" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "g" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("mul{l} %3" \ + : "=a" ((USItype) (w0)), \ + "=d" ((USItype) (w1)) \ + : "%0" ((USItype) (u)), \ + "rm" ((USItype) (v))) +#define udiv_qrnnd(q, r, n1, n0, dv) \ + __asm__ ("div{l} %4" \ + : "=a" ((USItype) (q)), \ + "=d" ((USItype) (r)) \ + : "0" ((USItype) (n0)), \ + "1" ((USItype) (n1)), \ + "rm" ((USItype) (dv))) +#define count_leading_zeros(count, x) ((count) = __builtin_clz (x)) +#define count_trailing_zeros(count, x) ((count) = __builtin_ctz (x)) +#define UMUL_TIME 40 +#define UDIV_TIME 40 +#endif /* 80x86 */ + +#if (defined (__x86_64__) || defined (__i386__)) && W_TYPE_SIZE == 64 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("add{q} {%5,%1|%1,%5}\n\tadc{q} {%3,%0|%0,%3}" \ + : "=r" ((UDItype) (sh)), \ + "=&r" ((UDItype) (sl)) \ + : "%0" ((UDItype) (ah)), \ + "rme" ((UDItype) (bh)), \ + "%1" ((UDItype) (al)), \ + "rme" ((UDItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("sub{q} {%5,%1|%1,%5}\n\tsbb{q} {%3,%0|%0,%3}" \ + : "=r" ((UDItype) (sh)), \ + "=&r" ((UDItype) (sl)) \ + : "0" ((UDItype) (ah)), \ + "rme" ((UDItype) (bh)), \ + "1" ((UDItype) (al)), \ + "rme" ((UDItype) (bl))) +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("mul{q} %3" \ + : "=a" ((UDItype) (w0)), \ + "=d" ((UDItype) (w1)) \ + : "%0" ((UDItype) (u)), \ + "rm" ((UDItype) (v))) +#define udiv_qrnnd(q, r, n1, n0, dv) \ + __asm__ ("div{q} %4" \ + : "=a" ((UDItype) (q)), \ + "=d" ((UDItype) (r)) \ + : "0" ((UDItype) (n0)), \ + "1" ((UDItype) (n1)), \ + "rm" ((UDItype) (dv))) +#define count_leading_zeros(count, x) ((count) = __builtin_clzl (x)) +#define count_trailing_zeros(count, x) ((count) = __builtin_ctzl (x)) +#define UMUL_TIME 40 +#define UDIV_TIME 40 +#endif /* x86_64 */ + +#if defined (__i960__) && W_TYPE_SIZE == 32 +#define umul_ppmm(w1, w0, u, v) \ + ({union {UDItype __ll; \ + struct {USItype __l, __h;} __i; \ + } __xx; \ + __asm__ ("emul %2,%1,%0" \ + : "=d" (__xx.__ll) \ + : "%dI" ((USItype) (u)), \ + "dI" ((USItype) (v))); \ + (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;}) +#define __umulsidi3(u, v) \ + ({UDItype __w; \ + __asm__ ("emul %2,%1,%0" \ + : "=d" (__w) \ + : "%dI" ((USItype) (u)), \ + "dI" ((USItype) (v))); \ + __w; }) +#endif /* __i960__ */ + +#if defined (__ia64) && W_TYPE_SIZE == 64 +/* This form encourages gcc (pre-release 3.4 at least) to emit predicated + "sub r=r,r" and "sub r=r,r,1", giving a 2 cycle latency. The generic + code using "al>= _c; \ + if (_x >= 1 << 4) \ + _x >>= 4, _c += 4; \ + if (_x >= 1 << 2) \ + _x >>= 2, _c += 2; \ + _c += _x >> 1; \ + (count) = W_TYPE_SIZE - 1 - _c; \ + } while (0) +/* similar to what gcc does for __builtin_ffs, but 0 based rather than 1 + based, and we don't need a special case for x==0 here */ +#define count_trailing_zeros(count, x) \ + do { \ + UWtype __ctz_x = (x); \ + __asm__ ("popcnt %0 = %1" \ + : "=r" (count) \ + : "r" ((__ctz_x-1) & ~__ctz_x)); \ + } while (0) +#define UMUL_TIME 14 +#endif + +#if defined (__M32R__) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + /* The cmp clears the condition bit. */ \ + __asm__ ("cmp %0,%0\n\taddx %1,%5\n\taddx %0,%3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "r" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "r" ((USItype) (bl)) \ + : "cbit") +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + /* The cmp clears the condition bit. */ \ + __asm__ ("cmp %0,%0\n\tsubx %1,%5\n\tsubx %0,%3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "r" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "r" ((USItype) (bl)) \ + : "cbit") +#endif /* __M32R__ */ + +#if defined (__mc68000__) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \ + : "=d" ((USItype) (sh)), \ + "=&d" ((USItype) (sl)) \ + : "%0" ((USItype) (ah)), \ + "d" ((USItype) (bh)), \ + "%1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0" \ + : "=d" ((USItype) (sh)), \ + "=&d" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "d" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "g" ((USItype) (bl))) + +/* The '020, '030, '040, '060 and CPU32 have 32x32->64 and 64/32->32q-32r. */ +#if (defined (__mc68020__) && !defined (__mc68060__)) +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("mulu%.l %3,%1:%0" \ + : "=d" ((USItype) (w0)), \ + "=d" ((USItype) (w1)) \ + : "%0" ((USItype) (u)), \ + "dmi" ((USItype) (v))) +#define UMUL_TIME 45 +#define udiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("divu%.l %4,%1:%0" \ + : "=d" ((USItype) (q)), \ + "=d" ((USItype) (r)) \ + : "0" ((USItype) (n0)), \ + "1" ((USItype) (n1)), \ + "dmi" ((USItype) (d))) +#define UDIV_TIME 90 +#define sdiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("divs%.l %4,%1:%0" \ + : "=d" ((USItype) (q)), \ + "=d" ((USItype) (r)) \ + : "0" ((USItype) (n0)), \ + "1" ((USItype) (n1)), \ + "dmi" ((USItype) (d))) + +#elif defined (__mcoldfire__) /* not mc68020 */ + +#define umul_ppmm(xh, xl, a, b) \ + __asm__ ("| Inlined umul_ppmm\n" \ + " move%.l %2,%/d0\n" \ + " move%.l %3,%/d1\n" \ + " move%.l %/d0,%/d2\n" \ + " swap %/d0\n" \ + " move%.l %/d1,%/d3\n" \ + " swap %/d1\n" \ + " move%.w %/d2,%/d4\n" \ + " mulu %/d3,%/d4\n" \ + " mulu %/d1,%/d2\n" \ + " mulu %/d0,%/d3\n" \ + " mulu %/d0,%/d1\n" \ + " move%.l %/d4,%/d0\n" \ + " clr%.w %/d0\n" \ + " swap %/d0\n" \ + " add%.l %/d0,%/d2\n" \ + " add%.l %/d3,%/d2\n" \ + " jcc 1f\n" \ + " add%.l %#65536,%/d1\n" \ + "1: swap %/d2\n" \ + " moveq %#0,%/d0\n" \ + " move%.w %/d2,%/d0\n" \ + " move%.w %/d4,%/d2\n" \ + " move%.l %/d2,%1\n" \ + " add%.l %/d1,%/d0\n" \ + " move%.l %/d0,%0" \ + : "=g" ((USItype) (xh)), \ + "=g" ((USItype) (xl)) \ + : "g" ((USItype) (a)), \ + "g" ((USItype) (b)) \ + : "d0", "d1", "d2", "d3", "d4") +#define UMUL_TIME 100 +#define UDIV_TIME 400 +#else /* not ColdFire */ +/* %/ inserts REGISTER_PREFIX, %# inserts IMMEDIATE_PREFIX. */ +#define umul_ppmm(xh, xl, a, b) \ + __asm__ ("| Inlined umul_ppmm\n" \ + " move%.l %2,%/d0\n" \ + " move%.l %3,%/d1\n" \ + " move%.l %/d0,%/d2\n" \ + " swap %/d0\n" \ + " move%.l %/d1,%/d3\n" \ + " swap %/d1\n" \ + " move%.w %/d2,%/d4\n" \ + " mulu %/d3,%/d4\n" \ + " mulu %/d1,%/d2\n" \ + " mulu %/d0,%/d3\n" \ + " mulu %/d0,%/d1\n" \ + " move%.l %/d4,%/d0\n" \ + " eor%.w %/d0,%/d0\n" \ + " swap %/d0\n" \ + " add%.l %/d0,%/d2\n" \ + " add%.l %/d3,%/d2\n" \ + " jcc 1f\n" \ + " add%.l %#65536,%/d1\n" \ + "1: swap %/d2\n" \ + " moveq %#0,%/d0\n" \ + " move%.w %/d2,%/d0\n" \ + " move%.w %/d4,%/d2\n" \ + " move%.l %/d2,%1\n" \ + " add%.l %/d1,%/d0\n" \ + " move%.l %/d0,%0" \ + : "=g" ((USItype) (xh)), \ + "=g" ((USItype) (xl)) \ + : "g" ((USItype) (a)), \ + "g" ((USItype) (b)) \ + : "d0", "d1", "d2", "d3", "d4") +#define UMUL_TIME 100 +#define UDIV_TIME 400 + +#endif /* not mc68020 */ + +/* The '020, '030, '040 and '060 have bitfield insns. + cpu32 disguises as a 68020, but lacks them. */ +#if defined (__mc68020__) && !defined (__mcpu32__) +#define count_leading_zeros(count, x) \ + __asm__ ("bfffo %1{%b2:%b2},%0" \ + : "=d" ((USItype) (count)) \ + : "od" ((USItype) (x)), "n" (0)) +/* Some ColdFire architectures have a ff1 instruction supported via + __builtin_clz. */ +#elif defined (__mcfisaaplus__) || defined (__mcfisac__) +#define count_leading_zeros(count,x) ((count) = __builtin_clz (x)) +#define COUNT_LEADING_ZEROS_0 32 +#endif +#endif /* mc68000 */ + +#if defined (__m88000__) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%rJ" ((USItype) (ah)), \ + "rJ" ((USItype) (bh)), \ + "%rJ" ((USItype) (al)), \ + "rJ" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "rJ" ((USItype) (ah)), \ + "rJ" ((USItype) (bh)), \ + "rJ" ((USItype) (al)), \ + "rJ" ((USItype) (bl))) +#define count_leading_zeros(count, x) \ + do { \ + USItype __cbtmp; \ + __asm__ ("ff1 %0,%1" \ + : "=r" (__cbtmp) \ + : "r" ((USItype) (x))); \ + (count) = __cbtmp ^ 31; \ + } while (0) +#define COUNT_LEADING_ZEROS_0 63 /* sic */ +#if defined (__mc88110__) +#define umul_ppmm(wh, wl, u, v) \ + do { \ + union {UDItype __ll; \ + struct {USItype __h, __l;} __i; \ + } __xx; \ + __asm__ ("mulu.d %0,%1,%2" \ + : "=r" (__xx.__ll) \ + : "r" ((USItype) (u)), \ + "r" ((USItype) (v))); \ + (wh) = __xx.__i.__h; \ + (wl) = __xx.__i.__l; \ + } while (0) +#define udiv_qrnnd(q, r, n1, n0, d) \ + ({union {UDItype __ll; \ + struct {USItype __h, __l;} __i; \ + } __xx; \ + USItype __q; \ + __xx.__i.__h = (n1); __xx.__i.__l = (n0); \ + __asm__ ("divu.d %0,%1,%2" \ + : "=r" (__q) \ + : "r" (__xx.__ll), \ + "r" ((USItype) (d))); \ + (r) = (n0) - __q * (d); (q) = __q; }) +#define UMUL_TIME 5 +#define UDIV_TIME 25 +#else +#define UMUL_TIME 17 +#define UDIV_TIME 150 +#endif /* __mc88110__ */ +#endif /* __m88000__ */ + +#if defined (__mips__) && W_TYPE_SIZE == 32 +#define umul_ppmm(w1, w0, u, v) \ + do { \ + UDItype __x = (UDItype) (USItype) (u) * (USItype) (v); \ + (w1) = (USItype) (__x >> 32); \ + (w0) = (USItype) (__x); \ + } while (0) +#define UMUL_TIME 10 +#define UDIV_TIME 100 + +#if (__mips == 32 || __mips == 64) && ! __mips16 +#define count_leading_zeros(COUNT,X) ((COUNT) = __builtin_clz (X)) +#define COUNT_LEADING_ZEROS_0 32 +#endif +#endif /* __mips__ */ + +#if defined (__ns32000__) && W_TYPE_SIZE == 32 +#define umul_ppmm(w1, w0, u, v) \ + ({union {UDItype __ll; \ + struct {USItype __l, __h;} __i; \ + } __xx; \ + __asm__ ("meid %2,%0" \ + : "=g" (__xx.__ll) \ + : "%0" ((USItype) (u)), \ + "g" ((USItype) (v))); \ + (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;}) +#define __umulsidi3(u, v) \ + ({UDItype __w; \ + __asm__ ("meid %2,%0" \ + : "=g" (__w) \ + : "%0" ((USItype) (u)), \ + "g" ((USItype) (v))); \ + __w; }) +#define udiv_qrnnd(q, r, n1, n0, d) \ + ({union {UDItype __ll; \ + struct {USItype __l, __h;} __i; \ + } __xx; \ + __xx.__i.__h = (n1); __xx.__i.__l = (n0); \ + __asm__ ("deid %2,%0" \ + : "=g" (__xx.__ll) \ + : "0" (__xx.__ll), \ + "g" ((USItype) (d))); \ + (r) = __xx.__i.__l; (q) = __xx.__i.__h; }) +#define count_trailing_zeros(count,x) \ + do { \ + __asm__ ("ffsd %2,%0" \ + : "=r" ((USItype) (count)) \ + : "0" ((USItype) 0), \ + "r" ((USItype) (x))); \ + } while (0) +#endif /* __ns32000__ */ + +/* FIXME: We should test _IBMR2 here when we add assembly support for the + system vendor compilers. + FIXME: What's needed for gcc PowerPC VxWorks? __vxworks__ is not good + enough, since that hits ARM and m68k too. */ +#if (defined (_ARCH_PPC) /* AIX */ \ + || defined (_ARCH_PWR) /* AIX */ \ + || defined (_ARCH_COM) /* AIX */ \ + || defined (__powerpc__) /* gcc */ \ + || defined (__POWERPC__) /* BEOS */ \ + || defined (__ppc__) /* Darwin */ \ + || (defined (PPC) && ! defined (CPU_FAMILY)) /* gcc 2.7.x GNU&SysV */ \ + || (defined (PPC) && defined (CPU_FAMILY) /* VxWorks */ \ + && CPU_FAMILY == PPC) \ + ) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + if (__builtin_constant_p (bh) && (bh) == 0) \ + __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ + else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ + __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ + else \ + __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \ + : "=r" (sh), "=&r" (sl) \ + : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ + } while (0) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + if (__builtin_constant_p (ah) && (ah) == 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ + else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ + else if (__builtin_constant_p (bh) && (bh) == 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ + else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ + else \ + __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \ + : "=r" (sh), "=&r" (sl) \ + : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ + } while (0) +#define count_leading_zeros(count, x) \ + __asm__ ("{cntlz|cntlzw} %0,%1" : "=r" (count) : "r" (x)) +#define COUNT_LEADING_ZEROS_0 32 +#if defined (_ARCH_PPC) || defined (__powerpc__) || defined (__POWERPC__) \ + || defined (__ppc__) \ + || (defined (PPC) && ! defined (CPU_FAMILY)) /* gcc 2.7.x GNU&SysV */ \ + || (defined (PPC) && defined (CPU_FAMILY) /* VxWorks */ \ + && CPU_FAMILY == PPC) +#define umul_ppmm(ph, pl, m0, m1) \ + do { \ + USItype __m0 = (m0), __m1 = (m1); \ + __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ + (pl) = __m0 * __m1; \ + } while (0) +#define UMUL_TIME 15 +#define smul_ppmm(ph, pl, m0, m1) \ + do { \ + SItype __m0 = (m0), __m1 = (m1); \ + __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ + (pl) = __m0 * __m1; \ + } while (0) +#define SMUL_TIME 14 +#define UDIV_TIME 120 +#elif defined (_ARCH_PWR) +#define UMUL_TIME 8 +#define smul_ppmm(xh, xl, m0, m1) \ + __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1)) +#define SMUL_TIME 4 +#define sdiv_qrnnd(q, r, nh, nl, d) \ + __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d)) +#define UDIV_TIME 100 +#endif +#endif /* 32-bit POWER architecture variants. */ + +/* We should test _IBMR2 here when we add assembly support for the system + vendor compilers. */ +#if (defined (_ARCH_PPC64) || defined (__powerpc64__)) && W_TYPE_SIZE == 64 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + if (__builtin_constant_p (bh) && (bh) == 0) \ + __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ + else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ + __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ + else \ + __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \ + : "=r" (sh), "=&r" (sl) \ + : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ + } while (0) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + if (__builtin_constant_p (ah) && (ah) == 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ + else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ + else if (__builtin_constant_p (bh) && (bh) == 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ + else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \ + : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ + else \ + __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \ + : "=r" (sh), "=&r" (sl) \ + : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ + } while (0) +#define count_leading_zeros(count, x) \ + __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x)) +#define COUNT_LEADING_ZEROS_0 64 +#define umul_ppmm(ph, pl, m0, m1) \ + do { \ + UDItype __m0 = (m0), __m1 = (m1); \ + __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ + (pl) = __m0 * __m1; \ + } while (0) +#define UMUL_TIME 15 +#define smul_ppmm(ph, pl, m0, m1) \ + do { \ + DItype __m0 = (m0), __m1 = (m1); \ + __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ + (pl) = __m0 * __m1; \ + } while (0) +#define SMUL_TIME 14 /* ??? */ +#define UDIV_TIME 120 /* ??? */ +#endif /* 64-bit PowerPC. */ + +#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("a %1,%5\n\tae %0,%3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%0" ((USItype) (ah)), \ + "r" ((USItype) (bh)), \ + "%1" ((USItype) (al)), \ + "r" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("s %1,%5\n\tse %0,%3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "r" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "r" ((USItype) (bl))) +#define umul_ppmm(ph, pl, m0, m1) \ + do { \ + USItype __m0 = (m0), __m1 = (m1); \ + __asm__ ( \ + "s r2,r2\n" \ +" mts r10,%2\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" m r2,%3\n" \ +" cas %0,r2,r0\n" \ +" mfs r10,%1" \ + : "=r" ((USItype) (ph)), \ + "=r" ((USItype) (pl)) \ + : "%r" (__m0), \ + "r" (__m1) \ + : "r2"); \ + (ph) += ((((SItype) __m0 >> 31) & __m1) \ + + (((SItype) __m1 >> 31) & __m0)); \ + } while (0) +#define UMUL_TIME 20 +#define UDIV_TIME 200 +#define count_leading_zeros(count, x) \ + do { \ + if ((x) >= 0x10000) \ + __asm__ ("clz %0,%1" \ + : "=r" ((USItype) (count)) \ + : "r" ((USItype) (x) >> 16)); \ + else \ + { \ + __asm__ ("clz %0,%1" \ + : "=r" ((USItype) (count)) \ + : "r" ((USItype) (x))); \ + (count) += 16; \ + } \ + } while (0) +#endif + +#if defined(__sh__) && !__SHMEDIA__ && W_TYPE_SIZE == 32 +#ifndef __sh1__ +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ( \ + "dmulu.l %2,%3\n\tsts%M1 macl,%1\n\tsts%M0 mach,%0" \ + : "=r<" ((USItype)(w1)), \ + "=r<" ((USItype)(w0)) \ + : "r" ((USItype)(u)), \ + "r" ((USItype)(v)) \ + : "macl", "mach") +#define UMUL_TIME 5 +#endif + +/* This is the same algorithm as __udiv_qrnnd_c. */ +#define UDIV_NEEDS_NORMALIZATION 1 + +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + extern UWtype __udiv_qrnnd_16 (UWtype, UWtype) \ + __attribute__ ((visibility ("hidden"))); \ + /* r0: rn r1: qn */ /* r0: n1 r4: n0 r5: d r6: d1 */ /* r2: __m */ \ + __asm__ ( \ + "mov%M4 %4,r5\n" \ +" swap.w %3,r4\n" \ +" swap.w r5,r6\n" \ +" jsr @%5\n" \ +" shll16 r6\n" \ +" swap.w r4,r4\n" \ +" jsr @%5\n" \ +" swap.w r1,%0\n" \ +" or r1,%0" \ + : "=r" (q), "=&z" (r) \ + : "1" (n1), "r" (n0), "rm" (d), "r" (&__udiv_qrnnd_16) \ + : "r1", "r2", "r4", "r5", "r6", "pr", "t"); \ + } while (0) + +#define UDIV_TIME 80 + +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("clrt;subc %5,%1; subc %4,%0" \ + : "=r" (sh), "=r" (sl) \ + : "0" (ah), "1" (al), "r" (bh), "r" (bl) : "t") + +#endif /* __sh__ */ + +#if defined (__SH5__) && __SHMEDIA__ && W_TYPE_SIZE == 32 +#define __umulsidi3(u,v) ((UDItype)(USItype)u*(USItype)v) +#define count_leading_zeros(count, x) \ + do \ + { \ + UDItype x_ = (USItype)(x); \ + SItype c_; \ + \ + __asm__ ("nsb %1, %0" : "=r" (c_) : "r" (x_)); \ + (count) = c_ - 31; \ + } \ + while (0) +#define COUNT_LEADING_ZEROS_0 32 +#endif + +#if defined (__sparc__) && !defined (__arch64__) && !defined (__sparcv9) \ + && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%rJ" ((USItype) (ah)), \ + "rI" ((USItype) (bh)), \ + "%rJ" ((USItype) (al)), \ + "rI" ((USItype) (bl)) \ + __CLOBBER_CC) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "rJ" ((USItype) (ah)), \ + "rI" ((USItype) (bh)), \ + "rJ" ((USItype) (al)), \ + "rI" ((USItype) (bl)) \ + __CLOBBER_CC) +#if defined (__sparc_v8__) +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("umul %2,%3,%1;rd %%y,%0" \ + : "=r" ((USItype) (w1)), \ + "=r" ((USItype) (w0)) \ + : "r" ((USItype) (u)), \ + "r" ((USItype) (v))) +#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \ + __asm__ ("mov %2,%%y;nop;nop;nop;udiv %3,%4,%0;umul %0,%4,%1;sub %3,%1,%1"\ + : "=&r" ((USItype) (__q)), \ + "=&r" ((USItype) (__r)) \ + : "r" ((USItype) (__n1)), \ + "r" ((USItype) (__n0)), \ + "r" ((USItype) (__d))) +#else +#if defined (__sparclite__) +/* This has hardware multiply but not divide. It also has two additional + instructions scan (ffs from high bit) and divscc. */ +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("umul %2,%3,%1;rd %%y,%0" \ + : "=r" ((USItype) (w1)), \ + "=r" ((USItype) (w0)) \ + : "r" ((USItype) (u)), \ + "r" ((USItype) (v))) +#define udiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("! Inlined udiv_qrnnd\n" \ +" wr %%g0,%2,%%y ! Not a delayed write for sparclite\n" \ +" tst %%g0\n" \ +" divscc %3,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%%g1\n" \ +" divscc %%g1,%4,%0\n" \ +" rd %%y,%1\n" \ +" bl,a 1f\n" \ +" add %1,%4,%1\n" \ +"1: ! End of inline udiv_qrnnd" \ + : "=r" ((USItype) (q)), \ + "=r" ((USItype) (r)) \ + : "r" ((USItype) (n1)), \ + "r" ((USItype) (n0)), \ + "rI" ((USItype) (d)) \ + : "g1" __AND_CLOBBER_CC) +#define UDIV_TIME 37 +#define count_leading_zeros(count, x) \ + do { \ + __asm__ ("scan %1,1,%0" \ + : "=r" ((USItype) (count)) \ + : "r" ((USItype) (x))); \ + } while (0) +/* Early sparclites return 63 for an argument of 0, but they warn that future + implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0 + undefined. */ +#else +/* SPARC without integer multiplication and divide instructions. + (i.e. at least Sun4/20,40,60,65,75,110,260,280,330,360,380,470,490) */ +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("! Inlined umul_ppmm\n" \ +" wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr\n"\ +" sra %3,31,%%o5 ! Don't move this insn\n" \ +" and %2,%%o5,%%o5 ! Don't move this insn\n" \ +" andcc %%g0,0,%%g1 ! Don't move this insn\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,%3,%%g1\n" \ +" mulscc %%g1,0,%%g1\n" \ +" add %%g1,%%o5,%0\n" \ +" rd %%y,%1" \ + : "=r" ((USItype) (w1)), \ + "=r" ((USItype) (w0)) \ + : "%rI" ((USItype) (u)), \ + "r" ((USItype) (v)) \ + : "g1", "o5" __AND_CLOBBER_CC) +#define UMUL_TIME 39 /* 39 instructions */ +/* It's quite necessary to add this much assembler for the sparc. + The default udiv_qrnnd (in C) is more than 10 times slower! */ +#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \ + __asm__ ("! Inlined udiv_qrnnd\n" \ +" mov 32,%%g1\n" \ +" subcc %1,%2,%%g0\n" \ +"1: bcs 5f\n" \ +" addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n" \ +" sub %1,%2,%1 ! this kills msb of n\n" \ +" addx %1,%1,%1 ! so this can't give carry\n" \ +" subcc %%g1,1,%%g1\n" \ +"2: bne 1b\n" \ +" subcc %1,%2,%%g0\n" \ +" bcs 3f\n" \ +" addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n" \ +" b 3f\n" \ +" sub %1,%2,%1 ! this kills msb of n\n" \ +"4: sub %1,%2,%1\n" \ +"5: addxcc %1,%1,%1\n" \ +" bcc 2b\n" \ +" subcc %%g1,1,%%g1\n" \ +"! Got carry from n. Subtract next step to cancel this carry.\n" \ +" bne 4b\n" \ +" addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb\n" \ +" sub %1,%2,%1\n" \ +"3: xnor %0,0,%0\n" \ +" ! End of inline udiv_qrnnd" \ + : "=&r" ((USItype) (__q)), \ + "=&r" ((USItype) (__r)) \ + : "r" ((USItype) (__d)), \ + "1" ((USItype) (__n1)), \ + "0" ((USItype) (__n0)) : "g1" __AND_CLOBBER_CC) +#define UDIV_TIME (3+7*32) /* 7 instructions/iteration. 32 iterations. */ +#endif /* __sparclite__ */ +#endif /* __sparc_v8__ */ +#endif /* sparc32 */ + +#if ((defined (__sparc__) && defined (__arch64__)) || defined (__sparcv9)) \ + && W_TYPE_SIZE == 64 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addcc %r4,%5,%1\n\t" \ + "add %r2,%3,%0\n\t" \ + "bcs,a,pn %%xcc, 1f\n\t" \ + "add %0, 1, %0\n" \ + "1:" \ + : "=r" ((UDItype)(sh)), \ + "=&r" ((UDItype)(sl)) \ + : "%rJ" ((UDItype)(ah)), \ + "rI" ((UDItype)(bh)), \ + "%rJ" ((UDItype)(al)), \ + "rI" ((UDItype)(bl)) \ + __CLOBBER_CC) + +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subcc %r4,%5,%1\n\t" \ + "sub %r2,%3,%0\n\t" \ + "bcs,a,pn %%xcc, 1f\n\t" \ + "sub %0, 1, %0\n\t" \ + "1:" \ + : "=r" ((UDItype)(sh)), \ + "=&r" ((UDItype)(sl)) \ + : "rJ" ((UDItype)(ah)), \ + "rI" ((UDItype)(bh)), \ + "rJ" ((UDItype)(al)), \ + "rI" ((UDItype)(bl)) \ + __CLOBBER_CC) + +#define umul_ppmm(wh, wl, u, v) \ + do { \ + UDItype tmp1, tmp2, tmp3, tmp4; \ + __asm__ __volatile__ ( \ + "srl %7,0,%3\n\t" \ + "mulx %3,%6,%1\n\t" \ + "srlx %6,32,%2\n\t" \ + "mulx %2,%3,%4\n\t" \ + "sllx %4,32,%5\n\t" \ + "srl %6,0,%3\n\t" \ + "sub %1,%5,%5\n\t" \ + "srlx %5,32,%5\n\t" \ + "addcc %4,%5,%4\n\t" \ + "srlx %7,32,%5\n\t" \ + "mulx %3,%5,%3\n\t" \ + "mulx %2,%5,%5\n\t" \ + "sethi %%hi(0x80000000),%2\n\t" \ + "addcc %4,%3,%4\n\t" \ + "srlx %4,32,%4\n\t" \ + "add %2,%2,%2\n\t" \ + "movcc %%xcc,%%g0,%2\n\t" \ + "addcc %5,%4,%5\n\t" \ + "sllx %3,32,%3\n\t" \ + "add %1,%3,%1\n\t" \ + "add %5,%2,%0" \ + : "=r" ((UDItype)(wh)), \ + "=&r" ((UDItype)(wl)), \ + "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3), "=&r" (tmp4) \ + : "r" ((UDItype)(u)), \ + "r" ((UDItype)(v)) \ + __CLOBBER_CC); \ + } while (0) +#define UMUL_TIME 96 +#define UDIV_TIME 230 +#endif /* sparc64 */ + +#if defined (__vax__) && W_TYPE_SIZE == 32 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addl2 %5,%1\n\tadwc %3,%0" \ + : "=g" ((USItype) (sh)), \ + "=&g" ((USItype) (sl)) \ + : "%0" ((USItype) (ah)), \ + "g" ((USItype) (bh)), \ + "%1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subl2 %5,%1\n\tsbwc %3,%0" \ + : "=g" ((USItype) (sh)), \ + "=&g" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "g" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define umul_ppmm(xh, xl, m0, m1) \ + do { \ + union { \ + UDItype __ll; \ + struct {USItype __l, __h;} __i; \ + } __xx; \ + USItype __m0 = (m0), __m1 = (m1); \ + __asm__ ("emul %1,%2,$0,%0" \ + : "=r" (__xx.__ll) \ + : "g" (__m0), \ + "g" (__m1)); \ + (xh) = __xx.__i.__h; \ + (xl) = __xx.__i.__l; \ + (xh) += ((((SItype) __m0 >> 31) & __m1) \ + + (((SItype) __m1 >> 31) & __m0)); \ + } while (0) +#define sdiv_qrnnd(q, r, n1, n0, d) \ + do { \ + union {DItype __ll; \ + struct {SItype __l, __h;} __i; \ + } __xx; \ + __xx.__i.__h = n1; __xx.__i.__l = n0; \ + __asm__ ("ediv %3,%2,%0,%1" \ + : "=g" (q), "=g" (r) \ + : "g" (__xx.__ll), "g" (d)); \ + } while (0) +#endif /* __vax__ */ + +#if defined (__xtensa__) && W_TYPE_SIZE == 32 +/* This code is not Xtensa-configuration-specific, so rely on the compiler + to expand builtin functions depending on what configuration features + are available. This avoids library calls when the operation can be + performed in-line. */ +#define umul_ppmm(w1, w0, u, v) \ + do { \ + DWunion __w; \ + __w.ll = __builtin_umulsidi3 (u, v); \ + w1 = __w.s.high; \ + w0 = __w.s.low; \ + } while (0) +#define __umulsidi3(u, v) __builtin_umulsidi3 (u, v) +#define count_leading_zeros(COUNT, X) ((COUNT) = __builtin_clz (X)) +#define count_trailing_zeros(COUNT, X) ((COUNT) = __builtin_ctz (X)) +#endif /* __xtensa__ */ + +#if defined (__z8000__) && W_TYPE_SIZE == 16 +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \ + : "=r" ((unsigned int)(sh)), \ + "=&r" ((unsigned int)(sl)) \ + : "%0" ((unsigned int)(ah)), \ + "r" ((unsigned int)(bh)), \ + "%1" ((unsigned int)(al)), \ + "rQR" ((unsigned int)(bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \ + : "=r" ((unsigned int)(sh)), \ + "=&r" ((unsigned int)(sl)) \ + : "0" ((unsigned int)(ah)), \ + "r" ((unsigned int)(bh)), \ + "1" ((unsigned int)(al)), \ + "rQR" ((unsigned int)(bl))) +#define umul_ppmm(xh, xl, m0, m1) \ + do { \ + union {long int __ll; \ + struct {unsigned int __h, __l;} __i; \ + } __xx; \ + unsigned int __m0 = (m0), __m1 = (m1); \ + __asm__ ("mult %S0,%H3" \ + : "=r" (__xx.__i.__h), \ + "=r" (__xx.__i.__l) \ + : "%1" (__m0), \ + "rQR" (__m1)); \ + (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \ + (xh) += ((((signed int) __m0 >> 15) & __m1) \ + + (((signed int) __m1 >> 15) & __m0)); \ + } while (0) +#endif /* __z8000__ */ + +#endif /* __GNUC__ */ + +/* If this machine has no inline assembler, use C macros. */ + +#if !defined (add_ssaaaa) +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + UWtype __x; \ + __x = (al) + (bl); \ + (sh) = (ah) + (bh) + (__x < (al)); \ + (sl) = __x; \ + } while (0) +#endif + +#if !defined (sub_ddmmss) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + UWtype __x; \ + __x = (al) - (bl); \ + (sh) = (ah) - (bh) - (__x > (al)); \ + (sl) = __x; \ + } while (0) +#endif + +/* If we lack umul_ppmm but have smul_ppmm, define umul_ppmm in terms of + smul_ppmm. */ +#if !defined (umul_ppmm) && defined (smul_ppmm) +#define umul_ppmm(w1, w0, u, v) \ + do { \ + UWtype __w1; \ + UWtype __xm0 = (u), __xm1 = (v); \ + smul_ppmm (__w1, w0, __xm0, __xm1); \ + (w1) = __w1 + (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \ + + (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \ + } while (0) +#endif + +/* If we still don't have umul_ppmm, define it using plain C. */ +#if !defined (umul_ppmm) +#define umul_ppmm(w1, w0, u, v) \ + do { \ + UWtype __x0, __x1, __x2, __x3; \ + UHWtype __ul, __vl, __uh, __vh; \ + \ + __ul = __ll_lowpart (u); \ + __uh = __ll_highpart (u); \ + __vl = __ll_lowpart (v); \ + __vh = __ll_highpart (v); \ + \ + __x0 = (UWtype) __ul * __vl; \ + __x1 = (UWtype) __ul * __vh; \ + __x2 = (UWtype) __uh * __vl; \ + __x3 = (UWtype) __uh * __vh; \ + \ + __x1 += __ll_highpart (__x0);/* this can't give carry */ \ + __x1 += __x2; /* but this indeed can */ \ + if (__x1 < __x2) /* did we get it? */ \ + __x3 += __ll_B; /* yes, add it in the proper pos. */ \ + \ + (w1) = __x3 + __ll_highpart (__x1); \ + (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \ + } while (0) +#endif + +#if !defined (__umulsidi3) +#define __umulsidi3(u, v) \ + ({DWunion __w; \ + umul_ppmm (__w.s.high, __w.s.low, u, v); \ + __w.ll; }) +#endif + +/* Define this unconditionally, so it can be used for debugging. */ +#define __udiv_qrnnd_c(q, r, n1, n0, d) \ + do { \ + UWtype __d1, __d0, __q1, __q0; \ + UWtype __r1, __r0, __m; \ + __d1 = __ll_highpart (d); \ + __d0 = __ll_lowpart (d); \ + \ + __r1 = (n1) % __d1; \ + __q1 = (n1) / __d1; \ + __m = (UWtype) __q1 * __d0; \ + __r1 = __r1 * __ll_B | __ll_highpart (n0); \ + if (__r1 < __m) \ + { \ + __q1--, __r1 += (d); \ + if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\ + if (__r1 < __m) \ + __q1--, __r1 += (d); \ + } \ + __r1 -= __m; \ + \ + __r0 = __r1 % __d1; \ + __q0 = __r1 / __d1; \ + __m = (UWtype) __q0 * __d0; \ + __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ + if (__r0 < __m) \ + { \ + __q0--, __r0 += (d); \ + if (__r0 >= (d)) \ + if (__r0 < __m) \ + __q0--, __r0 += (d); \ + } \ + __r0 -= __m; \ + \ + (q) = (UWtype) __q1 * __ll_B | __q0; \ + (r) = __r0; \ + } while (0) + +/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through + __udiv_w_sdiv (defined in libgcc or elsewhere). */ +#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd) +#define udiv_qrnnd(q, r, nh, nl, d) \ + do { \ + USItype __r; \ + (q) = __udiv_w_sdiv (&__r, nh, nl, d); \ + (r) = __r; \ + } while (0) +#endif + +/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */ +#if !defined (udiv_qrnnd) +#define UDIV_NEEDS_NORMALIZATION 1 +#define udiv_qrnnd __udiv_qrnnd_c +#endif + +#if !defined (count_leading_zeros) +#define count_leading_zeros(count, x) \ + do { \ + UWtype __xr = (x); \ + UWtype __a; \ + \ + if (W_TYPE_SIZE <= 32) \ + { \ + __a = __xr < ((UWtype)1<<2*__BITS4) \ + ? (__xr < ((UWtype)1<<__BITS4) ? 0 : __BITS4) \ + : (__xr < ((UWtype)1<<3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \ + } \ + else \ + { \ + for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \ + if (((__xr >> __a) & 0xff) != 0) \ + break; \ + } \ + \ + (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \ + } while (0) +#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE +#endif + +#if !defined (count_trailing_zeros) +/* Define count_trailing_zeros using count_leading_zeros. The latter might be + defined in asm, but if it is not, the C version above is good enough. */ +#define count_trailing_zeros(count, x) \ + do { \ + UWtype __ctz_x = (x); \ + UWtype __ctz_c; \ + count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \ + (count) = W_TYPE_SIZE - 1 - __ctz_c; \ + } while (0) +#endif + +#ifndef UDIV_NEEDS_NORMALIZATION +#define UDIV_NEEDS_NORMALIZATION 0 +#endif diff --git a/libports/include/gmp/config.h b/libports/include/gmp/config.h new file mode 100644 index 000000000..a564fa479 --- /dev/null +++ b/libports/include/gmp/config.h @@ -0,0 +1,522 @@ +/* config.h. Generated from config.in by configure. */ +/* config.in. Generated from configure.in by autoheader. */ + +/* + +Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +2007 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation; either version 3 of the License, or (at +your option) any later version. + +The GNU MP Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. +*/ + +/* The gmp-mparam.h file (a string) the tune program should suggest updating. + */ +#define GMP_MPARAM_H_SUGGEST "./mpn/x86/p6/sse2/gmp-mparam.h" + +/* Define to 1 if you have the `alarm' function. */ +#define HAVE_ALARM 1 + +/* Define to 1 if alloca() works (via gmp-impl.h). */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((const)) */ +#define HAVE_ATTRIBUTE_CONST 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((malloc)) */ +#define HAVE_ATTRIBUTE_MALLOC 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((mode (XX))) + */ +#define HAVE_ATTRIBUTE_MODE 1 + +/* Define to 1 if the compiler accepts gcc style __attribute__ ((noreturn)) */ +#define HAVE_ATTRIBUTE_NORETURN 1 + +/* Define to 1 if you have the `attr_get' function. */ +/* #undef HAVE_ATTR_GET */ + +/* Define to 1 if tests/libtests has calling conventions checking for the CPU + */ +#define HAVE_CALLING_CONVENTIONS 1 + +/* Define to 1 if you have the `clock' function. */ +#define HAVE_CLOCK 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* Define to 1 if you have the `cputime' function. */ +/* #undef HAVE_CPUTIME */ + +/* Define to 1 if you have the declaration of `fgetc', and to 0 if you don't. + */ +#define HAVE_DECL_FGETC 1 + +/* Define to 1 if you have the declaration of `fscanf', and to 0 if you don't. + */ +#define HAVE_DECL_FSCANF 1 + +/* Define to 1 if you have the declaration of `optarg', and to 0 if you don't. + */ +#define HAVE_DECL_OPTARG 1 + +/* Define to 1 if you have the declaration of `sys_errlist', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_ERRLIST 1 + +/* Define to 1 if you have the declaration of `sys_nerr', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_NERR 1 + +/* Define to 1 if you have the declaration of `ungetc', and to 0 if you don't. + */ +#define HAVE_DECL_UNGETC 1 + +/* Define to 1 if you have the declaration of `vfprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VFPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define one of the following to 1 for the format of a `double'. + If your format is not among these choices, or you don't know what it is, + then leave all undefined. + IEEE_LITTLE_SWAPPED means little endian, but with the two 4-byte halves + swapped, as used by ARM CPUs in little endian mode. */ +/* #undef HAVE_DOUBLE_IEEE_BIG_ENDIAN */ +#define HAVE_DOUBLE_IEEE_LITTLE_ENDIAN 1 +/* #undef HAVE_DOUBLE_IEEE_LITTLE_SWAPPED */ +/* #undef HAVE_DOUBLE_VAX_D */ +/* #undef HAVE_DOUBLE_VAX_G */ +/* #undef HAVE_DOUBLE_CRAY_CFP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FLOAT_H 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `getrusage' function. */ +#define HAVE_GETRUSAGE 1 + +/* Define to 1 if you have the `getsysinfo' function. */ +/* #undef HAVE_GETSYSINFO */ + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define one of these to 1 for the host CPU family. + If your CPU is not in any of these families, leave all undefined. + For an AMD64 chip, define "x86" in ABI=32, but not in ABI=64. */ +/* #undef HAVE_HOST_CPU_FAMILY_alpha */ +/* #undef HAVE_HOST_CPU_FAMILY_m68k */ +/* #undef HAVE_HOST_CPU_FAMILY_power */ +/* #undef HAVE_HOST_CPU_FAMILY_powerpc */ +#define HAVE_HOST_CPU_FAMILY_x86 1 + +/* Define one of the following to 1 for the host CPU, as per the output of + ./config.guess. If your CPU is not listed here, leave all undefined. */ +/* #undef HAVE_HOST_CPU_alphaev67 */ +/* #undef HAVE_HOST_CPU_alphaev68 */ +/* #undef HAVE_HOST_CPU_alphaev7 */ +/* #undef HAVE_HOST_CPU_m68020 */ +/* #undef HAVE_HOST_CPU_m68030 */ +/* #undef HAVE_HOST_CPU_m68040 */ +/* #undef HAVE_HOST_CPU_m68060 */ +/* #undef HAVE_HOST_CPU_m68360 */ +/* #undef HAVE_HOST_CPU_powerpc604 */ +/* #undef HAVE_HOST_CPU_powerpc604e */ +/* #undef HAVE_HOST_CPU_powerpc750 */ +/* #undef HAVE_HOST_CPU_powerpc7400 */ +/* #undef HAVE_HOST_CPU_supersparc */ +/* #undef HAVE_HOST_CPU_i386 */ +/* #undef HAVE_HOST_CPU_i586 */ +/* #undef HAVE_HOST_CPU_i686 */ +/* #undef HAVE_HOST_CPU_pentium */ +/* #undef HAVE_HOST_CPU_pentiummmx */ +/* #undef HAVE_HOST_CPU_pentiumpro */ +/* #undef HAVE_HOST_CPU_pentium2 */ +/* #undef HAVE_HOST_CPU_pentium3 */ + +/* Define to 1 if the system has the type `intmax_t'. */ +#define HAVE_INTMAX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INVENT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define one of these to 1 for the endianness of `mp_limb_t'. + If the endianness is not a simple big or little, or you don't know what + it is, then leave both undefined. */ +/* #undef HAVE_LIMB_BIG_ENDIAN */ +#define HAVE_LIMB_LITTLE_ENDIAN 1 + +/* Define to 1 if you have the `localeconv' function. */ +#define HAVE_LOCALECONV 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if the system has the type `long double'. */ +#define HAVE_LONG_DOUBLE 1 + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MACHINE_HAL_SYSINFO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mmap' function. */ +#define HAVE_MMAP 1 + +/* Define to 1 if you have the `mprotect' function. */ +#define HAVE_MPROTECT 1 + +/* Define to 1 each of the following for which a native (ie. CPU specific) + implementation of the corresponding routine exists. */ +#define HAVE_NATIVE_mpn_add_n 1 +#define HAVE_NATIVE_mpn_add_nc 1 +/* #undef HAVE_NATIVE_mpn_addlsh1_n */ +/* #undef HAVE_NATIVE_mpn_addmul_1c */ +/* #undef HAVE_NATIVE_mpn_addmul_2 */ +/* #undef HAVE_NATIVE_mpn_addmul_3 */ +/* #undef HAVE_NATIVE_mpn_addmul_4 */ +/* #undef HAVE_NATIVE_mpn_addmul_5 */ +/* #undef HAVE_NATIVE_mpn_addmul_6 */ +/* #undef HAVE_NATIVE_mpn_addmul_7 */ +/* #undef HAVE_NATIVE_mpn_addmul_8 */ +/* #undef HAVE_NATIVE_mpn_addsub_n */ +/* #undef HAVE_NATIVE_mpn_addaddmul_1msb0 */ +/* #undef HAVE_NATIVE_mpn_and_n */ +/* #undef HAVE_NATIVE_mpn_andn_n */ +#define HAVE_NATIVE_mpn_bdiv_dbm1c 1 +/* #undef HAVE_NATIVE_mpn_com_n */ +#define HAVE_NATIVE_mpn_copyd 1 +#define HAVE_NATIVE_mpn_copyi 1 +#define HAVE_NATIVE_mpn_divexact_1 1 +/* #undef HAVE_NATIVE_mpn_divexact_by3c */ +#define HAVE_NATIVE_mpn_divrem_1 1 +#define HAVE_NATIVE_mpn_divrem_1c 1 +#define HAVE_NATIVE_mpn_divrem_2 1 +/* #undef HAVE_NATIVE_mpn_gcd_1 */ +/* #undef HAVE_NATIVE_mpn_invert_limb */ +/* #undef HAVE_NATIVE_mpn_ior_n */ +/* #undef HAVE_NATIVE_mpn_iorn_n */ +/* #undef HAVE_NATIVE_mpn_lshiftc */ +#define HAVE_NATIVE_mpn_mod_1 1 +#define HAVE_NATIVE_mpn_mod_1c 1 +#define HAVE_NATIVE_mpn_modexact_1_odd 1 +#define HAVE_NATIVE_mpn_modexact_1c_odd 1 +/* #undef HAVE_NATIVE_mpn_mul_1c */ +/* #undef HAVE_NATIVE_mpn_mul_2 */ +/* #undef HAVE_NATIVE_mpn_mul_3 */ +/* #undef HAVE_NATIVE_mpn_mul_4 */ +/* #undef HAVE_NATIVE_mpn_nand_n */ +/* #undef HAVE_NATIVE_mpn_nior_n */ +#define HAVE_NATIVE_mpn_preinv_divrem_1 1 +#define HAVE_NATIVE_mpn_preinv_mod_1 1 +/* #undef HAVE_NATIVE_mpn_redc_1 */ +/* #undef HAVE_NATIVE_mpn_redc_2 */ +/* #undef HAVE_NATIVE_mpn_rsh1add_n */ +/* #undef HAVE_NATIVE_mpn_rsh1sub_n */ +#define HAVE_NATIVE_mpn_sqr_basecase 1 +/* #undef HAVE_NATIVE_mpn_sqr_diagonal */ +#define HAVE_NATIVE_mpn_sub_n 1 +#define HAVE_NATIVE_mpn_sub_nc 1 +/* #undef HAVE_NATIVE_mpn_sublsh1_n */ +/* #undef HAVE_NATIVE_mpn_submul_1c */ +#define HAVE_NATIVE_mpn_umul_ppmm 1 +/* #undef HAVE_NATIVE_mpn_umul_ppmm_r */ +#define HAVE_NATIVE_mpn_udiv_qrnnd 1 +/* #undef HAVE_NATIVE_mpn_udiv_qrnnd_r */ +/* #undef HAVE_NATIVE_mpn_xor_n */ +/* #undef HAVE_NATIVE_mpn_xnor_n */ + +/* Define to 1 if you have the `nl_langinfo' function. */ +#define HAVE_NL_LANGINFO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NL_TYPES_H 1 + +/* Define to 1 if you have the `obstack_vprintf' function. */ +#define HAVE_OBSTACK_VPRINTF 0 + +/* Define to 1 if you have the `popen' function. */ +#define HAVE_POPEN 1 + +/* Define to 1 if you have the `processor_info' function. */ +/* #undef HAVE_PROCESSOR_INFO */ + +/* Define to 1 if `struct pst_processor' exists and contains + `psp_iticksperclktick'. */ +/* #undef HAVE_PSP_ITICKSPERCLKTICK */ + +/* Define to 1 if you have the `pstat_getprocessor' function. */ +/* #undef HAVE_PSTAT_GETPROCESSOR */ + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#define HAVE_PTRDIFF_T 1 + +/* Define to 1 if the system has the type `quad_t'. */ +#define HAVE_QUAD_T 1 + +/* Define to 1 if you have the `raise' function. */ +#define HAVE_RAISE 1 + +/* Define to 1 if you have the `read_real_time' function. */ +/* #undef HAVE_READ_REAL_TIME */ + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if you have the `sigaltstack' function. */ +#define HAVE_SIGALTSTACK 1 + +/* Define to 1 if you have the `sigstack' function. */ +#define HAVE_SIGSTACK 1 + +/* Tune directory speed_cyclecounter, undef=none, 1=32bits, 2=64bits) */ +#define HAVE_SPEED_CYCLECOUNTER 2 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SSTREAM */ + +/* Define to 1 if the system has the type `stack_t'. */ +#define HAVE_STACK_T 1 + +/* Define to 1 if exists and works */ +#define HAVE_STDARG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if the system has the type `std::locale'. */ +/* #undef HAVE_STD__LOCALE */ + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if cpp supports the ANSI # stringizing operator. */ +#define HAVE_STRINGIZE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the `sysctl' function. */ +#define HAVE_SYSCTL 1 + +/* Define to 1 if you have the `sysctlbyname' function. */ +/* #undef HAVE_SYSCTLBYNAME */ + +/* Define to 1 if you have the `syssgi' function. */ +/* #undef HAVE_SYSSGI */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_ATTRIBUTES_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOGRAPH_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PROCESSOR_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PSTAT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSINFO_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSSGI_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSTEMCFG_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIMES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the `times' function. */ +#define HAVE_TIMES 1 + +/* Define to 1 if the system has the type `uint_least32_t'. */ +#define HAVE_UINT_LEAST32_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vsnprintf' function and it works properly. */ +#define HAVE_VSNPRINTF 1 + +/* Assembler local label prefix */ +#define LSYM_PREFIX ".L" + +/* Name of package */ +#define PACKAGE "gmp" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "gmp-bugs@gmplib.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "GNU MP" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "GNU MP 4.3.2" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "gmp" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "4.3.2" + +/* Define to 1 if the C compiler supports function prototypes. */ +#define PROTOTYPES 1 + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* The size of `mp_limb_t', as computed by sizeof. */ +#define SIZEOF_MP_LIMB_T 4 + +/* The size of `unsigned', as computed by sizeof. */ +#define SIZEOF_UNSIGNED 4 + +/* The size of `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The size of `unsigned short', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_SHORT 2 + +/* Define to 1 if sscanf requires writable inputs */ +/* #undef SSCANF_WRITABLE_INPUT */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Maximum size the tune program can test for SQR_KARATSUBA_THRESHOLD */ +/* #undef TUNE_SQR_KARATSUBA_MAX */ + +/* Version number of package */ +#define VERSION "4.3.2" + +/* Define to 1 to enable ASSERT checking, per --enable-assert */ +/* #undef WANT_ASSERT */ + +/* Define to 1 when building a fat binary. */ +/* #undef WANT_FAT_BINARY */ + +/* Define to 1 to enable FFTs for multiplication, per --enable-fft */ +#define WANT_FFT 1 + +/* Define to 1 if --enable-profiling=gprof */ +/* #undef WANT_PROFILING_GPROF */ + +/* Define to 1 if --enable-profiling=instrument */ +/* #undef WANT_PROFILING_INSTRUMENT */ + +/* Define to 1 if --enable-profiling=prof */ +/* #undef WANT_PROFILING_PROF */ + +/* Define one of these to 1 for the desired temporary memory allocation + method, per --enable-alloca. */ +#define WANT_TMP_ALLOCA 1 +/* #undef WANT_TMP_REENTRANT */ +/* #undef WANT_TMP_NOTREENTRANT */ +/* #undef WANT_TMP_DEBUG */ + +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ +#define YYTEXT_POINTER 1 + +/* Define like PROTOTYPES; this can be used by system headers. */ +#define __PROTOTYPES 1 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to equivalent of C99 restrict keyword, or to nothing if this is not + supported. Do not define if restrict is supported directly. */ +/* #undef restrict */ + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +/* #undef volatile */ diff --git a/libports/include/gmp/x86_32/fac_ui.h b/libports/include/gmp/x86_32/fac_ui.h new file mode 100644 index 000000000..09ddf43c6 --- /dev/null +++ b/libports/include/gmp/x86_32/fac_ui.h @@ -0,0 +1,19 @@ +/* This file is automatically generated by gen-fac_ui.c */ + +#if GMP_NUMB_BITS != 32 +Error , error this data is for 32 GMP_NUMB_BITS only +#endif +#if GMP_LIMB_BITS != 32 +Error , error this data is for 32 GMP_LIMB_BITS only +#endif +/* This table is 0!,1!,2!,3!,...,n! where n! has <= GMP_NUMB_BITS bits */ +#define ONE_LIMB_FACTORIAL_TABLE CNST_LIMB(0x1),CNST_LIMB(0x1),CNST_LIMB(0x2),CNST_LIMB(0x6),CNST_LIMB(0x18),CNST_LIMB(0x78),CNST_LIMB(0x2d0),CNST_LIMB(0x13b0),CNST_LIMB(0x9d80),CNST_LIMB(0x58980),CNST_LIMB(0x375f00),CNST_LIMB(0x2611500),CNST_LIMB(0x1c8cfc00) + +/* is 2^(GMP_LIMB_BITS+1)/exp(1) */ +#define FAC2OVERE CNST_LIMB(0xbc5c254b) + +/* FACMULn is largest odd x such that x*(x+2)*...*(x+2(n-1))<=2^GMP_NUMB_BITS-1 */ + +#define FACMUL2 CNST_LIMB(0xffff) +#define FACMUL3 CNST_LIMB(0x657) +#define FACMUL4 CNST_LIMB(0xfd) diff --git a/libports/include/gmp/x86_32/fib_table.h b/libports/include/gmp/x86_32/fib_table.h new file mode 100644 index 000000000..558aeb64b --- /dev/null +++ b/libports/include/gmp/x86_32/fib_table.h @@ -0,0 +1,8 @@ +/* This file generated by gen-fib.c - DO NOT EDIT. */ + +#if GMP_NUMB_BITS != 32 +Error, error, this data is for 32 bits +#endif + +#define FIB_TABLE_LIMIT 47 +#define FIB_TABLE_LUCNUM_LIMIT 46 diff --git a/libports/include/gmp/x86_32/gmp.h b/libports/include/gmp/x86_32/gmp.h new file mode 100644 index 000000000..fa4e2f78e --- /dev/null +++ b/libports/include/gmp/x86_32/gmp.h @@ -0,0 +1,2230 @@ +/* Definitions for GNU multiple precision functions. -*- mode: c -*- + +Copyright 1991, 1993, 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2003, +2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +The GNU MP Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */ + +#ifndef __GMP_H__ + +#if defined (__cplusplus) +#include /* for std::istream, std::ostream, std::string */ +#include +#endif + + +/* Instantiated by configure. */ +#if ! defined (__GMP_WITHIN_CONFIGURE) +#define __GMP_BITS_PER_MP_LIMB 32 +#define __GMP_HAVE_HOST_CPU_FAMILY_power 0 +#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 0 +#define GMP_LIMB_BITS 32 +#define GMP_NAIL_BITS 0 +#endif +#define GMP_NUMB_BITS (GMP_LIMB_BITS - GMP_NAIL_BITS) +#define GMP_NUMB_MASK ((~ __GMP_CAST (mp_limb_t, 0)) >> GMP_NAIL_BITS) +#define GMP_NUMB_MAX GMP_NUMB_MASK +#define GMP_NAIL_MASK (~ GMP_NUMB_MASK) + + +/* The following (everything under ifndef __GNU_MP__) must be identical in + gmp.h and mp.h to allow both to be included in an application or during + the library build. */ +#ifndef __GNU_MP__ +#define __GNU_MP__ 4 + +#define __need_size_t /* tell gcc stddef.h we only want size_t */ +#if defined (__cplusplus) +#include /* for size_t */ +#else +#include /* for size_t */ +#endif +#undef __need_size_t + +/* Instantiated by configure. */ +#if ! defined (__GMP_WITHIN_CONFIGURE) +/* #undef _LONG_LONG_LIMB */ +#define __GMP_LIBGMP_DLL 0 +#endif + + +/* __STDC__ - some ANSI compilers define this only to 0, hence the use of + "defined" and not "__STDC__-0". In particular Sun workshop C 5.0 + sets __STDC__ to 0, but requires "##" for token pasting. + + _AIX - gnu ansidecl.h asserts that all known AIX compilers are ANSI but + don't always define __STDC__. + + __DECC - current versions of DEC C (5.9 for instance) for alpha are ANSI, + but don't define __STDC__ in their default mode. Don't know if old + versions might have been K&R, but let's not worry about that unless + someone is still using one. + + _mips - gnu ansidecl.h says the RISC/OS MIPS compiler is ANSI in SVR4 + mode, but doesn't define __STDC__. + + _MSC_VER - Microsoft C is ANSI, but __STDC__ is undefined unless the /Za + option is given (in which case it's 1). + + _WIN32 - tested for by gnu ansidecl.h, no doubt on the assumption that + all w32 compilers are ansi. + + Note: This same set of tests is used by gen-psqr.c and + demos/expr/expr-impl.h, so if anything needs adding, then be sure to + update those too. */ + +#if defined (__STDC__) \ + || defined (__cplusplus) \ + || defined (_AIX) \ + || defined (__DECC) \ + || (defined (__mips) && defined (_SYSTYPE_SVR4)) \ + || defined (_MSC_VER) \ + || defined (_WIN32) +#define __GMP_HAVE_CONST 1 +#define __GMP_HAVE_PROTOTYPES 1 +#define __GMP_HAVE_TOKEN_PASTE 1 +#else +#define __GMP_HAVE_CONST 0 +#define __GMP_HAVE_PROTOTYPES 0 +#define __GMP_HAVE_TOKEN_PASTE 0 +#endif + + +#if __GMP_HAVE_CONST +#define __gmp_const const +#define __gmp_signed signed +#else +#define __gmp_const +#define __gmp_signed +#endif + + +/* __GMP_DECLSPEC supports Windows DLL versions of libgmp, and is empty in + all other circumstances. + + When compiling objects for libgmp, __GMP_DECLSPEC is an export directive, + or when compiling for an application it's an import directive. The two + cases are differentiated by __GMP_WITHIN_GMP defined by the GMP Makefiles + (and not defined from an application). + + __GMP_DECLSPEC_XX is similarly used for libgmpxx. __GMP_WITHIN_GMPXX + indicates when building libgmpxx, and in that case libgmpxx functions are + exports, but libgmp functions which might get called are imports. + + libmp.la uses __GMP_DECLSPEC, just as if it were libgmp.la. libgmp and + libmp don't call each other, so there's no conflict or confusion. + + Libtool DLL_EXPORT define is not used. + + There's no attempt to support GMP built both static and DLL. Doing so + would mean applications would have to tell us which of the two is going + to be used when linking, and that seems very tedious and error prone if + using GMP by hand, and equally tedious from a package since autoconf and + automake don't give much help. + + __GMP_DECLSPEC is required on all documented global functions and + variables, the various internals in gmp-impl.h etc can be left unadorned. + But internals used by the test programs or speed measuring programs + should have __GMP_DECLSPEC, and certainly constants or variables must + have it or the wrong address will be resolved. + + In gcc __declspec can go at either the start or end of a prototype. + + In Microsoft C __declspec must go at the start, or after the type like + void __declspec(...) *foo()". There's no __dllexport or anything to + guard against someone foolish #defining dllexport. _export used to be + available, but no longer. + + In Borland C _export still exists, but needs to go after the type, like + "void _export foo();". Would have to change the __GMP_DECLSPEC syntax to + make use of that. Probably more trouble than it's worth. */ + +#if defined (__GNUC__) +#define __GMP_DECLSPEC_EXPORT __declspec(__dllexport__) +#define __GMP_DECLSPEC_IMPORT __declspec(__dllimport__) +#endif +#if defined (_MSC_VER) || defined (__BORLANDC__) +#define __GMP_DECLSPEC_EXPORT __declspec(dllexport) +#define __GMP_DECLSPEC_IMPORT __declspec(dllimport) +#endif +#ifdef __WATCOMC__ +#define __GMP_DECLSPEC_EXPORT __export +#define __GMP_DECLSPEC_IMPORT __import +#endif +#ifdef __IBMC__ +#define __GMP_DECLSPEC_EXPORT _Export +#define __GMP_DECLSPEC_IMPORT _Import +#endif + +#if __GMP_LIBGMP_DLL +#if __GMP_WITHIN_GMP +/* compiling to go into a DLL libgmp */ +#define __GMP_DECLSPEC __GMP_DECLSPEC_EXPORT +#else +/* compiling to go into an application which will link to a DLL libgmp */ +#define __GMP_DECLSPEC __GMP_DECLSPEC_IMPORT +#endif +#else +/* all other cases */ +#define __GMP_DECLSPEC +#endif + + +#ifdef __GMP_SHORT_LIMB +typedef unsigned int mp_limb_t; +typedef int mp_limb_signed_t; +#else +#ifdef _LONG_LONG_LIMB +typedef unsigned long long int mp_limb_t; +typedef long long int mp_limb_signed_t; +#else +typedef unsigned long int mp_limb_t; +typedef long int mp_limb_signed_t; +#endif +#endif + +/* For reference, note that the name __mpz_struct gets into C++ mangled + function names, which means although the "__" suggests an internal, we + must leave this name for binary compatibility. */ +typedef struct +{ + int _mp_alloc; /* Number of *limbs* allocated and pointed + to by the _mp_d field. */ + int _mp_size; /* abs(_mp_size) is the number of limbs the + last field points to. If _mp_size is + negative this is a negative number. */ + mp_limb_t *_mp_d; /* Pointer to the limbs. */ +} __mpz_struct; + +#endif /* __GNU_MP__ */ + + +typedef __mpz_struct MP_INT; /* gmp 1 source compatibility */ +typedef __mpz_struct mpz_t[1]; + +typedef mp_limb_t * mp_ptr; +typedef __gmp_const mp_limb_t * mp_srcptr; +#if defined (_CRAY) && ! defined (_CRAYMPP) +/* plain `int' is much faster (48 bits) */ +#define __GMP_MP_SIZE_T_INT 1 +typedef int mp_size_t; +typedef int mp_exp_t; +#else +#define __GMP_MP_SIZE_T_INT 0 +typedef long int mp_size_t; +typedef long int mp_exp_t; +#endif + +typedef struct +{ + __mpz_struct _mp_num; + __mpz_struct _mp_den; +} __mpq_struct; + +typedef __mpq_struct MP_RAT; /* gmp 1 source compatibility */ +typedef __mpq_struct mpq_t[1]; + +typedef struct +{ + int _mp_prec; /* Max precision, in number of `mp_limb_t's. + Set by mpf_init and modified by + mpf_set_prec. The area pointed to by the + _mp_d field contains `prec' + 1 limbs. */ + int _mp_size; /* abs(_mp_size) is the number of limbs the + last field points to. If _mp_size is + negative this is a negative number. */ + mp_exp_t _mp_exp; /* Exponent, in the base of `mp_limb_t'. */ + mp_limb_t *_mp_d; /* Pointer to the limbs. */ +} __mpf_struct; + +/* typedef __mpf_struct MP_FLOAT; */ +typedef __mpf_struct mpf_t[1]; + +/* Available random number generation algorithms. */ +typedef enum +{ + GMP_RAND_ALG_DEFAULT = 0, + GMP_RAND_ALG_LC = GMP_RAND_ALG_DEFAULT /* Linear congruential. */ +} gmp_randalg_t; + +/* Random state struct. */ +typedef struct +{ + mpz_t _mp_seed; /* _mp_d member points to state of the generator. */ + gmp_randalg_t _mp_alg; /* Currently unused. */ + union { + void *_mp_lc; /* Pointer to function pointers structure. */ + } _mp_algdata; +} __gmp_randstate_struct; +typedef __gmp_randstate_struct gmp_randstate_t[1]; + +/* Types for function declarations in gmp files. */ +/* ??? Should not pollute user name space with these ??? */ +typedef __gmp_const __mpz_struct *mpz_srcptr; +typedef __mpz_struct *mpz_ptr; +typedef __gmp_const __mpf_struct *mpf_srcptr; +typedef __mpf_struct *mpf_ptr; +typedef __gmp_const __mpq_struct *mpq_srcptr; +typedef __mpq_struct *mpq_ptr; + + +/* This is not wanted in mp.h, so put it outside the __GNU_MP__ common + section. */ +#if __GMP_LIBGMP_DLL +#if __GMP_WITHIN_GMPXX +/* compiling to go into a DLL libgmpxx */ +#define __GMP_DECLSPEC_XX __GMP_DECLSPEC_EXPORT +#else +/* compiling to go into a application which will link to a DLL libgmpxx */ +#define __GMP_DECLSPEC_XX __GMP_DECLSPEC_IMPORT +#endif +#else +/* all other cases */ +#define __GMP_DECLSPEC_XX +#endif + + +#if __GMP_HAVE_PROTOTYPES +#define __GMP_PROTO(x) x +#else +#define __GMP_PROTO(x) () +#endif + +#ifndef __MPN +#if __GMP_HAVE_TOKEN_PASTE +#define __MPN(x) __gmpn_##x +#else +#define __MPN(x) __gmpn_/**/x +#endif +#endif + +/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4, + defines EOF but not FILE. */ +#if defined (FILE) \ + || defined (H_STDIO) \ + || defined (_H_STDIO) /* AIX */ \ + || defined (_STDIO_H) /* glibc, Sun, SCO */ \ + || defined (_STDIO_H_) /* BSD, OSF */ \ + || defined (__STDIO_H) /* Borland */ \ + || defined (__STDIO_H__) /* IRIX */ \ + || defined (_STDIO_INCLUDED) /* HPUX */ \ + || defined (__dj_include_stdio_h_) /* DJGPP */ \ + || defined (_FILE_DEFINED) /* Microsoft */ \ + || defined (__STDIO__) /* Apple MPW MrC */ \ + || defined (_MSL_STDIO_H) /* Metrowerks */ \ + || defined (_STDIO_H_INCLUDED) /* QNX4 */ \ + || defined (_ISO_STDIO_ISO_H) /* Sun C++ */ +#define _GMP_H_HAVE_FILE 1 +#endif + +/* In ISO C, if a prototype involving "struct obstack *" is given without + that structure defined, then the struct is scoped down to just the + prototype, causing a conflict if it's subsequently defined for real. So + only give prototypes if we've got obstack.h. */ +#if defined (_OBSTACK_H) /* glibc */ +#define _GMP_H_HAVE_OBSTACK 1 +#endif + +/* The prototypes for gmp_vprintf etc are provided only if va_list is + available, via an application having included or . + Usually va_list is a typedef so can't be tested directly, but C99 + specifies that va_start is a macro (and it was normally a macro on past + systems too), so look for that. + + will define some sort of va_list for vprintf and vfprintf, but + let's not bother trying to use that since it's not standard and since + application uses for gmp_vprintf etc will almost certainly require the + whole or anyway. */ + +#ifdef va_start +#define _GMP_H_HAVE_VA_LIST 1 +#endif + +/* Test for gcc >= maj.min, as per __GNUC_PREREQ in glibc */ +#if defined (__GNUC__) && defined (__GNUC_MINOR__) +#define __GMP_GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +#define __GMP_GNUC_PREREQ(maj, min) 0 +#endif + +/* "pure" is in gcc 2.96 and up, see "(gcc)Function Attributes". Basically + it means a function does nothing but examine its arguments and memory + (global or via arguments) to generate a return value, but changes nothing + and has no side-effects. __GMP_NO_ATTRIBUTE_CONST_PURE lets + tune/common.c etc turn this off when trying to write timing loops. */ +#if __GMP_GNUC_PREREQ (2,96) && ! defined (__GMP_NO_ATTRIBUTE_CONST_PURE) +#define __GMP_ATTRIBUTE_PURE __attribute__ ((__pure__)) +#else +#define __GMP_ATTRIBUTE_PURE +#endif + + +/* __GMP_CAST allows us to use static_cast in C++, so our macros are clean + to "g++ -Wold-style-cast". + + Casts in "extern inline" code within an extern "C" block don't induce + these warnings, so __GMP_CAST only needs to be used on documented + macros. */ + +#ifdef __cplusplus +#define __GMP_CAST(type, expr) (static_cast (expr)) +#else +#define __GMP_CAST(type, expr) ((type) (expr)) +#endif + + +/* An empty "throw ()" means the function doesn't throw any C++ exceptions, + this can save some stack frame info in applications. + + Currently it's given only on functions which never divide-by-zero etc, + don't allocate memory, and are expected to never need to allocate memory. + This leaves open the possibility of a C++ throw from a future GMP + exceptions scheme. + + mpz_set_ui etc are omitted to leave open the lazy allocation scheme + described in doc/tasks.html. mpz_get_d etc are omitted to leave open + exceptions for float overflows. + + Note that __GMP_NOTHROW must be given on any inlines the same as on their + prototypes (for g++ at least, where they're used together). Note also + that g++ 3.0 demands that __GMP_NOTHROW is before other attributes like + __GMP_ATTRIBUTE_PURE. */ + +#if defined (__cplusplus) +#define __GMP_NOTHROW throw () +#else +#define __GMP_NOTHROW +#endif + + +/* PORTME: What other compilers have a useful "extern inline"? "static + inline" would be an acceptable substitute if the compiler (or linker) + discards unused statics. */ + + /* gcc has __inline__ in all modes, including strict ansi. Give a prototype + for an inline too, so as to correctly specify "dllimport" on windows, in + case the function is called rather than inlined. + GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99 + inline semantics, unless -fgnu89-inline is used. */ +#ifdef __GNUC__ +#if (defined __GNUC_STDC_INLINE__) || (__GNUC__ == 4 && __GNUC_MINOR__ == 2) +#define __GMP_EXTERN_INLINE extern __inline__ __attribute__ ((__gnu_inline__)) +#else +#define __GMP_EXTERN_INLINE extern __inline__ +#endif +#define __GMP_INLINE_PROTOTYPES 1 +#endif + +/* DEC C (eg. version 5.9) supports "static __inline foo()", even in -std1 + strict ANSI mode. Inlining is done even when not optimizing (ie. -O0 + mode, which is the default), but an unnecessary local copy of foo is + emitted unless -O is used. "extern __inline" is accepted, but the + "extern" appears to be ignored, ie. it becomes a plain global function + but which is inlined within its file. Don't know if all old versions of + DEC C supported __inline, but as a start let's do the right thing for + current versions. */ +#ifdef __DECC +#define __GMP_EXTERN_INLINE static __inline +#endif + +/* SCO OpenUNIX 8 cc supports "static inline foo()" but not in -Xc strict + ANSI mode (__STDC__ is 1 in that mode). Inlining only actually takes + place under -O. Without -O "foo" seems to be emitted whether it's used + or not, which is wasteful. "extern inline foo()" isn't useful, the + "extern" is apparently ignored, so foo is inlined if possible but also + emitted as a global, which causes multiple definition errors when + building a shared libgmp. */ +#ifdef __SCO_VERSION__ +#if __SCO_VERSION__ > 400000000 && __STDC__ != 1 \ + && ! defined (__GMP_EXTERN_INLINE) +#define __GMP_EXTERN_INLINE static inline +#endif +#endif + +/* Microsoft's C compiler accepts __inline */ +#ifdef _MSC_VER +#define __GMP_EXTERN_INLINE __inline +#endif + +/* Recent enough Sun C compilers accept "extern inline" */ +#if defined (__SUNPRO_C) && __SUNPRO_C >= 0x560 \ + && ! defined (__GMP_EXTERN_INLINE) +#define __GMP_EXTERN_INLINE extern inline +#endif + +/* Somewhat older Sun C compilers accept "static inline" */ +#if defined (__SUNPRO_C) && __SUNPRO_C >= 0x540 \ + && ! defined (__GMP_EXTERN_INLINE) +#define __GMP_EXTERN_INLINE static inline +#endif + + +/* C++ always has "inline" and since it's a normal feature the linker should + discard duplicate non-inlined copies, or if it doesn't then that's a + problem for everyone, not just GMP. */ +#if defined (__cplusplus) && ! defined (__GMP_EXTERN_INLINE) +#define __GMP_EXTERN_INLINE inline +#endif + +/* Don't do any inlining within a configure run, since if the compiler ends + up emitting copies of the code into the object file it can end up + demanding the various support routines (like mpn_popcount) for linking, + making the "alloca" test and perhaps others fail. And on hppa ia64 a + pre-release gcc 3.2 was seen not respecting the "extern" in "extern + __inline__", triggering this problem too. */ +#if defined (__GMP_WITHIN_CONFIGURE) && ! __GMP_WITHIN_CONFIGURE_INLINE +#undef __GMP_EXTERN_INLINE +#endif + +/* By default, don't give a prototype when there's going to be an inline + version. Note in particular that Cray C++ objects to the combination of + prototype and inline. */ +#ifdef __GMP_EXTERN_INLINE +#ifndef __GMP_INLINE_PROTOTYPES +#define __GMP_INLINE_PROTOTYPES 0 +#endif +#else +#define __GMP_INLINE_PROTOTYPES 1 +#endif + + +#define __GMP_ABS(x) ((x) >= 0 ? (x) : -(x)) +#define __GMP_MAX(h,i) ((h) > (i) ? (h) : (i)) + +/* __GMP_USHRT_MAX is not "~ (unsigned short) 0" because short is promoted + to int by "~". */ +#define __GMP_UINT_MAX (~ (unsigned) 0) +#define __GMP_ULONG_MAX (~ (unsigned long) 0) +#define __GMP_USHRT_MAX ((unsigned short) ~0) + + +/* __builtin_expect is in gcc 3.0, and not in 2.95. */ +#if __GMP_GNUC_PREREQ (3,0) +#define __GMP_LIKELY(cond) __builtin_expect ((cond) != 0, 1) +#define __GMP_UNLIKELY(cond) __builtin_expect ((cond) != 0, 0) +#else +#define __GMP_LIKELY(cond) (cond) +#define __GMP_UNLIKELY(cond) (cond) +#endif + +#ifdef _CRAY +#define __GMP_CRAY_Pragma(str) _Pragma (str) +#else +#define __GMP_CRAY_Pragma(str) +#endif + + +/* Allow direct user access to numerator and denominator of a mpq_t object. */ +#define mpq_numref(Q) (&((Q)->_mp_num)) +#define mpq_denref(Q) (&((Q)->_mp_den)) + + +#if defined (__cplusplus) +extern "C" { +using std::FILE; +#endif + +#define mp_set_memory_functions __gmp_set_memory_functions +__GMP_DECLSPEC void mp_set_memory_functions __GMP_PROTO ((void *(*) (size_t), + void *(*) (void *, size_t, size_t), + void (*) (void *, size_t))) __GMP_NOTHROW; + +#define mp_get_memory_functions __gmp_get_memory_functions +__GMP_DECLSPEC void mp_get_memory_functions __GMP_PROTO ((void *(**) (size_t), + void *(**) (void *, size_t, size_t), + void (**) (void *, size_t))) __GMP_NOTHROW; + +#define mp_bits_per_limb __gmp_bits_per_limb +__GMP_DECLSPEC extern __gmp_const int mp_bits_per_limb; + +#define gmp_errno __gmp_errno +__GMP_DECLSPEC extern int gmp_errno; + +#define gmp_version __gmp_version +__GMP_DECLSPEC extern __gmp_const char * __gmp_const gmp_version; + + +/**************** Random number routines. ****************/ + +/* obsolete */ +#define gmp_randinit __gmp_randinit +__GMP_DECLSPEC void gmp_randinit __GMP_PROTO ((gmp_randstate_t, gmp_randalg_t, ...)); + +#define gmp_randinit_default __gmp_randinit_default +__GMP_DECLSPEC void gmp_randinit_default __GMP_PROTO ((gmp_randstate_t)); + +#define gmp_randinit_lc_2exp __gmp_randinit_lc_2exp +__GMP_DECLSPEC void gmp_randinit_lc_2exp __GMP_PROTO ((gmp_randstate_t, + mpz_srcptr, unsigned long int, + unsigned long int)); + +#define gmp_randinit_lc_2exp_size __gmp_randinit_lc_2exp_size +__GMP_DECLSPEC int gmp_randinit_lc_2exp_size __GMP_PROTO ((gmp_randstate_t, unsigned long)); + +#define gmp_randinit_mt __gmp_randinit_mt +__GMP_DECLSPEC void gmp_randinit_mt __GMP_PROTO ((gmp_randstate_t)); + +#define gmp_randinit_set __gmp_randinit_set +__GMP_DECLSPEC void gmp_randinit_set __GMP_PROTO ((gmp_randstate_t, __gmp_const __gmp_randstate_struct *)); + +#define gmp_randseed __gmp_randseed +__GMP_DECLSPEC void gmp_randseed __GMP_PROTO ((gmp_randstate_t, mpz_srcptr)); + +#define gmp_randseed_ui __gmp_randseed_ui +__GMP_DECLSPEC void gmp_randseed_ui __GMP_PROTO ((gmp_randstate_t, unsigned long int)); + +#define gmp_randclear __gmp_randclear +__GMP_DECLSPEC void gmp_randclear __GMP_PROTO ((gmp_randstate_t)); + +#define gmp_urandomb_ui __gmp_urandomb_ui +__GMP_DECLSPEC unsigned long gmp_urandomb_ui __GMP_PROTO ((gmp_randstate_t, unsigned long)); + +#define gmp_urandomm_ui __gmp_urandomm_ui +__GMP_DECLSPEC unsigned long gmp_urandomm_ui __GMP_PROTO ((gmp_randstate_t, unsigned long)); + + +/**************** Formatted output routines. ****************/ + +#define gmp_asprintf __gmp_asprintf +__GMP_DECLSPEC int gmp_asprintf __GMP_PROTO ((char **, __gmp_const char *, ...)); + +#define gmp_fprintf __gmp_fprintf +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC int gmp_fprintf __GMP_PROTO ((FILE *, __gmp_const char *, ...)); +#endif + +#define gmp_obstack_printf __gmp_obstack_printf +#if defined (_GMP_H_HAVE_OBSTACK) +__GMP_DECLSPEC int gmp_obstack_printf __GMP_PROTO ((struct obstack *, __gmp_const char *, ...)); +#endif + +#define gmp_obstack_vprintf __gmp_obstack_vprintf +#if defined (_GMP_H_HAVE_OBSTACK) && defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_obstack_vprintf __GMP_PROTO ((struct obstack *, __gmp_const char *, va_list)); +#endif + +#define gmp_printf __gmp_printf +__GMP_DECLSPEC int gmp_printf __GMP_PROTO ((__gmp_const char *, ...)); + +#define gmp_snprintf __gmp_snprintf +__GMP_DECLSPEC int gmp_snprintf __GMP_PROTO ((char *, size_t, __gmp_const char *, ...)); + +#define gmp_sprintf __gmp_sprintf +__GMP_DECLSPEC int gmp_sprintf __GMP_PROTO ((char *, __gmp_const char *, ...)); + +#define gmp_vasprintf __gmp_vasprintf +#if defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vasprintf __GMP_PROTO ((char **, __gmp_const char *, va_list)); +#endif + +#define gmp_vfprintf __gmp_vfprintf +#if defined (_GMP_H_HAVE_FILE) && defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vfprintf __GMP_PROTO ((FILE *, __gmp_const char *, va_list)); +#endif + +#define gmp_vprintf __gmp_vprintf +#if defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vprintf __GMP_PROTO ((__gmp_const char *, va_list)); +#endif + +#define gmp_vsnprintf __gmp_vsnprintf +#if defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vsnprintf __GMP_PROTO ((char *, size_t, __gmp_const char *, va_list)); +#endif + +#define gmp_vsprintf __gmp_vsprintf +#if defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vsprintf __GMP_PROTO ((char *, __gmp_const char *, va_list)); +#endif + + +/**************** Formatted input routines. ****************/ + +#define gmp_fscanf __gmp_fscanf +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC int gmp_fscanf __GMP_PROTO ((FILE *, __gmp_const char *, ...)); +#endif + +#define gmp_scanf __gmp_scanf +__GMP_DECLSPEC int gmp_scanf __GMP_PROTO ((__gmp_const char *, ...)); + +#define gmp_sscanf __gmp_sscanf +__GMP_DECLSPEC int gmp_sscanf __GMP_PROTO ((__gmp_const char *, __gmp_const char *, ...)); + +#define gmp_vfscanf __gmp_vfscanf +#if defined (_GMP_H_HAVE_FILE) && defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vfscanf __GMP_PROTO ((FILE *, __gmp_const char *, va_list)); +#endif + +#define gmp_vscanf __gmp_vscanf +#if defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vscanf __GMP_PROTO ((__gmp_const char *, va_list)); +#endif + +#define gmp_vsscanf __gmp_vsscanf +#if defined (_GMP_H_HAVE_VA_LIST) +__GMP_DECLSPEC int gmp_vsscanf __GMP_PROTO ((__gmp_const char *, __gmp_const char *, va_list)); +#endif + + +/**************** Integer (i.e. Z) routines. ****************/ + +#define _mpz_realloc __gmpz_realloc +#define mpz_realloc __gmpz_realloc +__GMP_DECLSPEC void *_mpz_realloc __GMP_PROTO ((mpz_ptr, mp_size_t)); + +#define mpz_abs __gmpz_abs +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_abs) +__GMP_DECLSPEC void mpz_abs __GMP_PROTO ((mpz_ptr, mpz_srcptr)); +#endif + +#define mpz_add __gmpz_add +__GMP_DECLSPEC void mpz_add __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_add_ui __gmpz_add_ui +__GMP_DECLSPEC void mpz_add_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_addmul __gmpz_addmul +__GMP_DECLSPEC void mpz_addmul __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_addmul_ui __gmpz_addmul_ui +__GMP_DECLSPEC void mpz_addmul_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_and __gmpz_and +__GMP_DECLSPEC void mpz_and __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_array_init __gmpz_array_init +__GMP_DECLSPEC void mpz_array_init __GMP_PROTO ((mpz_ptr, mp_size_t, mp_size_t)); + +#define mpz_bin_ui __gmpz_bin_ui +__GMP_DECLSPEC void mpz_bin_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_bin_uiui __gmpz_bin_uiui +__GMP_DECLSPEC void mpz_bin_uiui __GMP_PROTO ((mpz_ptr, unsigned long int, unsigned long int)); + +#define mpz_cdiv_q __gmpz_cdiv_q +__GMP_DECLSPEC void mpz_cdiv_q __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_cdiv_q_2exp __gmpz_cdiv_q_2exp +__GMP_DECLSPEC void mpz_cdiv_q_2exp __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long)); + +#define mpz_cdiv_q_ui __gmpz_cdiv_q_ui +__GMP_DECLSPEC unsigned long int mpz_cdiv_q_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_cdiv_qr __gmpz_cdiv_qr +__GMP_DECLSPEC void mpz_cdiv_qr __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_cdiv_qr_ui __gmpz_cdiv_qr_ui +__GMP_DECLSPEC unsigned long int mpz_cdiv_qr_ui __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_cdiv_r __gmpz_cdiv_r +__GMP_DECLSPEC void mpz_cdiv_r __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_cdiv_r_2exp __gmpz_cdiv_r_2exp +__GMP_DECLSPEC void mpz_cdiv_r_2exp __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long)); + +#define mpz_cdiv_r_ui __gmpz_cdiv_r_ui +__GMP_DECLSPEC unsigned long int mpz_cdiv_r_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_cdiv_ui __gmpz_cdiv_ui +__GMP_DECLSPEC unsigned long int mpz_cdiv_ui __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_ATTRIBUTE_PURE; + +#define mpz_clear __gmpz_clear +__GMP_DECLSPEC void mpz_clear __GMP_PROTO ((mpz_ptr)); + +#define mpz_clrbit __gmpz_clrbit +__GMP_DECLSPEC void mpz_clrbit __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_cmp __gmpz_cmp +__GMP_DECLSPEC int mpz_cmp __GMP_PROTO ((mpz_srcptr, mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_cmp_d __gmpz_cmp_d +__GMP_DECLSPEC int mpz_cmp_d __GMP_PROTO ((mpz_srcptr, double)) __GMP_ATTRIBUTE_PURE; + +#define _mpz_cmp_si __gmpz_cmp_si +__GMP_DECLSPEC int _mpz_cmp_si __GMP_PROTO ((mpz_srcptr, signed long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define _mpz_cmp_ui __gmpz_cmp_ui +__GMP_DECLSPEC int _mpz_cmp_ui __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_cmpabs __gmpz_cmpabs +__GMP_DECLSPEC int mpz_cmpabs __GMP_PROTO ((mpz_srcptr, mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_cmpabs_d __gmpz_cmpabs_d +__GMP_DECLSPEC int mpz_cmpabs_d __GMP_PROTO ((mpz_srcptr, double)) __GMP_ATTRIBUTE_PURE; + +#define mpz_cmpabs_ui __gmpz_cmpabs_ui +__GMP_DECLSPEC int mpz_cmpabs_ui __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_com __gmpz_com +__GMP_DECLSPEC void mpz_com __GMP_PROTO ((mpz_ptr, mpz_srcptr)); + +#define mpz_combit __gmpz_combit +__GMP_DECLSPEC void mpz_combit __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_congruent_p __gmpz_congruent_p +__GMP_DECLSPEC int mpz_congruent_p __GMP_PROTO ((mpz_srcptr, mpz_srcptr, mpz_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpz_congruent_2exp_p __gmpz_congruent_2exp_p +__GMP_DECLSPEC int mpz_congruent_2exp_p __GMP_PROTO ((mpz_srcptr, mpz_srcptr, unsigned long)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_congruent_ui_p __gmpz_congruent_ui_p +__GMP_DECLSPEC int mpz_congruent_ui_p __GMP_PROTO ((mpz_srcptr, unsigned long, unsigned long)) __GMP_ATTRIBUTE_PURE; + +#define mpz_divexact __gmpz_divexact +__GMP_DECLSPEC void mpz_divexact __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_divexact_ui __gmpz_divexact_ui +__GMP_DECLSPEC void mpz_divexact_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long)); + +#define mpz_divisible_p __gmpz_divisible_p +__GMP_DECLSPEC int mpz_divisible_p __GMP_PROTO ((mpz_srcptr, mpz_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpz_divisible_ui_p __gmpz_divisible_ui_p +__GMP_DECLSPEC int mpz_divisible_ui_p __GMP_PROTO ((mpz_srcptr, unsigned long)) __GMP_ATTRIBUTE_PURE; + +#define mpz_divisible_2exp_p __gmpz_divisible_2exp_p +__GMP_DECLSPEC int mpz_divisible_2exp_p __GMP_PROTO ((mpz_srcptr, unsigned long)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_dump __gmpz_dump +__GMP_DECLSPEC void mpz_dump __GMP_PROTO ((mpz_srcptr)); + +#define mpz_export __gmpz_export +__GMP_DECLSPEC void *mpz_export __GMP_PROTO ((void *, size_t *, int, size_t, int, size_t, mpz_srcptr)); + +#define mpz_fac_ui __gmpz_fac_ui +__GMP_DECLSPEC void mpz_fac_ui __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_fdiv_q __gmpz_fdiv_q +__GMP_DECLSPEC void mpz_fdiv_q __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_fdiv_q_2exp __gmpz_fdiv_q_2exp +__GMP_DECLSPEC void mpz_fdiv_q_2exp __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_fdiv_q_ui __gmpz_fdiv_q_ui +__GMP_DECLSPEC unsigned long int mpz_fdiv_q_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_fdiv_qr __gmpz_fdiv_qr +__GMP_DECLSPEC void mpz_fdiv_qr __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_fdiv_qr_ui __gmpz_fdiv_qr_ui +__GMP_DECLSPEC unsigned long int mpz_fdiv_qr_ui __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_fdiv_r __gmpz_fdiv_r +__GMP_DECLSPEC void mpz_fdiv_r __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_fdiv_r_2exp __gmpz_fdiv_r_2exp +__GMP_DECLSPEC void mpz_fdiv_r_2exp __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_fdiv_r_ui __gmpz_fdiv_r_ui +__GMP_DECLSPEC unsigned long int mpz_fdiv_r_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_fdiv_ui __gmpz_fdiv_ui +__GMP_DECLSPEC unsigned long int mpz_fdiv_ui __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_ATTRIBUTE_PURE; + +#define mpz_fib_ui __gmpz_fib_ui +__GMP_DECLSPEC void mpz_fib_ui __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_fib2_ui __gmpz_fib2_ui +__GMP_DECLSPEC void mpz_fib2_ui __GMP_PROTO ((mpz_ptr, mpz_ptr, unsigned long int)); + +#define mpz_fits_sint_p __gmpz_fits_sint_p +__GMP_DECLSPEC int mpz_fits_sint_p __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_fits_slong_p __gmpz_fits_slong_p +__GMP_DECLSPEC int mpz_fits_slong_p __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_fits_sshort_p __gmpz_fits_sshort_p +__GMP_DECLSPEC int mpz_fits_sshort_p __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_fits_uint_p __gmpz_fits_uint_p +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_fits_uint_p) +__GMP_DECLSPEC int mpz_fits_uint_p __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_fits_ulong_p __gmpz_fits_ulong_p +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_fits_ulong_p) +__GMP_DECLSPEC int mpz_fits_ulong_p __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_fits_ushort_p __gmpz_fits_ushort_p +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_fits_ushort_p) +__GMP_DECLSPEC int mpz_fits_ushort_p __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_gcd __gmpz_gcd +__GMP_DECLSPEC void mpz_gcd __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_gcd_ui __gmpz_gcd_ui +__GMP_DECLSPEC unsigned long int mpz_gcd_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_gcdext __gmpz_gcdext +__GMP_DECLSPEC void mpz_gcdext __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_get_d __gmpz_get_d +__GMP_DECLSPEC double mpz_get_d __GMP_PROTO ((mpz_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpz_get_d_2exp __gmpz_get_d_2exp +__GMP_DECLSPEC double mpz_get_d_2exp __GMP_PROTO ((signed long int *, mpz_srcptr)); + +#define mpz_get_si __gmpz_get_si +__GMP_DECLSPEC /* signed */ long int mpz_get_si __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_get_str __gmpz_get_str +__GMP_DECLSPEC char *mpz_get_str __GMP_PROTO ((char *, int, mpz_srcptr)); + +#define mpz_get_ui __gmpz_get_ui +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_get_ui) +__GMP_DECLSPEC unsigned long int mpz_get_ui __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_getlimbn __gmpz_getlimbn +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_getlimbn) +__GMP_DECLSPEC mp_limb_t mpz_getlimbn __GMP_PROTO ((mpz_srcptr, mp_size_t)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_hamdist __gmpz_hamdist +__GMP_DECLSPEC unsigned long int mpz_hamdist __GMP_PROTO ((mpz_srcptr, mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_import __gmpz_import +__GMP_DECLSPEC void mpz_import __GMP_PROTO ((mpz_ptr, size_t, int, size_t, int, size_t, __gmp_const void *)); + +#define mpz_init __gmpz_init +__GMP_DECLSPEC void mpz_init __GMP_PROTO ((mpz_ptr)); + +#define mpz_init2 __gmpz_init2 +__GMP_DECLSPEC void mpz_init2 __GMP_PROTO ((mpz_ptr, unsigned long)); + +#define mpz_init_set __gmpz_init_set +__GMP_DECLSPEC void mpz_init_set __GMP_PROTO ((mpz_ptr, mpz_srcptr)); + +#define mpz_init_set_d __gmpz_init_set_d +__GMP_DECLSPEC void mpz_init_set_d __GMP_PROTO ((mpz_ptr, double)); + +#define mpz_init_set_si __gmpz_init_set_si +__GMP_DECLSPEC void mpz_init_set_si __GMP_PROTO ((mpz_ptr, signed long int)); + +#define mpz_init_set_str __gmpz_init_set_str +__GMP_DECLSPEC int mpz_init_set_str __GMP_PROTO ((mpz_ptr, __gmp_const char *, int)); + +#define mpz_init_set_ui __gmpz_init_set_ui +__GMP_DECLSPEC void mpz_init_set_ui __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_inp_raw __gmpz_inp_raw +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpz_inp_raw __GMP_PROTO ((mpz_ptr, FILE *)); +#endif + +#define mpz_inp_str __gmpz_inp_str +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpz_inp_str __GMP_PROTO ((mpz_ptr, FILE *, int)); +#endif + +#define mpz_invert __gmpz_invert +__GMP_DECLSPEC int mpz_invert __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_ior __gmpz_ior +__GMP_DECLSPEC void mpz_ior __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_jacobi __gmpz_jacobi +__GMP_DECLSPEC int mpz_jacobi __GMP_PROTO ((mpz_srcptr, mpz_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpz_kronecker mpz_jacobi /* alias */ + +#define mpz_kronecker_si __gmpz_kronecker_si +__GMP_DECLSPEC int mpz_kronecker_si __GMP_PROTO ((mpz_srcptr, long)) __GMP_ATTRIBUTE_PURE; + +#define mpz_kronecker_ui __gmpz_kronecker_ui +__GMP_DECLSPEC int mpz_kronecker_ui __GMP_PROTO ((mpz_srcptr, unsigned long)) __GMP_ATTRIBUTE_PURE; + +#define mpz_si_kronecker __gmpz_si_kronecker +__GMP_DECLSPEC int mpz_si_kronecker __GMP_PROTO ((long, mpz_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpz_ui_kronecker __gmpz_ui_kronecker +__GMP_DECLSPEC int mpz_ui_kronecker __GMP_PROTO ((unsigned long, mpz_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpz_lcm __gmpz_lcm +__GMP_DECLSPEC void mpz_lcm __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_lcm_ui __gmpz_lcm_ui +__GMP_DECLSPEC void mpz_lcm_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long)); + +#define mpz_legendre mpz_jacobi /* alias */ + +#define mpz_lucnum_ui __gmpz_lucnum_ui +__GMP_DECLSPEC void mpz_lucnum_ui __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_lucnum2_ui __gmpz_lucnum2_ui +__GMP_DECLSPEC void mpz_lucnum2_ui __GMP_PROTO ((mpz_ptr, mpz_ptr, unsigned long int)); + +#define mpz_millerrabin __gmpz_millerrabin +__GMP_DECLSPEC int mpz_millerrabin __GMP_PROTO ((mpz_srcptr, int)) __GMP_ATTRIBUTE_PURE; + +#define mpz_mod __gmpz_mod +__GMP_DECLSPEC void mpz_mod __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_mod_ui mpz_fdiv_r_ui /* same as fdiv_r because divisor unsigned */ + +#define mpz_mul __gmpz_mul +__GMP_DECLSPEC void mpz_mul __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_mul_2exp __gmpz_mul_2exp +__GMP_DECLSPEC void mpz_mul_2exp __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_mul_si __gmpz_mul_si +__GMP_DECLSPEC void mpz_mul_si __GMP_PROTO ((mpz_ptr, mpz_srcptr, long int)); + +#define mpz_mul_ui __gmpz_mul_ui +__GMP_DECLSPEC void mpz_mul_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_neg __gmpz_neg +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_neg) +__GMP_DECLSPEC void mpz_neg __GMP_PROTO ((mpz_ptr, mpz_srcptr)); +#endif + +#define mpz_nextprime __gmpz_nextprime +__GMP_DECLSPEC void mpz_nextprime __GMP_PROTO ((mpz_ptr, mpz_srcptr)); + +#define mpz_out_raw __gmpz_out_raw +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpz_out_raw __GMP_PROTO ((FILE *, mpz_srcptr)); +#endif + +#define mpz_out_str __gmpz_out_str +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpz_out_str __GMP_PROTO ((FILE *, int, mpz_srcptr)); +#endif + +#define mpz_perfect_power_p __gmpz_perfect_power_p +__GMP_DECLSPEC int mpz_perfect_power_p __GMP_PROTO ((mpz_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpz_perfect_square_p __gmpz_perfect_square_p +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_perfect_square_p) +__GMP_DECLSPEC int mpz_perfect_square_p __GMP_PROTO ((mpz_srcptr)) __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_popcount __gmpz_popcount +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_popcount) +__GMP_DECLSPEC unsigned long int mpz_popcount __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_pow_ui __gmpz_pow_ui +__GMP_DECLSPEC void mpz_pow_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_powm __gmpz_powm +__GMP_DECLSPEC void mpz_powm __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_powm_ui __gmpz_powm_ui +__GMP_DECLSPEC void mpz_powm_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int, mpz_srcptr)); + +#define mpz_probab_prime_p __gmpz_probab_prime_p +__GMP_DECLSPEC int mpz_probab_prime_p __GMP_PROTO ((mpz_srcptr, int)) __GMP_ATTRIBUTE_PURE; + +#define mpz_random __gmpz_random +__GMP_DECLSPEC void mpz_random __GMP_PROTO ((mpz_ptr, mp_size_t)); + +#define mpz_random2 __gmpz_random2 +__GMP_DECLSPEC void mpz_random2 __GMP_PROTO ((mpz_ptr, mp_size_t)); + +#define mpz_realloc2 __gmpz_realloc2 +__GMP_DECLSPEC void mpz_realloc2 __GMP_PROTO ((mpz_ptr, unsigned long)); + +#define mpz_remove __gmpz_remove +__GMP_DECLSPEC unsigned long int mpz_remove __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_root __gmpz_root +__GMP_DECLSPEC int mpz_root __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_rootrem __gmpz_rootrem +__GMP_DECLSPEC void mpz_rootrem __GMP_PROTO ((mpz_ptr,mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_rrandomb __gmpz_rrandomb +__GMP_DECLSPEC void mpz_rrandomb __GMP_PROTO ((mpz_ptr, gmp_randstate_t, unsigned long int)); + +#define mpz_scan0 __gmpz_scan0 +__GMP_DECLSPEC unsigned long int mpz_scan0 __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_scan1 __gmpz_scan1 +__GMP_DECLSPEC unsigned long int mpz_scan1 __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_set __gmpz_set +__GMP_DECLSPEC void mpz_set __GMP_PROTO ((mpz_ptr, mpz_srcptr)); + +#define mpz_set_d __gmpz_set_d +__GMP_DECLSPEC void mpz_set_d __GMP_PROTO ((mpz_ptr, double)); + +#define mpz_set_f __gmpz_set_f +__GMP_DECLSPEC void mpz_set_f __GMP_PROTO ((mpz_ptr, mpf_srcptr)); + +#define mpz_set_q __gmpz_set_q +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_set_q) +__GMP_DECLSPEC void mpz_set_q __GMP_PROTO ((mpz_ptr, mpq_srcptr)); +#endif + +#define mpz_set_si __gmpz_set_si +__GMP_DECLSPEC void mpz_set_si __GMP_PROTO ((mpz_ptr, signed long int)); + +#define mpz_set_str __gmpz_set_str +__GMP_DECLSPEC int mpz_set_str __GMP_PROTO ((mpz_ptr, __gmp_const char *, int)); + +#define mpz_set_ui __gmpz_set_ui +__GMP_DECLSPEC void mpz_set_ui __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_setbit __gmpz_setbit +__GMP_DECLSPEC void mpz_setbit __GMP_PROTO ((mpz_ptr, unsigned long int)); + +#define mpz_size __gmpz_size +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpz_size) +__GMP_DECLSPEC size_t mpz_size __GMP_PROTO ((mpz_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpz_sizeinbase __gmpz_sizeinbase +__GMP_DECLSPEC size_t mpz_sizeinbase __GMP_PROTO ((mpz_srcptr, int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_sqrt __gmpz_sqrt +__GMP_DECLSPEC void mpz_sqrt __GMP_PROTO ((mpz_ptr, mpz_srcptr)); + +#define mpz_sqrtrem __gmpz_sqrtrem +__GMP_DECLSPEC void mpz_sqrtrem __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_srcptr)); + +#define mpz_sub __gmpz_sub +__GMP_DECLSPEC void mpz_sub __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_sub_ui __gmpz_sub_ui +__GMP_DECLSPEC void mpz_sub_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_ui_sub __gmpz_ui_sub +__GMP_DECLSPEC void mpz_ui_sub __GMP_PROTO ((mpz_ptr, unsigned long int, mpz_srcptr)); + +#define mpz_submul __gmpz_submul +__GMP_DECLSPEC void mpz_submul __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_submul_ui __gmpz_submul_ui +__GMP_DECLSPEC void mpz_submul_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_swap __gmpz_swap +__GMP_DECLSPEC void mpz_swap __GMP_PROTO ((mpz_ptr, mpz_ptr)) __GMP_NOTHROW; + +#define mpz_tdiv_ui __gmpz_tdiv_ui +__GMP_DECLSPEC unsigned long int mpz_tdiv_ui __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_ATTRIBUTE_PURE; + +#define mpz_tdiv_q __gmpz_tdiv_q +__GMP_DECLSPEC void mpz_tdiv_q __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_tdiv_q_2exp __gmpz_tdiv_q_2exp +__GMP_DECLSPEC void mpz_tdiv_q_2exp __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_tdiv_q_ui __gmpz_tdiv_q_ui +__GMP_DECLSPEC unsigned long int mpz_tdiv_q_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_tdiv_qr __gmpz_tdiv_qr +__GMP_DECLSPEC void mpz_tdiv_qr __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_tdiv_qr_ui __gmpz_tdiv_qr_ui +__GMP_DECLSPEC unsigned long int mpz_tdiv_qr_ui __GMP_PROTO ((mpz_ptr, mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_tdiv_r __gmpz_tdiv_r +__GMP_DECLSPEC void mpz_tdiv_r __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + +#define mpz_tdiv_r_2exp __gmpz_tdiv_r_2exp +__GMP_DECLSPEC void mpz_tdiv_r_2exp __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_tdiv_r_ui __gmpz_tdiv_r_ui +__GMP_DECLSPEC unsigned long int mpz_tdiv_r_ui __GMP_PROTO ((mpz_ptr, mpz_srcptr, unsigned long int)); + +#define mpz_tstbit __gmpz_tstbit +__GMP_DECLSPEC int mpz_tstbit __GMP_PROTO ((mpz_srcptr, unsigned long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpz_ui_pow_ui __gmpz_ui_pow_ui +__GMP_DECLSPEC void mpz_ui_pow_ui __GMP_PROTO ((mpz_ptr, unsigned long int, unsigned long int)); + +#define mpz_urandomb __gmpz_urandomb +__GMP_DECLSPEC void mpz_urandomb __GMP_PROTO ((mpz_ptr, gmp_randstate_t, unsigned long int)); + +#define mpz_urandomm __gmpz_urandomm +__GMP_DECLSPEC void mpz_urandomm __GMP_PROTO ((mpz_ptr, gmp_randstate_t, mpz_srcptr)); + +#define mpz_xor __gmpz_xor +#define mpz_eor __gmpz_xor +__GMP_DECLSPEC void mpz_xor __GMP_PROTO ((mpz_ptr, mpz_srcptr, mpz_srcptr)); + + +/**************** Rational (i.e. Q) routines. ****************/ + +#define mpq_abs __gmpq_abs +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpq_abs) +__GMP_DECLSPEC void mpq_abs __GMP_PROTO ((mpq_ptr, mpq_srcptr)); +#endif + +#define mpq_add __gmpq_add +__GMP_DECLSPEC void mpq_add __GMP_PROTO ((mpq_ptr, mpq_srcptr, mpq_srcptr)); + +#define mpq_canonicalize __gmpq_canonicalize +__GMP_DECLSPEC void mpq_canonicalize __GMP_PROTO ((mpq_ptr)); + +#define mpq_clear __gmpq_clear +__GMP_DECLSPEC void mpq_clear __GMP_PROTO ((mpq_ptr)); + +#define mpq_cmp __gmpq_cmp +__GMP_DECLSPEC int mpq_cmp __GMP_PROTO ((mpq_srcptr, mpq_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define _mpq_cmp_si __gmpq_cmp_si +__GMP_DECLSPEC int _mpq_cmp_si __GMP_PROTO ((mpq_srcptr, long, unsigned long)) __GMP_ATTRIBUTE_PURE; + +#define _mpq_cmp_ui __gmpq_cmp_ui +__GMP_DECLSPEC int _mpq_cmp_ui __GMP_PROTO ((mpq_srcptr, unsigned long int, unsigned long int)) __GMP_ATTRIBUTE_PURE; + +#define mpq_div __gmpq_div +__GMP_DECLSPEC void mpq_div __GMP_PROTO ((mpq_ptr, mpq_srcptr, mpq_srcptr)); + +#define mpq_div_2exp __gmpq_div_2exp +__GMP_DECLSPEC void mpq_div_2exp __GMP_PROTO ((mpq_ptr, mpq_srcptr, unsigned long)); + +#define mpq_equal __gmpq_equal +__GMP_DECLSPEC int mpq_equal __GMP_PROTO ((mpq_srcptr, mpq_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpq_get_num __gmpq_get_num +__GMP_DECLSPEC void mpq_get_num __GMP_PROTO ((mpz_ptr, mpq_srcptr)); + +#define mpq_get_den __gmpq_get_den +__GMP_DECLSPEC void mpq_get_den __GMP_PROTO ((mpz_ptr, mpq_srcptr)); + +#define mpq_get_d __gmpq_get_d +__GMP_DECLSPEC double mpq_get_d __GMP_PROTO ((mpq_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpq_get_str __gmpq_get_str +__GMP_DECLSPEC char *mpq_get_str __GMP_PROTO ((char *, int, mpq_srcptr)); + +#define mpq_init __gmpq_init +__GMP_DECLSPEC void mpq_init __GMP_PROTO ((mpq_ptr)); + +#define mpq_inp_str __gmpq_inp_str +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpq_inp_str __GMP_PROTO ((mpq_ptr, FILE *, int)); +#endif + +#define mpq_inv __gmpq_inv +__GMP_DECLSPEC void mpq_inv __GMP_PROTO ((mpq_ptr, mpq_srcptr)); + +#define mpq_mul __gmpq_mul +__GMP_DECLSPEC void mpq_mul __GMP_PROTO ((mpq_ptr, mpq_srcptr, mpq_srcptr)); + +#define mpq_mul_2exp __gmpq_mul_2exp +__GMP_DECLSPEC void mpq_mul_2exp __GMP_PROTO ((mpq_ptr, mpq_srcptr, unsigned long)); + +#define mpq_neg __gmpq_neg +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpq_neg) +__GMP_DECLSPEC void mpq_neg __GMP_PROTO ((mpq_ptr, mpq_srcptr)); +#endif + +#define mpq_out_str __gmpq_out_str +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpq_out_str __GMP_PROTO ((FILE *, int, mpq_srcptr)); +#endif + +#define mpq_set __gmpq_set +__GMP_DECLSPEC void mpq_set __GMP_PROTO ((mpq_ptr, mpq_srcptr)); + +#define mpq_set_d __gmpq_set_d +__GMP_DECLSPEC void mpq_set_d __GMP_PROTO ((mpq_ptr, double)); + +#define mpq_set_den __gmpq_set_den +__GMP_DECLSPEC void mpq_set_den __GMP_PROTO ((mpq_ptr, mpz_srcptr)); + +#define mpq_set_f __gmpq_set_f +__GMP_DECLSPEC void mpq_set_f __GMP_PROTO ((mpq_ptr, mpf_srcptr)); + +#define mpq_set_num __gmpq_set_num +__GMP_DECLSPEC void mpq_set_num __GMP_PROTO ((mpq_ptr, mpz_srcptr)); + +#define mpq_set_si __gmpq_set_si +__GMP_DECLSPEC void mpq_set_si __GMP_PROTO ((mpq_ptr, signed long int, unsigned long int)); + +#define mpq_set_str __gmpq_set_str +__GMP_DECLSPEC int mpq_set_str __GMP_PROTO ((mpq_ptr, __gmp_const char *, int)); + +#define mpq_set_ui __gmpq_set_ui +__GMP_DECLSPEC void mpq_set_ui __GMP_PROTO ((mpq_ptr, unsigned long int, unsigned long int)); + +#define mpq_set_z __gmpq_set_z +__GMP_DECLSPEC void mpq_set_z __GMP_PROTO ((mpq_ptr, mpz_srcptr)); + +#define mpq_sub __gmpq_sub +__GMP_DECLSPEC void mpq_sub __GMP_PROTO ((mpq_ptr, mpq_srcptr, mpq_srcptr)); + +#define mpq_swap __gmpq_swap +__GMP_DECLSPEC void mpq_swap __GMP_PROTO ((mpq_ptr, mpq_ptr)) __GMP_NOTHROW; + + +/**************** Float (i.e. F) routines. ****************/ + +#define mpf_abs __gmpf_abs +__GMP_DECLSPEC void mpf_abs __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_add __gmpf_add +__GMP_DECLSPEC void mpf_add __GMP_PROTO ((mpf_ptr, mpf_srcptr, mpf_srcptr)); + +#define mpf_add_ui __gmpf_add_ui +__GMP_DECLSPEC void mpf_add_ui __GMP_PROTO ((mpf_ptr, mpf_srcptr, unsigned long int)); +#define mpf_ceil __gmpf_ceil +__GMP_DECLSPEC void mpf_ceil __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_clear __gmpf_clear +__GMP_DECLSPEC void mpf_clear __GMP_PROTO ((mpf_ptr)); + +#define mpf_cmp __gmpf_cmp +__GMP_DECLSPEC int mpf_cmp __GMP_PROTO ((mpf_srcptr, mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_cmp_d __gmpf_cmp_d +__GMP_DECLSPEC int mpf_cmp_d __GMP_PROTO ((mpf_srcptr, double)) __GMP_ATTRIBUTE_PURE; + +#define mpf_cmp_si __gmpf_cmp_si +__GMP_DECLSPEC int mpf_cmp_si __GMP_PROTO ((mpf_srcptr, signed long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_cmp_ui __gmpf_cmp_ui +__GMP_DECLSPEC int mpf_cmp_ui __GMP_PROTO ((mpf_srcptr, unsigned long int)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_div __gmpf_div +__GMP_DECLSPEC void mpf_div __GMP_PROTO ((mpf_ptr, mpf_srcptr, mpf_srcptr)); + +#define mpf_div_2exp __gmpf_div_2exp +__GMP_DECLSPEC void mpf_div_2exp __GMP_PROTO ((mpf_ptr, mpf_srcptr, unsigned long int)); + +#define mpf_div_ui __gmpf_div_ui +__GMP_DECLSPEC void mpf_div_ui __GMP_PROTO ((mpf_ptr, mpf_srcptr, unsigned long int)); + +#define mpf_dump __gmpf_dump +__GMP_DECLSPEC void mpf_dump __GMP_PROTO ((mpf_srcptr)); + +#define mpf_eq __gmpf_eq +__GMP_DECLSPEC int mpf_eq __GMP_PROTO ((mpf_srcptr, mpf_srcptr, unsigned long int)) __GMP_ATTRIBUTE_PURE; + +#define mpf_fits_sint_p __gmpf_fits_sint_p +__GMP_DECLSPEC int mpf_fits_sint_p __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_fits_slong_p __gmpf_fits_slong_p +__GMP_DECLSPEC int mpf_fits_slong_p __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_fits_sshort_p __gmpf_fits_sshort_p +__GMP_DECLSPEC int mpf_fits_sshort_p __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_fits_uint_p __gmpf_fits_uint_p +__GMP_DECLSPEC int mpf_fits_uint_p __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_fits_ulong_p __gmpf_fits_ulong_p +__GMP_DECLSPEC int mpf_fits_ulong_p __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_fits_ushort_p __gmpf_fits_ushort_p +__GMP_DECLSPEC int mpf_fits_ushort_p __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_floor __gmpf_floor +__GMP_DECLSPEC void mpf_floor __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_get_d __gmpf_get_d +__GMP_DECLSPEC double mpf_get_d __GMP_PROTO ((mpf_srcptr)) __GMP_ATTRIBUTE_PURE; + +#define mpf_get_d_2exp __gmpf_get_d_2exp +__GMP_DECLSPEC double mpf_get_d_2exp __GMP_PROTO ((signed long int *, mpf_srcptr)); + +#define mpf_get_default_prec __gmpf_get_default_prec +__GMP_DECLSPEC unsigned long int mpf_get_default_prec __GMP_PROTO ((void)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_get_prec __gmpf_get_prec +__GMP_DECLSPEC unsigned long int mpf_get_prec __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_get_si __gmpf_get_si +__GMP_DECLSPEC long mpf_get_si __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_get_str __gmpf_get_str +__GMP_DECLSPEC char *mpf_get_str __GMP_PROTO ((char *, mp_exp_t *, int, size_t, mpf_srcptr)); + +#define mpf_get_ui __gmpf_get_ui +__GMP_DECLSPEC unsigned long mpf_get_ui __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_init __gmpf_init +__GMP_DECLSPEC void mpf_init __GMP_PROTO ((mpf_ptr)); + +#define mpf_init2 __gmpf_init2 +__GMP_DECLSPEC void mpf_init2 __GMP_PROTO ((mpf_ptr, unsigned long int)); + +#define mpf_init_set __gmpf_init_set +__GMP_DECLSPEC void mpf_init_set __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_init_set_d __gmpf_init_set_d +__GMP_DECLSPEC void mpf_init_set_d __GMP_PROTO ((mpf_ptr, double)); + +#define mpf_init_set_si __gmpf_init_set_si +__GMP_DECLSPEC void mpf_init_set_si __GMP_PROTO ((mpf_ptr, signed long int)); + +#define mpf_init_set_str __gmpf_init_set_str +__GMP_DECLSPEC int mpf_init_set_str __GMP_PROTO ((mpf_ptr, __gmp_const char *, int)); + +#define mpf_init_set_ui __gmpf_init_set_ui +__GMP_DECLSPEC void mpf_init_set_ui __GMP_PROTO ((mpf_ptr, unsigned long int)); + +#define mpf_inp_str __gmpf_inp_str +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpf_inp_str __GMP_PROTO ((mpf_ptr, FILE *, int)); +#endif + +#define mpf_integer_p __gmpf_integer_p +__GMP_DECLSPEC int mpf_integer_p __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_mul __gmpf_mul +__GMP_DECLSPEC void mpf_mul __GMP_PROTO ((mpf_ptr, mpf_srcptr, mpf_srcptr)); + +#define mpf_mul_2exp __gmpf_mul_2exp +__GMP_DECLSPEC void mpf_mul_2exp __GMP_PROTO ((mpf_ptr, mpf_srcptr, unsigned long int)); + +#define mpf_mul_ui __gmpf_mul_ui +__GMP_DECLSPEC void mpf_mul_ui __GMP_PROTO ((mpf_ptr, mpf_srcptr, unsigned long int)); + +#define mpf_neg __gmpf_neg +__GMP_DECLSPEC void mpf_neg __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_out_str __gmpf_out_str +#ifdef _GMP_H_HAVE_FILE +__GMP_DECLSPEC size_t mpf_out_str __GMP_PROTO ((FILE *, int, size_t, mpf_srcptr)); +#endif + +#define mpf_pow_ui __gmpf_pow_ui +__GMP_DECLSPEC void mpf_pow_ui __GMP_PROTO ((mpf_ptr, mpf_srcptr, unsigned long int)); + +#define mpf_random2 __gmpf_random2 +__GMP_DECLSPEC void mpf_random2 __GMP_PROTO ((mpf_ptr, mp_size_t, mp_exp_t)); + +#define mpf_reldiff __gmpf_reldiff +__GMP_DECLSPEC void mpf_reldiff __GMP_PROTO ((mpf_ptr, mpf_srcptr, mpf_srcptr)); + +#define mpf_set __gmpf_set +__GMP_DECLSPEC void mpf_set __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_set_d __gmpf_set_d +__GMP_DECLSPEC void mpf_set_d __GMP_PROTO ((mpf_ptr, double)); + +#define mpf_set_default_prec __gmpf_set_default_prec +__GMP_DECLSPEC void mpf_set_default_prec __GMP_PROTO ((unsigned long int)) __GMP_NOTHROW; + +#define mpf_set_prec __gmpf_set_prec +__GMP_DECLSPEC void mpf_set_prec __GMP_PROTO ((mpf_ptr, unsigned long int)); + +#define mpf_set_prec_raw __gmpf_set_prec_raw +__GMP_DECLSPEC void mpf_set_prec_raw __GMP_PROTO ((mpf_ptr, unsigned long int)) __GMP_NOTHROW; + +#define mpf_set_q __gmpf_set_q +__GMP_DECLSPEC void mpf_set_q __GMP_PROTO ((mpf_ptr, mpq_srcptr)); + +#define mpf_set_si __gmpf_set_si +__GMP_DECLSPEC void mpf_set_si __GMP_PROTO ((mpf_ptr, signed long int)); + +#define mpf_set_str __gmpf_set_str +__GMP_DECLSPEC int mpf_set_str __GMP_PROTO ((mpf_ptr, __gmp_const char *, int)); + +#define mpf_set_ui __gmpf_set_ui +__GMP_DECLSPEC void mpf_set_ui __GMP_PROTO ((mpf_ptr, unsigned long int)); + +#define mpf_set_z __gmpf_set_z +__GMP_DECLSPEC void mpf_set_z __GMP_PROTO ((mpf_ptr, mpz_srcptr)); + +#define mpf_size __gmpf_size +__GMP_DECLSPEC size_t mpf_size __GMP_PROTO ((mpf_srcptr)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpf_sqrt __gmpf_sqrt +__GMP_DECLSPEC void mpf_sqrt __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_sqrt_ui __gmpf_sqrt_ui +__GMP_DECLSPEC void mpf_sqrt_ui __GMP_PROTO ((mpf_ptr, unsigned long int)); + +#define mpf_sub __gmpf_sub +__GMP_DECLSPEC void mpf_sub __GMP_PROTO ((mpf_ptr, mpf_srcptr, mpf_srcptr)); + +#define mpf_sub_ui __gmpf_sub_ui +__GMP_DECLSPEC void mpf_sub_ui __GMP_PROTO ((mpf_ptr, mpf_srcptr, unsigned long int)); + +#define mpf_swap __gmpf_swap +__GMP_DECLSPEC void mpf_swap __GMP_PROTO ((mpf_ptr, mpf_ptr)) __GMP_NOTHROW; + +#define mpf_trunc __gmpf_trunc +__GMP_DECLSPEC void mpf_trunc __GMP_PROTO ((mpf_ptr, mpf_srcptr)); + +#define mpf_ui_div __gmpf_ui_div +__GMP_DECLSPEC void mpf_ui_div __GMP_PROTO ((mpf_ptr, unsigned long int, mpf_srcptr)); + +#define mpf_ui_sub __gmpf_ui_sub +__GMP_DECLSPEC void mpf_ui_sub __GMP_PROTO ((mpf_ptr, unsigned long int, mpf_srcptr)); + +#define mpf_urandomb __gmpf_urandomb +__GMP_DECLSPEC void mpf_urandomb __GMP_PROTO ((mpf_t, gmp_randstate_t, unsigned long int)); + + +/************ Low level positive-integer (i.e. N) routines. ************/ + +/* This is ugly, but we need to make user calls reach the prefixed function. */ + +#define mpn_add __MPN(add) +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpn_add) +__GMP_DECLSPEC mp_limb_t mpn_add __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_srcptr,mp_size_t)); +#endif + +#define mpn_add_1 __MPN(add_1) +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpn_add_1) +__GMP_DECLSPEC mp_limb_t mpn_add_1 __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_limb_t)) __GMP_NOTHROW; +#endif + +#define mpn_add_n __MPN(add_n) +__GMP_DECLSPEC mp_limb_t mpn_add_n __GMP_PROTO ((mp_ptr, mp_srcptr, mp_srcptr, mp_size_t)); + +#define mpn_addmul_1 __MPN(addmul_1) +__GMP_DECLSPEC mp_limb_t mpn_addmul_1 __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_limb_t)); + +#define mpn_bdivmod __MPN(bdivmod) +__GMP_DECLSPEC mp_limb_t mpn_bdivmod __GMP_PROTO ((mp_ptr, mp_ptr, mp_size_t, mp_srcptr, mp_size_t, unsigned long int)); + +#define mpn_cmp __MPN(cmp) +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpn_cmp) +__GMP_DECLSPEC int mpn_cmp __GMP_PROTO ((mp_srcptr, mp_srcptr, mp_size_t)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; +#endif + +#define mpn_divexact_by3(dst,src,size) \ + mpn_divexact_by3c (dst, src, size, __GMP_CAST (mp_limb_t, 0)) + +#define mpn_divexact_by3c __MPN(divexact_by3c) +__GMP_DECLSPEC mp_limb_t mpn_divexact_by3c __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_limb_t)); + +#define mpn_divmod_1(qp,np,nsize,dlimb) \ + mpn_divrem_1 (qp, __GMP_CAST (mp_size_t, 0), np, nsize, dlimb) + +#define mpn_divrem __MPN(divrem) +__GMP_DECLSPEC mp_limb_t mpn_divrem __GMP_PROTO ((mp_ptr, mp_size_t, mp_ptr, mp_size_t, mp_srcptr, mp_size_t)); + +#define mpn_divrem_1 __MPN(divrem_1) +__GMP_DECLSPEC mp_limb_t mpn_divrem_1 __GMP_PROTO ((mp_ptr, mp_size_t, mp_srcptr, mp_size_t, mp_limb_t)); + +#define mpn_divrem_2 __MPN(divrem_2) +__GMP_DECLSPEC mp_limb_t mpn_divrem_2 __GMP_PROTO ((mp_ptr, mp_size_t, mp_ptr, mp_size_t, mp_srcptr)); + +#define mpn_gcd __MPN(gcd) +__GMP_DECLSPEC mp_size_t mpn_gcd __GMP_PROTO ((mp_ptr, mp_ptr, mp_size_t, mp_ptr, mp_size_t)); + +#define mpn_gcd_1 __MPN(gcd_1) +__GMP_DECLSPEC mp_limb_t mpn_gcd_1 __GMP_PROTO ((mp_srcptr, mp_size_t, mp_limb_t)) __GMP_ATTRIBUTE_PURE; + +#define mpn_gcdext_1 __MPN(gcdext_1) +__GMP_DECLSPEC mp_limb_t mpn_gcdext_1 __GMP_PROTO ((mp_limb_signed_t *, mp_limb_signed_t *, mp_limb_t, mp_limb_t)); + +#define mpn_gcdext __MPN(gcdext) +__GMP_DECLSPEC mp_size_t mpn_gcdext __GMP_PROTO ((mp_ptr, mp_ptr, mp_size_t *, mp_ptr, mp_size_t, mp_ptr, mp_size_t)); + +#define mpn_get_str __MPN(get_str) +__GMP_DECLSPEC size_t mpn_get_str __GMP_PROTO ((unsigned char *, int, mp_ptr, mp_size_t)); + +#define mpn_hamdist __MPN(hamdist) +__GMP_DECLSPEC unsigned long int mpn_hamdist __GMP_PROTO ((mp_srcptr, mp_srcptr, mp_size_t)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpn_lshift __MPN(lshift) +__GMP_DECLSPEC mp_limb_t mpn_lshift __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, unsigned int)); + +#define mpn_mod_1 __MPN(mod_1) +__GMP_DECLSPEC mp_limb_t mpn_mod_1 __GMP_PROTO ((mp_srcptr, mp_size_t, mp_limb_t)) __GMP_ATTRIBUTE_PURE; + +#define mpn_mul __MPN(mul) +__GMP_DECLSPEC mp_limb_t mpn_mul __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t)); + +#define mpn_mul_1 __MPN(mul_1) +__GMP_DECLSPEC mp_limb_t mpn_mul_1 __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_limb_t)); + +#define mpn_mul_n __MPN(mul_n) +__GMP_DECLSPEC void mpn_mul_n __GMP_PROTO ((mp_ptr, mp_srcptr, mp_srcptr, mp_size_t)); + +#define mpn_sqr __MPN(sqr) +__GMP_DECLSPEC void mpn_sqr __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t)); + +#define mpn_neg_n __MPN(neg_n) +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpn_neg_n) +__GMP_DECLSPEC mp_limb_t mpn_neg_n __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t)); +#endif + +#define mpn_perfect_square_p __MPN(perfect_square_p) +__GMP_DECLSPEC int mpn_perfect_square_p __GMP_PROTO ((mp_srcptr, mp_size_t)) __GMP_ATTRIBUTE_PURE; + +#define mpn_popcount __MPN(popcount) +__GMP_DECLSPEC unsigned long int mpn_popcount __GMP_PROTO ((mp_srcptr, mp_size_t)) __GMP_NOTHROW __GMP_ATTRIBUTE_PURE; + +#define mpn_pow_1 __MPN(pow_1) +__GMP_DECLSPEC mp_size_t mpn_pow_1 __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_limb_t, mp_ptr)); + +/* undocumented now, but retained here for upward compatibility */ +#define mpn_preinv_mod_1 __MPN(preinv_mod_1) +__GMP_DECLSPEC mp_limb_t mpn_preinv_mod_1 __GMP_PROTO ((mp_srcptr, mp_size_t, mp_limb_t, mp_limb_t)) __GMP_ATTRIBUTE_PURE; + +#define mpn_random __MPN(random) +__GMP_DECLSPEC void mpn_random __GMP_PROTO ((mp_ptr, mp_size_t)); + +#define mpn_random2 __MPN(random2) +__GMP_DECLSPEC void mpn_random2 __GMP_PROTO ((mp_ptr, mp_size_t)); + +#define mpn_rshift __MPN(rshift) +__GMP_DECLSPEC mp_limb_t mpn_rshift __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, unsigned int)); + +#define mpn_scan0 __MPN(scan0) +__GMP_DECLSPEC unsigned long int mpn_scan0 __GMP_PROTO ((mp_srcptr, unsigned long int)) __GMP_ATTRIBUTE_PURE; + +#define mpn_scan1 __MPN(scan1) +__GMP_DECLSPEC unsigned long int mpn_scan1 __GMP_PROTO ((mp_srcptr, unsigned long int)) __GMP_ATTRIBUTE_PURE; + +#define mpn_set_str __MPN(set_str) +__GMP_DECLSPEC mp_size_t mpn_set_str __GMP_PROTO ((mp_ptr, __gmp_const unsigned char *, size_t, int)); + +#define mpn_sqrtrem __MPN(sqrtrem) +__GMP_DECLSPEC mp_size_t mpn_sqrtrem __GMP_PROTO ((mp_ptr, mp_ptr, mp_srcptr, mp_size_t)); + +#define mpn_sub __MPN(sub) +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpn_sub) +__GMP_DECLSPEC mp_limb_t mpn_sub __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_srcptr,mp_size_t)); +#endif + +#define mpn_sub_1 __MPN(sub_1) +#if __GMP_INLINE_PROTOTYPES || defined (__GMP_FORCE_mpn_sub_1) +__GMP_DECLSPEC mp_limb_t mpn_sub_1 __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_limb_t)) __GMP_NOTHROW; +#endif + +#define mpn_sub_n __MPN(sub_n) +__GMP_DECLSPEC mp_limb_t mpn_sub_n __GMP_PROTO ((mp_ptr, mp_srcptr, mp_srcptr, mp_size_t)); + +#define mpn_submul_1 __MPN(submul_1) +__GMP_DECLSPEC mp_limb_t mpn_submul_1 __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_limb_t)); + +#define mpn_tdiv_qr __MPN(tdiv_qr) +__GMP_DECLSPEC void mpn_tdiv_qr __GMP_PROTO ((mp_ptr, mp_ptr, mp_size_t, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t)); + + +/**************** mpz inlines ****************/ + +/* The following are provided as inlines where possible, but always exist as + library functions too, for binary compatibility. + + Within gmp itself this inlining generally isn't relied on, since it + doesn't get done for all compilers, whereas if something is worth + inlining then it's worth arranging always. + + There are two styles of inlining here. When the same bit of code is + wanted for the inline as for the library version, then __GMP_FORCE_foo + arranges for that code to be emitted and the __GMP_EXTERN_INLINE + directive suppressed, eg. mpz_fits_uint_p. When a different bit of code + is wanted for the inline than for the library version, then + __GMP_FORCE_foo arranges the inline to be suppressed, eg. mpz_abs. */ + +#if defined (__GMP_EXTERN_INLINE) && ! defined (__GMP_FORCE_mpz_abs) +__GMP_EXTERN_INLINE void +mpz_abs (mpz_ptr __gmp_w, mpz_srcptr __gmp_u) +{ + if (__gmp_w != __gmp_u) + mpz_set (__gmp_w, __gmp_u); + __gmp_w->_mp_size = __GMP_ABS (__gmp_w->_mp_size); +} +#endif + +#if GMP_NAIL_BITS == 0 +#define __GMPZ_FITS_UTYPE_P(z,maxval) \ + mp_size_t __gmp_n = z->_mp_size; \ + mp_ptr __gmp_p = z->_mp_d; \ + return (__gmp_n == 0 || (__gmp_n == 1 && __gmp_p[0] <= maxval)); +#else +#define __GMPZ_FITS_UTYPE_P(z,maxval) \ + mp_size_t __gmp_n = z->_mp_size; \ + mp_ptr __gmp_p = z->_mp_d; \ + return (__gmp_n == 0 || (__gmp_n == 1 && __gmp_p[0] <= maxval) \ + || (__gmp_n == 2 && __gmp_p[1] <= ((mp_limb_t) maxval >> GMP_NUMB_BITS))); +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_fits_uint_p) +#if ! defined (__GMP_FORCE_mpz_fits_uint_p) +__GMP_EXTERN_INLINE +#endif +int +mpz_fits_uint_p (mpz_srcptr __gmp_z) __GMP_NOTHROW +{ + __GMPZ_FITS_UTYPE_P (__gmp_z, __GMP_UINT_MAX); +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_fits_ulong_p) +#if ! defined (__GMP_FORCE_mpz_fits_ulong_p) +__GMP_EXTERN_INLINE +#endif +int +mpz_fits_ulong_p (mpz_srcptr __gmp_z) __GMP_NOTHROW +{ + __GMPZ_FITS_UTYPE_P (__gmp_z, __GMP_ULONG_MAX); +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_fits_ushort_p) +#if ! defined (__GMP_FORCE_mpz_fits_ushort_p) +__GMP_EXTERN_INLINE +#endif +int +mpz_fits_ushort_p (mpz_srcptr __gmp_z) __GMP_NOTHROW +{ + __GMPZ_FITS_UTYPE_P (__gmp_z, __GMP_USHRT_MAX); +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_get_ui) +#if ! defined (__GMP_FORCE_mpz_get_ui) +__GMP_EXTERN_INLINE +#endif +unsigned long +mpz_get_ui (mpz_srcptr __gmp_z) __GMP_NOTHROW +{ + mp_ptr __gmp_p = __gmp_z->_mp_d; + mp_size_t __gmp_n = __gmp_z->_mp_size; + mp_limb_t __gmp_l = __gmp_p[0]; + /* This is a "#if" rather than a plain "if" so as to avoid gcc warnings + about "<< GMP_NUMB_BITS" exceeding the type size, and to avoid Borland + C++ 6.0 warnings about condition always true for something like + "__GMP_ULONG_MAX < GMP_NUMB_MASK". */ +#if GMP_NAIL_BITS == 0 || defined (_LONG_LONG_LIMB) + /* limb==long and no nails, or limb==longlong, one limb is enough */ + return (__gmp_n != 0 ? __gmp_l : 0); +#else + /* limb==long and nails, need two limbs when available */ + __gmp_n = __GMP_ABS (__gmp_n); + if (__gmp_n <= 1) + return (__gmp_n != 0 ? __gmp_l : 0); + else + return __gmp_l + (__gmp_p[1] << GMP_NUMB_BITS); +#endif +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_getlimbn) +#if ! defined (__GMP_FORCE_mpz_getlimbn) +__GMP_EXTERN_INLINE +#endif +mp_limb_t +mpz_getlimbn (mpz_srcptr __gmp_z, mp_size_t __gmp_n) __GMP_NOTHROW +{ + mp_limb_t __gmp_result = 0; + if (__GMP_LIKELY (__gmp_n >= 0 && __gmp_n < __GMP_ABS (__gmp_z->_mp_size))) + __gmp_result = __gmp_z->_mp_d[__gmp_n]; + return __gmp_result; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) && ! defined (__GMP_FORCE_mpz_neg) +__GMP_EXTERN_INLINE void +mpz_neg (mpz_ptr __gmp_w, mpz_srcptr __gmp_u) +{ + if (__gmp_w != __gmp_u) + mpz_set (__gmp_w, __gmp_u); + __gmp_w->_mp_size = - __gmp_w->_mp_size; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_perfect_square_p) +#if ! defined (__GMP_FORCE_mpz_perfect_square_p) +__GMP_EXTERN_INLINE +#endif +int +mpz_perfect_square_p (mpz_srcptr __gmp_a) +{ + mp_size_t __gmp_asize; + int __gmp_result; + + __gmp_asize = __gmp_a->_mp_size; + __gmp_result = (__gmp_asize >= 0); /* zero is a square, negatives are not */ + if (__GMP_LIKELY (__gmp_asize > 0)) + __gmp_result = mpn_perfect_square_p (__gmp_a->_mp_d, __gmp_asize); + return __gmp_result; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_popcount) +#if ! defined (__GMP_FORCE_mpz_popcount) +__GMP_EXTERN_INLINE +#endif +unsigned long +mpz_popcount (mpz_srcptr __gmp_u) __GMP_NOTHROW +{ + mp_size_t __gmp_usize; + unsigned long __gmp_result; + + __gmp_usize = __gmp_u->_mp_size; + __gmp_result = (__gmp_usize < 0 ? __GMP_ULONG_MAX : 0); + if (__GMP_LIKELY (__gmp_usize > 0)) + __gmp_result = mpn_popcount (__gmp_u->_mp_d, __gmp_usize); + return __gmp_result; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_set_q) +#if ! defined (__GMP_FORCE_mpz_set_q) +__GMP_EXTERN_INLINE +#endif +void +mpz_set_q (mpz_ptr __gmp_w, mpq_srcptr __gmp_u) +{ + mpz_tdiv_q (__gmp_w, mpq_numref (__gmp_u), mpq_denref (__gmp_u)); +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpz_size) +#if ! defined (__GMP_FORCE_mpz_size) +__GMP_EXTERN_INLINE +#endif +size_t +mpz_size (mpz_srcptr __gmp_z) __GMP_NOTHROW +{ + return __GMP_ABS (__gmp_z->_mp_size); +} +#endif + + +/**************** mpq inlines ****************/ + +#if defined (__GMP_EXTERN_INLINE) && ! defined (__GMP_FORCE_mpq_abs) +__GMP_EXTERN_INLINE void +mpq_abs (mpq_ptr __gmp_w, mpq_srcptr __gmp_u) +{ + if (__gmp_w != __gmp_u) + mpq_set (__gmp_w, __gmp_u); + __gmp_w->_mp_num._mp_size = __GMP_ABS (__gmp_w->_mp_num._mp_size); +} +#endif + +#if defined (__GMP_EXTERN_INLINE) && ! defined (__GMP_FORCE_mpq_neg) +__GMP_EXTERN_INLINE void +mpq_neg (mpq_ptr __gmp_w, mpq_srcptr __gmp_u) +{ + if (__gmp_w != __gmp_u) + mpq_set (__gmp_w, __gmp_u); + __gmp_w->_mp_num._mp_size = - __gmp_w->_mp_num._mp_size; +} +#endif + + +/**************** mpn inlines ****************/ + +/* The comments with __GMPN_ADD_1 below apply here too. + + The test for FUNCTION returning 0 should predict well. If it's assumed + {yp,ysize} will usually have a random number of bits then the high limb + won't be full and a carry out will occur a good deal less than 50% of the + time. + + ysize==0 isn't a documented feature, but is used internally in a few + places. + + Producing cout last stops it using up a register during the main part of + the calculation, though gcc (as of 3.0) on an "if (mpn_add (...))" + doesn't seem able to move the true and false legs of the conditional up + to the two places cout is generated. */ + +#define __GMPN_AORS(cout, wp, xp, xsize, yp, ysize, FUNCTION, TEST) \ + do { \ + mp_size_t __gmp_i; \ + mp_limb_t __gmp_x; \ + \ + /* ASSERT ((ysize) >= 0); */ \ + /* ASSERT ((xsize) >= (ysize)); */ \ + /* ASSERT (MPN_SAME_OR_SEPARATE2_P (wp, xsize, xp, xsize)); */ \ + /* ASSERT (MPN_SAME_OR_SEPARATE2_P (wp, xsize, yp, ysize)); */ \ + \ + __gmp_i = (ysize); \ + if (__gmp_i != 0) \ + { \ + if (FUNCTION (wp, xp, yp, __gmp_i)) \ + { \ + do \ + { \ + if (__gmp_i >= (xsize)) \ + { \ + (cout) = 1; \ + goto __gmp_done; \ + } \ + __gmp_x = (xp)[__gmp_i]; \ + } \ + while (TEST); \ + } \ + } \ + if ((wp) != (xp)) \ + __GMPN_COPY_REST (wp, xp, xsize, __gmp_i); \ + (cout) = 0; \ + __gmp_done: \ + ; \ + } while (0) + +#define __GMPN_ADD(cout, wp, xp, xsize, yp, ysize) \ + __GMPN_AORS (cout, wp, xp, xsize, yp, ysize, mpn_add_n, \ + (((wp)[__gmp_i++] = (__gmp_x + 1) & GMP_NUMB_MASK) == 0)) +#define __GMPN_SUB(cout, wp, xp, xsize, yp, ysize) \ + __GMPN_AORS (cout, wp, xp, xsize, yp, ysize, mpn_sub_n, \ + (((wp)[__gmp_i++] = (__gmp_x - 1) & GMP_NUMB_MASK), __gmp_x == 0)) + + +/* The use of __gmp_i indexing is designed to ensure a compile time src==dst + remains nice and clear to the compiler, so that __GMPN_COPY_REST can + disappear, and the load/add/store gets a chance to become a + read-modify-write on CISC CPUs. + + Alternatives: + + Using a pair of pointers instead of indexing would be possible, but gcc + isn't able to recognise compile-time src==dst in that case, even when the + pointers are incremented more or less together. Other compilers would + very likely have similar difficulty. + + gcc could use "if (__builtin_constant_p(src==dst) && src==dst)" or + similar to detect a compile-time src==dst. This works nicely on gcc + 2.95.x, it's not good on gcc 3.0 where __builtin_constant_p(p==p) seems + to be always false, for a pointer p. But the current code form seems + good enough for src==dst anyway. + + gcc on x86 as usual doesn't give particularly good flags handling for the + carry/borrow detection. It's tempting to want some multi instruction asm + blocks to help it, and this was tried, but in truth there's only a few + instructions to save and any gain is all too easily lost by register + juggling setting up for the asm. */ + +#if GMP_NAIL_BITS == 0 +#define __GMPN_AORS_1(cout, dst, src, n, v, OP, CB) \ + do { \ + mp_size_t __gmp_i; \ + mp_limb_t __gmp_x, __gmp_r; \ + \ + /* ASSERT ((n) >= 1); */ \ + /* ASSERT (MPN_SAME_OR_SEPARATE_P (dst, src, n)); */ \ + \ + __gmp_x = (src)[0]; \ + __gmp_r = __gmp_x OP (v); \ + (dst)[0] = __gmp_r; \ + if (CB (__gmp_r, __gmp_x, (v))) \ + { \ + (cout) = 1; \ + for (__gmp_i = 1; __gmp_i < (n);) \ + { \ + __gmp_x = (src)[__gmp_i]; \ + __gmp_r = __gmp_x OP 1; \ + (dst)[__gmp_i] = __gmp_r; \ + ++__gmp_i; \ + if (!CB (__gmp_r, __gmp_x, 1)) \ + { \ + if ((src) != (dst)) \ + __GMPN_COPY_REST (dst, src, n, __gmp_i); \ + (cout) = 0; \ + break; \ + } \ + } \ + } \ + else \ + { \ + if ((src) != (dst)) \ + __GMPN_COPY_REST (dst, src, n, 1); \ + (cout) = 0; \ + } \ + } while (0) +#endif + +#if GMP_NAIL_BITS >= 1 +#define __GMPN_AORS_1(cout, dst, src, n, v, OP, CB) \ + do { \ + mp_size_t __gmp_i; \ + mp_limb_t __gmp_x, __gmp_r; \ + \ + /* ASSERT ((n) >= 1); */ \ + /* ASSERT (MPN_SAME_OR_SEPARATE_P (dst, src, n)); */ \ + \ + __gmp_x = (src)[0]; \ + __gmp_r = __gmp_x OP (v); \ + (dst)[0] = __gmp_r & GMP_NUMB_MASK; \ + if (__gmp_r >> GMP_NUMB_BITS != 0) \ + { \ + (cout) = 1; \ + for (__gmp_i = 1; __gmp_i < (n);) \ + { \ + __gmp_x = (src)[__gmp_i]; \ + __gmp_r = __gmp_x OP 1; \ + (dst)[__gmp_i] = __gmp_r & GMP_NUMB_MASK; \ + ++__gmp_i; \ + if (__gmp_r >> GMP_NUMB_BITS == 0) \ + { \ + if ((src) != (dst)) \ + __GMPN_COPY_REST (dst, src, n, __gmp_i); \ + (cout) = 0; \ + break; \ + } \ + } \ + } \ + else \ + { \ + if ((src) != (dst)) \ + __GMPN_COPY_REST (dst, src, n, 1); \ + (cout) = 0; \ + } \ + } while (0) +#endif + +#define __GMPN_ADDCB(r,x,y) ((r) < (y)) +#define __GMPN_SUBCB(r,x,y) ((x) < (y)) + +#define __GMPN_ADD_1(cout, dst, src, n, v) \ + __GMPN_AORS_1(cout, dst, src, n, v, +, __GMPN_ADDCB) +#define __GMPN_SUB_1(cout, dst, src, n, v) \ + __GMPN_AORS_1(cout, dst, src, n, v, -, __GMPN_SUBCB) + + +/* Compare {xp,size} and {yp,size}, setting "result" to positive, zero or + negative. size==0 is allowed. On random data usually only one limb will + need to be examined to get a result, so it's worth having it inline. */ +#define __GMPN_CMP(result, xp, yp, size) \ + do { \ + mp_size_t __gmp_i; \ + mp_limb_t __gmp_x, __gmp_y; \ + \ + /* ASSERT ((size) >= 0); */ \ + \ + (result) = 0; \ + __gmp_i = (size); \ + while (--__gmp_i >= 0) \ + { \ + __gmp_x = (xp)[__gmp_i]; \ + __gmp_y = (yp)[__gmp_i]; \ + if (__gmp_x != __gmp_y) \ + { \ + /* Cannot use __gmp_x - __gmp_y, may overflow an "int" */ \ + (result) = (__gmp_x > __gmp_y ? 1 : -1); \ + break; \ + } \ + } \ + } while (0) + + +#if defined (__GMPN_COPY) && ! defined (__GMPN_COPY_REST) +#define __GMPN_COPY_REST(dst, src, size, start) \ + do { \ + /* ASSERT ((start) >= 0); */ \ + /* ASSERT ((start) <= (size)); */ \ + __GMPN_COPY ((dst)+(start), (src)+(start), (size)-(start)); \ + } while (0) +#endif + +/* Copy {src,size} to {dst,size}, starting at "start". This is designed to + keep the indexing dst[j] and src[j] nice and simple for __GMPN_ADD_1, + __GMPN_ADD, etc. */ +#if ! defined (__GMPN_COPY_REST) +#define __GMPN_COPY_REST(dst, src, size, start) \ + do { \ + mp_size_t __gmp_j; \ + /* ASSERT ((size) >= 0); */ \ + /* ASSERT ((start) >= 0); */ \ + /* ASSERT ((start) <= (size)); */ \ + /* ASSERT (MPN_SAME_OR_SEPARATE_P (dst, src, size)); */ \ + __GMP_CRAY_Pragma ("_CRI ivdep"); \ + for (__gmp_j = (start); __gmp_j < (size); __gmp_j++) \ + (dst)[__gmp_j] = (src)[__gmp_j]; \ + } while (0) +#endif + +/* Enhancement: Use some of the smarter code from gmp-impl.h. Maybe use + mpn_copyi if there's a native version, and if we don't mind demanding + binary compatibility for it (on targets which use it). */ + +#if ! defined (__GMPN_COPY) +#define __GMPN_COPY(dst, src, size) __GMPN_COPY_REST (dst, src, size, 0) +#endif + + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpn_add) +#if ! defined (__GMP_FORCE_mpn_add) +__GMP_EXTERN_INLINE +#endif +mp_limb_t +mpn_add (mp_ptr __gmp_wp, mp_srcptr __gmp_xp, mp_size_t __gmp_xsize, mp_srcptr __gmp_yp, mp_size_t __gmp_ysize) +{ + mp_limb_t __gmp_c; + __GMPN_ADD (__gmp_c, __gmp_wp, __gmp_xp, __gmp_xsize, __gmp_yp, __gmp_ysize); + return __gmp_c; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpn_add_1) +#if ! defined (__GMP_FORCE_mpn_add_1) +__GMP_EXTERN_INLINE +#endif +mp_limb_t +mpn_add_1 (mp_ptr __gmp_dst, mp_srcptr __gmp_src, mp_size_t __gmp_size, mp_limb_t __gmp_n) __GMP_NOTHROW +{ + mp_limb_t __gmp_c; + __GMPN_ADD_1 (__gmp_c, __gmp_dst, __gmp_src, __gmp_size, __gmp_n); + return __gmp_c; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpn_cmp) +#if ! defined (__GMP_FORCE_mpn_cmp) +__GMP_EXTERN_INLINE +#endif +int +mpn_cmp (mp_srcptr __gmp_xp, mp_srcptr __gmp_yp, mp_size_t __gmp_size) __GMP_NOTHROW +{ + int __gmp_result; + __GMPN_CMP (__gmp_result, __gmp_xp, __gmp_yp, __gmp_size); + return __gmp_result; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpn_sub) +#if ! defined (__GMP_FORCE_mpn_sub) +__GMP_EXTERN_INLINE +#endif +mp_limb_t +mpn_sub (mp_ptr __gmp_wp, mp_srcptr __gmp_xp, mp_size_t __gmp_xsize, mp_srcptr __gmp_yp, mp_size_t __gmp_ysize) +{ + mp_limb_t __gmp_c; + __GMPN_SUB (__gmp_c, __gmp_wp, __gmp_xp, __gmp_xsize, __gmp_yp, __gmp_ysize); + return __gmp_c; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpn_sub_1) +#if ! defined (__GMP_FORCE_mpn_sub_1) +__GMP_EXTERN_INLINE +#endif +mp_limb_t +mpn_sub_1 (mp_ptr __gmp_dst, mp_srcptr __gmp_src, mp_size_t __gmp_size, mp_limb_t __gmp_n) __GMP_NOTHROW +{ + mp_limb_t __gmp_c; + __GMPN_SUB_1 (__gmp_c, __gmp_dst, __gmp_src, __gmp_size, __gmp_n); + return __gmp_c; +} +#endif + +#if defined (__GMP_EXTERN_INLINE) || defined (__GMP_FORCE_mpn_neg_n) +#if ! defined (__GMP_FORCE_mpn_neg_n) +__GMP_EXTERN_INLINE +#endif +mp_limb_t +mpn_neg_n (mp_ptr __gmp_rp, mp_srcptr __gmp_up, mp_size_t __gmp_n) +{ + mp_limb_t __gmp_ul, __gmp_cy; + __gmp_cy = 0; + do { + __gmp_ul = *__gmp_up++; + *__gmp_rp++ = -__gmp_ul - __gmp_cy; + __gmp_cy |= __gmp_ul != 0; + } while (--__gmp_n != 0); + return __gmp_cy; +} +#endif + +#if defined (__cplusplus) +} +#endif + + +/* Allow faster testing for negative, zero, and positive. */ +#define mpz_sgn(Z) ((Z)->_mp_size < 0 ? -1 : (Z)->_mp_size > 0) +#define mpf_sgn(F) ((F)->_mp_size < 0 ? -1 : (F)->_mp_size > 0) +#define mpq_sgn(Q) ((Q)->_mp_num._mp_size < 0 ? -1 : (Q)->_mp_num._mp_size > 0) + +/* When using GCC, optimize certain common comparisons. */ +#if defined (__GNUC__) && __GNUC__ >= 2 +#define mpz_cmp_ui(Z,UI) \ + (__builtin_constant_p (UI) && (UI) == 0 \ + ? mpz_sgn (Z) : _mpz_cmp_ui (Z,UI)) +#define mpz_cmp_si(Z,SI) \ + (__builtin_constant_p (SI) && (SI) == 0 ? mpz_sgn (Z) \ + : __builtin_constant_p (SI) && (SI) > 0 \ + ? _mpz_cmp_ui (Z, __GMP_CAST (unsigned long int, SI)) \ + : _mpz_cmp_si (Z,SI)) +#define mpq_cmp_ui(Q,NUI,DUI) \ + (__builtin_constant_p (NUI) && (NUI) == 0 \ + ? mpq_sgn (Q) : _mpq_cmp_ui (Q,NUI,DUI)) +#define mpq_cmp_si(q,n,d) \ + (__builtin_constant_p ((n) >= 0) && (n) >= 0 \ + ? mpq_cmp_ui (q, __GMP_CAST (unsigned long, n), d) \ + : _mpq_cmp_si (q, n, d)) +#else +#define mpz_cmp_ui(Z,UI) _mpz_cmp_ui (Z,UI) +#define mpz_cmp_si(Z,UI) _mpz_cmp_si (Z,UI) +#define mpq_cmp_ui(Q,NUI,DUI) _mpq_cmp_ui (Q,NUI,DUI) +#define mpq_cmp_si(q,n,d) _mpq_cmp_si(q,n,d) +#endif + + +/* Using "&" rather than "&&" means these can come out branch-free. Every + mpz_t has at least one limb allocated, so fetching the low limb is always + allowed. */ +#define mpz_odd_p(z) (((z)->_mp_size != 0) & __GMP_CAST (int, (z)->_mp_d[0])) +#define mpz_even_p(z) (! mpz_odd_p (z)) + + +/**************** C++ routines ****************/ + +#ifdef __cplusplus +__GMP_DECLSPEC_XX std::ostream& operator<< (std::ostream &, mpz_srcptr); +__GMP_DECLSPEC_XX std::ostream& operator<< (std::ostream &, mpq_srcptr); +__GMP_DECLSPEC_XX std::ostream& operator<< (std::ostream &, mpf_srcptr); +__GMP_DECLSPEC_XX std::istream& operator>> (std::istream &, mpz_ptr); +__GMP_DECLSPEC_XX std::istream& operator>> (std::istream &, mpq_ptr); +__GMP_DECLSPEC_XX std::istream& operator>> (std::istream &, mpf_ptr); +#endif + + +/* Source-level compatibility with GMP 2 and earlier. */ +#define mpn_divmod(qp,np,nsize,dp,dsize) \ + mpn_divrem (qp, __GMP_CAST (mp_size_t, 0), np, nsize, dp, dsize) + +/* Source-level compatibility with GMP 1. */ +#define mpz_mdiv mpz_fdiv_q +#define mpz_mdivmod mpz_fdiv_qr +#define mpz_mmod mpz_fdiv_r +#define mpz_mdiv_ui mpz_fdiv_q_ui +#define mpz_mdivmod_ui(q,r,n,d) \ + (((r) == 0) ? mpz_fdiv_q_ui (q,n,d) : mpz_fdiv_qr_ui (q,r,n,d)) +#define mpz_mmod_ui(r,n,d) \ + (((r) == 0) ? mpz_fdiv_ui (n,d) : mpz_fdiv_r_ui (r,n,d)) + +/* Useful synonyms, but not quite compatible with GMP 1. */ +#define mpz_div mpz_fdiv_q +#define mpz_divmod mpz_fdiv_qr +#define mpz_div_ui mpz_fdiv_q_ui +#define mpz_divmod_ui mpz_fdiv_qr_ui +#define mpz_div_2exp mpz_fdiv_q_2exp +#define mpz_mod_2exp mpz_fdiv_r_2exp + +enum +{ + GMP_ERROR_NONE = 0, + GMP_ERROR_UNSUPPORTED_ARGUMENT = 1, + GMP_ERROR_DIVISION_BY_ZERO = 2, + GMP_ERROR_SQRT_OF_NEGATIVE = 4, + GMP_ERROR_INVALID_ARGUMENT = 8 +}; + +/* Define CC and CFLAGS which were used to build this version of GMP */ +#define __GMP_CC "gcc -std=gnu99" +#define __GMP_CFLAGS "-m32 -O2 -pedantic -fomit-frame-pointer -mtune=core2 -march=core2" + +/* Major version number is the value of __GNU_MP__ too, above and in mp.h. */ +#define __GNU_MP_VERSION 4 +#define __GNU_MP_VERSION_MINOR 3 +#define __GNU_MP_VERSION_PATCHLEVEL 2 + +#define __GMP_H__ +#endif /* __GMP_H__ */ diff --git a/libports/include/gmp/x86_32/mp_bases.h b/libports/include/gmp/x86_32/mp_bases.h new file mode 100644 index 000000000..73ddaf6d0 --- /dev/null +++ b/libports/include/gmp/x86_32/mp_bases.h @@ -0,0 +1,11 @@ +/* This file generated by gen-bases.c - DO NOT EDIT. */ + +#if GMP_NUMB_BITS != 32 +Error, error, this data is for 32 bits +#endif + +/* mp_bases[10] data, as literal values */ +#define MP_BASES_CHARS_PER_LIMB_10 9 +#define MP_BASES_BIG_BASE_10 CNST_LIMB(0x3b9aca00) +#define MP_BASES_BIG_BASE_INVERTED_10 CNST_LIMB(0x12e0be82) +#define MP_BASES_NORMALIZATION_STEPS_10 2 diff --git a/libports/include/gmp/x86_32/perfsqr.h b/libports/include/gmp/x86_32/perfsqr.h new file mode 100644 index 000000000..af9a40eac --- /dev/null +++ b/libports/include/gmp/x86_32/perfsqr.h @@ -0,0 +1,50 @@ +/* This file generated by gen-psqr.c - DO NOT EDIT. */ + +#if GMP_LIMB_BITS != 32 || GMP_NAIL_BITS != 0 +Error, error, this data is for 32 bit limb and 0 bit nail +#endif + +/* Non-zero bit indicates a quadratic residue mod 0x100. + This test identifies 82.81% as non-squares (212/256). */ +static const mp_limb_t +sq_res_0x100[8] = { + CNST_LIMB(0x2030213), + CNST_LIMB(0x2020212), + CNST_LIMB(0x2020213), + CNST_LIMB(0x2020212), + CNST_LIMB(0x2030212), + CNST_LIMB(0x2020212), + CNST_LIMB(0x2020212), + CNST_LIMB(0x2020212), +}; + +/* 2^24-1 = 3^2 * 5 * 7 * 13 * 17 ... */ +#define PERFSQR_MOD_BITS 25 + +/* This test identifies 95.66% as non-squares. */ +#define PERFSQR_MOD_TEST(up, usize) \ + do { \ + mp_limb_t r; \ + PERFSQR_MOD_34 (r, up, usize); \ + \ + /* 73.33% */ \ + PERFSQR_MOD_2 (r, CNST_LIMB(45), CNST_LIMB(0xfa4fa5), \ + CNST_LIMB(0x920), CNST_LIMB(0x1a442481)); \ + \ + /* 47.06% */ \ + PERFSQR_MOD_1 (r, CNST_LIMB(17), CNST_LIMB(0xf0f0f1), \ + CNST_LIMB(0x1a317)); \ + \ + /* 46.15% */ \ + PERFSQR_MOD_1 (r, CNST_LIMB(13), CNST_LIMB(0xec4ec5), \ + CNST_LIMB(0x9e5)); \ + \ + /* 42.86% */ \ + PERFSQR_MOD_1 (r, CNST_LIMB( 7), CNST_LIMB(0xdb6db7), \ + CNST_LIMB(0x69)); \ + } while (0) + +/* Grand total sq_res_0x100 and PERFSQR_MOD_TEST, 99.25% non-squares. */ + +/* helper for tests/mpz/t-perfsqr.c */ +#define PERFSQR_DIVISORS { 256, 45, 17, 13, 7, } diff --git a/libports/include/jpeg/jconfig.h b/libports/include/jpeg/jconfig.h new file mode 100644 index 000000000..97ca0268b --- /dev/null +++ b/libports/include/jpeg/jconfig.h @@ -0,0 +1,46 @@ +/* jconfig.h. Generated from jconfig.cfg by configure. */ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.txt for explanations */ + +#define HAVE_PROTOTYPES 1 +#define HAVE_UNSIGNED_CHAR 1 +#define HAVE_UNSIGNED_SHORT 1 +/* #undef void */ +/* #undef const */ +/* #undef CHAR_IS_UNSIGNED */ +#define HAVE_STDDEF_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_LOCALE_H 1 +/* #undef NEED_BSD_STRINGS */ +/* #undef NEED_SYS_TYPES_H */ +/* #undef NEED_FAR_POINTERS */ +/* #undef NEED_SHORT_EXTERNAL_NAMES */ +/* Define this if you get warnings about undefined structures. */ +/* #undef INCOMPLETE_TYPES_BROKEN */ + +#ifdef JPEG_INTERNALS + +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ +#define INLINE __inline__ +/* These are for configuring the JPEG memory manager. */ +/* #undef DEFAULT_MAX_MEM */ +/* #undef NO_MKTEMP */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +/* #undef RLE_SUPPORTED */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* #undef TWO_FILE_COMMANDLINE */ +/* #undef NEED_SIGNAL_CATCHER */ +/* #undef DONT_USE_B_MODE */ + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +/* #undef PROGRESS_REPORT */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/libports/include/libc-genode/mntent.h b/libports/include/libc-genode/mntent.h new file mode 100644 index 000000000..f0527dfff --- /dev/null +++ b/libports/include/libc-genode/mntent.h @@ -0,0 +1,13 @@ +#ifndef _LIBC__INCLUDE__MNTENT_H_ +#define _LIBC__INCLUDE__MNTENT_H_ + +#include + +struct statfs; +typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat; + + +struct statfs *getmntentry(const char *fromname, const char *onname, + fsid_t *fsid, dowhat what); + +#endif /* _LIBC__INCLUDE__MNTENT_H_ */ diff --git a/libports/include/libc-genode/sys/syscall.h b/libports/include/libc-genode/sys/syscall.h new file mode 100644 index 000000000..03445b17d --- /dev/null +++ b/libports/include/libc-genode/sys/syscall.h @@ -0,0 +1,4 @@ +/* + * This file is just here to prevent a compiler warning about the missing include file. + * On Genode, we do not support calling syscalls directly via a libc mechanism. + */ diff --git a/libports/include/libc-genode/timeconv.h b/libports/include/libc-genode/timeconv.h new file mode 100644 index 000000000..e69de29bb diff --git a/libports/include/libc-plugin/fd_alloc.h b/libports/include/libc-plugin/fd_alloc.h new file mode 100644 index 000000000..fcf906a63 --- /dev/null +++ b/libports/include/libc-plugin/fd_alloc.h @@ -0,0 +1,69 @@ +/* + * \brief file descriptor allocator interface + * \author Christian Prochaska + * \date 2010-01-21 + * + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LIBC_PLUGIN__FD_ALLOC_H_ +#define _LIBC_PLUGIN__FD_ALLOC_H_ + +#include + +#include + +enum { MAX_NUM_FDS = 1024 }; + +namespace Libc { + + /** + * Plugin-specific file-descriptor context + */ + class Plugin_context { }; + + + struct File_descriptor + { + int libc_fd; + Plugin *plugin; + Plugin_context *context; + }; + + + class File_descriptor_allocator : Allocator_avl_tpl + { + public: + + /** + * Constructor + */ + File_descriptor_allocator(); + + /** + * Allocate file descriptor + */ + File_descriptor *alloc(Plugin *plugin, Plugin_context *context, int libc_fd = -1); + + /** + * Release file descriptor + */ + void free(File_descriptor *fdo); + + File_descriptor *find_by_libc_fd(int libc_fd); + }; + + + /** + * Return singleton instance of file-descriptor allocator + */ + extern File_descriptor_allocator *file_descriptor_allocator(); +} + +#endif /* _LIBC_PLUGIN__FD_ALLOC_H_ */ diff --git a/libports/include/libc-plugin/plugin.h b/libports/include/libc-plugin/plugin.h new file mode 100644 index 000000000..bcf45d501 --- /dev/null +++ b/libports/include/libc-plugin/plugin.h @@ -0,0 +1,124 @@ +/* + * \brief plugin interface + * \author Christian Prochaska + * \date 2010-01-21 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LIBC_PLUGIN__PLUGIN_H_ +#define _LIBC_PLUGIN__PLUGIN_H_ + +#include + +#include +#include +#include +#include +#include /* for 'struct statfs' */ + +namespace Libc { + + using namespace Genode; + + class File_descriptor; + + class Plugin : public List::Element + { + protected: + + int _priority; + + public: + + Plugin(int priority = 0); + virtual ~Plugin(); + + virtual int priority(); + + virtual bool supports_chdir(const char *path); + virtual bool supports_mkdir(const char *path, mode_t mode); + virtual bool supports_freeaddrinfo(struct ::addrinfo *res); + virtual bool supports_getaddrinfo(const char *node, const char *service, + const struct ::addrinfo *hints, + struct ::addrinfo **res); + virtual bool supports_open(const char *pathname, int flags); + virtual bool supports_pipe(); + virtual bool supports_rename(const char *oldpath, const char *newpath); + virtual bool supports_select(int nfds, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval *timeout); + virtual bool supports_socket(int domain, int type, int protocol); + virtual bool supports_stat(const char *path); + virtual bool supports_unlink(const char *path); + + virtual File_descriptor *accept(File_descriptor *, + struct ::sockaddr *addr, + socklen_t *addrlen); + virtual int bind(File_descriptor *, + const struct ::sockaddr *addr, + socklen_t addrlen); + virtual int chdir(const char *path); + virtual int close(File_descriptor *fd); + virtual int connect(File_descriptor *, + const struct ::sockaddr *addr, + socklen_t addrlen); + virtual int dup2(File_descriptor *, File_descriptor *new_fd); + virtual int fstatfs(File_descriptor *, struct statfs *buf); + virtual int fchdir(File_descriptor *); + virtual int fcntl(File_descriptor *, int cmd, long arg); + virtual void freeaddrinfo(struct ::addrinfo *res); + virtual int fstat(File_descriptor *, struct stat *buf); + virtual int fsync(File_descriptor *); + virtual int getaddrinfo(const char *node, const char *service, + const struct ::addrinfo *hints, + struct ::addrinfo **res); + virtual ssize_t getdirentries(File_descriptor *, char *buf, + ::size_t nbytes, ::off_t *basep); + virtual int getpeername(File_descriptor *, + struct sockaddr *addr, + socklen_t *addrlen); + virtual int getsockname(File_descriptor *, + struct sockaddr *addr, + socklen_t *addrlen); + virtual int getsockopt(File_descriptor *, int level, + int optname, void *optval, + socklen_t *optlen); + virtual int ioctl(File_descriptor *, int request, char *argp); + virtual int listen(File_descriptor *, int backlog); + virtual ::off_t lseek(File_descriptor *, ::off_t offset, int whence); + virtual int mkdir(const char *pathname, mode_t mode); + virtual void *mmap(void *addr, ::size_t length, int prot, int flags, + File_descriptor *, ::off_t offset); + virtual File_descriptor *open(const char *pathname, int flags); + virtual int pipe(File_descriptor *pipefd[2]); + virtual ssize_t read(File_descriptor *, void *buf, ::size_t count); + virtual ssize_t recv(File_descriptor *, void *buf, ::size_t len, int flags); + virtual ssize_t recvfrom(File_descriptor *, void *buf, ::size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen); + virtual int rename(const char *oldpath, const char *newpath); + virtual int select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); + virtual ssize_t send(File_descriptor *, const void *buf, ::size_t len, int flags); + virtual ssize_t sendto(File_descriptor *, const void *buf, + ::size_t len, int flags, + const struct sockaddr *dest_addr, + socklen_t addrlen); + virtual int setsockopt(File_descriptor *, int level, + int optname, const void *optval, + socklen_t optlen); + virtual File_descriptor *socket(int domain, int type, int protocol); + virtual int stat(const char *path, struct stat *buf); + virtual int unlink(const char *path); + virtual ssize_t write(File_descriptor *, const void *buf, ::size_t count); + }; +} + +#endif /* _LIBC_PLUGIN__PLUGIN_H_ */ diff --git a/libports/include/libc-plugin/plugin_registry.h b/libports/include/libc-plugin/plugin_registry.h new file mode 100644 index 000000000..4e43f68f3 --- /dev/null +++ b/libports/include/libc-plugin/plugin_registry.h @@ -0,0 +1,46 @@ +/* + * \brief plugin registry interface + * \author Christian Prochaska + * \date 2010-01-21 + * + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _LIBC_PLUGIN__PLUGIN_REGISTRY_H_ +#define _LIBC_PLUGIN__PLUGIN_REGISTRY_H_ + +#include + +#include + +namespace Libc { + + class Plugin_registry : public List + { + public: + + Plugin *get_plugin_for_chdir(const char *path); + Plugin *get_plugin_for_freeaddrinfo(struct addrinfo *res); + Plugin *get_plugin_for_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); + Plugin *get_plugin_for_mkdir(const char *path, mode_t mode); + Plugin *get_plugin_for_open(const char *pathname, int flags); + Plugin *get_plugin_for_pipe(); + Plugin *get_plugin_for_rename(const char *oldpath, const char *newpath); + Plugin *get_plugin_for_socket(int domain, int type, int protocol); + Plugin *get_plugin_for_stat(const char *path, struct stat *); + Plugin *get_plugin_for_unlink(const char *path); + }; + + extern Plugin_registry *plugin_registry(); + +} + +#endif /* _LIBC_PLUGIN__PLUGIN_REGISTRY_H_ */ diff --git a/libports/include/lwip/arch/cc.h b/libports/include/lwip/arch/cc.h new file mode 100644 index 000000000..4e6b1199a --- /dev/null +++ b/libports/include/lwip/arch/cc.h @@ -0,0 +1,61 @@ +/* + * \brief Some size definitions and macros needed by LwIP. + * \author Stefan Kalkowski + * \date 2009-11-10 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __LWIP__ARCH__CC_H__ +#define __LWIP__ARCH__CC_H__ + +#include + +#include + +typedef genode_uint8_t u8_t; +typedef genode_int8_t s8_t; +typedef genode_uint16_t u16_t; +typedef genode_int16_t s16_t; +typedef genode_uint32_t u32_t; +typedef genode_int32_t s32_t; + +typedef unsigned long mem_ptr_t; + +/* Define (sn)printf formatters */ +#define U16_F "u" // we don't have hu +#define S16_F "d" // we don't have hd +#define X16_F "x" // we don't have hx +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" + +void lwip_printf(const char *format, ...); + +#define LWIP_PLATFORM_DIAG(x) do { lwip_printf x; } while(0) + +#ifdef GENODE_RELEASE +#define LWIP_PLATFORM_ASSERT(x) +#else /* GENODE_RELEASE */ +#define LWIP_PLATFORM_ASSERT(x) do { \ + lwip_printf("Assertion \"%s\" failed at line %d in %s\n", \ + x, __LINE__, __FILE__); } while(0) +#endif /* GENODE_RELEASE */ + +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_END + +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif /* BYTE_ORDER */ + +#define mem_realloc realloc + +#endif /* __LWIP__ARCH__CC_H__ */ diff --git a/libports/include/lwip/arch/perf.h b/libports/include/lwip/arch/perf.h new file mode 100644 index 000000000..869b7422c --- /dev/null +++ b/libports/include/lwip/arch/perf.h @@ -0,0 +1,20 @@ +/* + * \brief Header file with macros needed by LwIP. + * \author Stefan Kalkowski + * \date 2009-11-10 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __LWIP__ARCH__PERF_H__ +#define __LWIP__ARCH__PERF_H__ + +#define PERF_START +#define PERF_STOP(x) + +#endif /* __LWIP__ARCH__PERF_H__ */ diff --git a/libports/include/lwip/arch/sys_arch.h b/libports/include/lwip/arch/sys_arch.h new file mode 100644 index 000000000..d82e4849b --- /dev/null +++ b/libports/include/lwip/arch/sys_arch.h @@ -0,0 +1,30 @@ +/* + * \brief Platform definitions for certain types in LwIP. + * \author Stefan Kalkowski + * \date 2009-11-10 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __LWIP__ARCH__SYS_ARCH_H__ +#define __LWIP__ARCH__SYS_ARCH_H__ + +#include + +typedef mem_ptr_t sys_sem_t; +typedef mem_ptr_t sys_mbox_t; +typedef mem_ptr_t sys_thread_t; +typedef mem_ptr_t sys_prot_t; + +#define SYS_MBOX_NULL 0 +#define SYS_SEM_NULL 0 + +sys_prot_t sys_arch_protect(void); +void sys_arch_unprotect(sys_prot_t pval); + +#endif /* __LWIP__ARCH__SYS_ARCH_H__ */ diff --git a/libports/include/lwip/genode.h b/libports/include/lwip/genode.h new file mode 100644 index 000000000..637498ca0 --- /dev/null +++ b/libports/include/lwip/genode.h @@ -0,0 +1,52 @@ +/* + * \brief Genode-specific lwIP API + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2010-02-22 + * + * This header is included from C sources. + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __LWIP__GENODE_H__ +#define __LWIP__GENODE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize the TCP/IP stack */ +void lwip_tcpip_init(void); + +/** + * Initialize lwIP for NIC session + * + * \param ip_addr IPv4 address in network byte order, or zero when requesting + * DHCP + * \param netmask IPv4 network mask in network byte order + * \param gateway IPv4 network-gateway address in network byte order + * + * \return 0 on success, or 1 if DHCP failed. + * + * This function initializes Genode's nic_drv and does optionally send DHCP + * requests. + */ +int lwip_nic_init(genode_int32_t ip_addr, + genode_int32_t netmask, genode_int32_t gateway); + +/** Initialize lwIP for loopback only */ +struct netif *lwip_loopback_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP__GENODE_H__ */ diff --git a/libports/include/lwip/lwipopts.h b/libports/include/lwip/lwipopts.h new file mode 100644 index 000000000..afd8a52aa --- /dev/null +++ b/libports/include/lwip/lwipopts.h @@ -0,0 +1,77 @@ +/* + * \brief Configuration file for LwIP, adapt it to your needs. + * \author Stefan Kalkowski + * \date 2009-11-10 + */ + +/* + * Copyright (C) 2009-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __LWIP__LWIPOPTS_H__ +#define __LWIP__LWIPOPTS_H__ + +#include +#include + +#define NO_SYS 0 /* single-threaded? do not touch! */ +#define SYS_LIGHTWEIGHT_PROT 1 /* do we provide lightweight protection */ +#define LWIP_ARP 1 /* ARP support */ +#define LWIP_RAW 1 /* LwIP raw API */ +#define LWIP_UDP 1 /* UDP support */ +#define LWIP_TCP 1 /* TCP support */ +#define LWIP_DNS 1 /* DNS support */ +#define LWIP_DHCP 1 /* DHCP support */ +#define LWIP_SOCKET 1 /* LwIP socket API */ +#define LWIP_COMPAT_SOCKETS 0 /* Libc compatibility layer */ +#define LWIP_NETIF_API 1 /* Network interface API */ +#define LWIP_NETIF_LOOPBACK 1 /* Looping back to same address? */ +#define LWIP_HAVE_LOOPIF 1 /* 127.0.0.1 support ? */ + +#if LWIP_DHCP +#define LWIP_NETIF_STATUS_CALLBACK 1 /* callback function used by DHCP init */ +#endif + + +/********************* + ** Memory settings ** + *********************/ + +#define MEM_LIBC_MALLOC 1 +#define MEMP_MEM_MALLOC 1 +#define DEFAULT_ACCEPTMBOX_SIZE 128 +#define TCPIP_MBOX_SIZE 128 + +#define TCP_MSS 1460 +#define TCP_SND_BUF (2 * TCP_MSS) + +#define MEMP_NUM_SYS_TIMEOUT 8 +#define MEMP_NUM_TCP_PCB 64 +#define MEMP_NUM_NETCONN (MEMP_NUM_TCP_PCB + MEMP_NUM_UDP_PCB + MEMP_NUM_RAW_PCB + MEMP_NUM_TCP_PCB_LISTEN - 1) + +/******************** + ** Debug settings ** + ********************/ + +/* #define LWIP_DEBUG */ +/* #define DHCP_DEBUG LWIP_DBG_ON */ +/* #define ETHARP_DEBUG LWIP_DBG_ON */ +/* #define NETIF_DEBUG LWIP_DBG_ON */ +/* #define PBUF_DEBUG LWIP_DBG_ON */ +/* #define API_LIB_DEBUG LWIP_DBG_ON */ +/* #define API_MSG_DEBUG LWIP_DBG_ON */ +/* #define SOCKETS_DEBUG LWIP_DBG_ON */ +/* #define ICMP_DEBUG LWIP_DBG_ON */ +/* #define INET_DEBUG LWIP_DBG_ON */ +/* #define IP_DEBUG LWIP_DBG_ON */ +/* #define IP_REASS_DEBUG LWIP_DBG_ON */ +/* #define RAW_DEBUG LWIP_DBG_ON */ +/* #define MEM_DEBUG LWIP_DBG_ON */ +/* #define MEMP_DEBUG LWIP_DBG_ON */ +/* #define SYS_DEBUG LWIP_DBG_ON */ +/* #define TCP_DEBUG LWIP_DBG_ON */ + +#endif /* __LWIP__LWIPOPTS_H__ */ diff --git a/libports/include/ncurses/ncurses_cfg.h b/libports/include/ncurses/ncurses_cfg.h new file mode 100644 index 000000000..92f69a43b --- /dev/null +++ b/libports/include/ncurses/ncurses_cfg.h @@ -0,0 +1,201 @@ +/* include/ncurses_cfg.h. Generated automatically by configure. */ +/**************************************************************************** + * Copyright (c) 1998-2004,2005 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Thomas E. Dickey 1997 * + ****************************************************************************/ +/* + * $Id: ncurses_cfg.hin,v 1.7 2005/01/02 01:26:58 tom Exp $ + * + * This is a template-file used to generate the "ncurses_cfg.h" file. + * + * Rather than list every definition, the configuration script substitutes the + * definitions that it finds using 'sed'. You need a patch (original date + * 971222) to autoconf 2.12 or 2.13 to do this. + * + * See: + * http://invisible-island.net/autoconf/ + * ftp://invisible-island.net/autoconf/ + */ +#ifndef NC_CONFIG_H +#define NC_CONFIG_H + +#define SYSTEM_NAME "linux-gnu" +#define CC_HAS_PROTOS 1 +#if 0 +#include +#endif +#define HAVE_LONG_FILE_NAMES 1 +#define MIXEDCASE_FILENAMES 1 +#define USE_DATABASE 1 +#define TERMINFO_DIRS "/usr/share/terminfo" +#define TERMINFO "/usr/share/terminfo" +#define HAVE_BIG_CORE 1 +#define PURE_TERMINFO 1 +#define USE_HOME_TERMINFO 1 +#define USE_ROOT_ENVIRON 1 +#define HAVE_REMOVE 1 +#define HAVE_UNLINK 1 +#define HAVE_LINK 1 +#define HAVE_SYMLINK 1 +#define USE_LINKS 1 +#define HAVE_LANGINFO_CODESET 1 +#define _FILE_OFFSET_BITS 64 +#define HAVE_FSEEKO 1 +#define HAVE_CURSES_VERSION 1 +#define HAVE_HAS_KEY 1 +#define HAVE_RESIZETERM 1 +#define HAVE_RESIZE_TERM 1 +#define HAVE_TERM_ENTRY_H 1 +#define HAVE_USE_DEFAULT_COLORS 1 +#define HAVE_WRESIZE 1 +#define NCURSES_EXT_FUNCS 1 +#define NCURSES_NO_PADDING 1 +#define STDC_HEADERS 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_UNISTD_H 1 +#define SIZEOF_SIGNED_CHAR 1 +#define USE_SIGWINCH 1 +#define USE_ASSUMED_COLOR 1 +#define USE_HASHMAP 1 +#define NCURSES_WRAP_PREFIX "_nc_" +#define GCC_SCANF 1 +#define GCC_SCANFLIKE(fmt,var) __attribute__((format(scanf,fmt,var))) +#define GCC_PRINTF 1 +#define GCC_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) +#define GCC_UNUSED __attribute__((unused)) +#define GCC_NORETURN __attribute__((noreturn)) +#define NDEBUG 1 +#define HAVE_NC_ALLOC_H 1 +#define HAVE_GETTIMEOFDAY 1 +#define STDC_HEADERS 1 +#define HAVE_DIRENT_H 1 +#define TIME_WITH_SYS_TIME 1 +#define HAVE_REGEX_H_FUNCS 1 +#define HAVE_FCNTL_H 1 +#define HAVE_GETOPT_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_LOCALE_H 1 +#define HAVE_MATH_H 1 +#define HAVE_POLL_H 0 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_POLL_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMES_H 1 +#define HAVE_TTYENT_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_WCTYPE_H 1 +#define HAVE_SYS_TIME_SELECT 1 +#define SIG_ATOMIC_T volatile sig_atomic_t +#define TYPEOF_CHTYPE long +#define HAVE_GETCWD 1 +#define HAVE_GETEGID 1 +#define HAVE_GETEUID 1 +#define HAVE_GETTTYNAM 1 +#define HAVE_POLL 0 +#define HAVE_REMOVE 1 +#define HAVE_SELECT 1 +#define HAVE_SETBUF 1 +#define HAVE_SETBUFFER 1 +#define HAVE_SETVBUF 1 +#define HAVE_SIGACTION 1 +#define HAVE_SIGVEC 1 +#define HAVE_STRDUP 1 +#define HAVE_STRSTR 1 +#define HAVE_TCGETPGRP 1 +#define HAVE_TIMES 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_ISASCII 1 +#define HAVE_NANOSLEEP 1 +#define HAVE_TERMIO_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_TCGETATTR 1 +#define HAVE_VSSCANF 1 +#define HAVE_MKSTEMP 1 +#define RETSIGTYPE void +#define HAVE_SIZECHANGE 1 +#define HAVE_WORKING_POLL 0 +#define HAVE_VA_COPY 1 +#define HAVE___VA_COPY 1 +#define HAVE_UNISTD_H 1 +#define HAVE_FORK 1 +#define HAVE_VFORK 1 +#define HAVE_WORKING_VFORK 1 +#define HAVE_WORKING_FORK 1 +#define USE_OPENPTY_HEADER +#define USE_XTERM_PTY 1 +#define HAVE_IOSTREAM 1 +#define HAVE_TYPEINFO 1 +#define IOSTREAM_NAMESPACE 1 +#define ETIP_NEEDS_MATH_H 1 +#define CPP_HAS_STATIC_CAST 1 +#define HAVE_SLK_COLOR 1 +#define HAVE_PANEL_H 1 +#define HAVE_LIBPANEL 1 +#define HAVE_MENU_H 1 +#define HAVE_LIBMENU 1 +#define HAVE_FORM_H 1 +#define HAVE_LIBFORM 1 +#define NCURSES_PATHSEP ':' +#define NCURSES_VERSION_STRING "5.9.20110404" + +#include + + /* The C compiler may not treat these properly but C++ has to */ +#ifdef __cplusplus +#undef const +#undef inline +#else +#if defined(lint) || defined(TRACE) +#undef inline +#define inline /* nothing */ +#endif +#endif + + /* On HP-UX, the C compiler doesn't grok mbstate_t without + -D_XOPEN_SOURCE=500. However, this causes problems on + IRIX. So, we #define mbstate_t to int in configure.in + only for the C compiler if needed. */ +#ifndef __cplusplus +#ifdef NEED_MBSTATE_T_DEF +#define mbstate_t int +#endif +#endif + +#endif /* NC_CONFIG_H */ diff --git a/libports/include/python/osreldate.h b/libports/include/python/osreldate.h new file mode 100644 index 000000000..b84e70816 --- /dev/null +++ b/libports/include/python/osreldate.h @@ -0,0 +1,3 @@ +/* + * Note: This is just a dummy, since Python thinks we're running on FreeBSD. + */ diff --git a/libports/include/python/pyconfig.h b/libports/include/python/pyconfig.h new file mode 100644 index 000000000..32608bede --- /dev/null +++ b/libports/include/python/pyconfig.h @@ -0,0 +1,1092 @@ +/* pyconfig.h.in. Generated from configure.in by autoheader. */ + +#include + +#ifndef Py_PYCONFIG_H +#define Py_PYCONFIG_H + +/* Define for AIX if your compiler is a genuine IBM xlC/xlC_r and you want + support for AIX C++ shared extension modules. */ +#undef AIX_GENUINE_CPLUSPLUS + +/* Define this if you have AtheOS threads. */ +#undef ATHEOS_THREADS + +/* Define this if you have BeOS threads. */ +#undef BEOS_THREADS + +/* Define if you have the Mach cthreads package */ +#undef C_THREADS + +/* Define if --enable-ipv6 is specified */ +#undef ENABLE_IPV6 + +/* Define if getpgrp() must be called as getpgrp(0). */ +#undef GETPGRP_HAVE_ARG + +/* Define if gettimeofday() does not have second (timezone) argument This is + the case on Motorola V4 (R40V4.2) */ +#undef GETTIMEOFDAY_NO_TZ + +/* Define to 1 if you have the `acosh' function. */ +#undef HAVE_ACOSH + +/* struct addrinfo (netdb.h) */ +#undef HAVE_ADDRINFO + +/* Define to 1 if you have the `alarm' function. */ +#undef HAVE_ALARM + +/* Define this if your time.h defines altzone. */ +#undef HAVE_ALTZONE + +/* Define to 1 if you have the `asinh' function. */ +#undef HAVE_ASINH + +/* Define to 1 if you have the header file. */ +#undef HAVE_ASM_TYPES_H + +/* Define to 1 if you have the `atanh' function. */ +#undef HAVE_ATANH + +/* Define if GCC supports __attribute__((format(PyArg_ParseTuple, 2, 3))) */ +#undef HAVE_ATTRIBUTE_FORMAT_PARSETUPLE + +/* Define to 1 if you have the `bind_textdomain_codeset' function. */ +#undef HAVE_BIND_TEXTDOMAIN_CODESET + +/* Define to 1 if you have the header file. */ +#undef HAVE_BLUETOOTH_BLUETOOTH_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_BLUETOOTH_H + +/* Define if nice() returns success/failure instead of the new priority. */ +#undef HAVE_BROKEN_NICE + +/* Define if poll() sets errno on invalid file descriptors. */ +#undef HAVE_BROKEN_POLL + +/* Define if the Posix semaphores do not work on your system */ +#undef HAVE_BROKEN_POSIX_SEMAPHORES + +/* Define if pthread_sigmask() does not work on your system. */ +#undef HAVE_BROKEN_PTHREAD_SIGMASK + +/* Define this if you have the type _Bool. */ +#undef HAVE_C99_BOOL + +/* Define to 1 if you have the `chflags' function. */ +#undef HAVE_CHFLAGS + +/* Define to 1 if you have the `chown' function. */ +#undef HAVE_CHOWN + +/* Define if you have the 'chroot' function. */ +#undef HAVE_CHROOT + +/* Define to 1 if you have the `clock' function. */ +#undef HAVE_CLOCK + +/* Define to 1 if you have the `confstr' function. */ +#undef HAVE_CONFSTR + +/* Define to 1 if you have the header file. */ +#undef HAVE_CONIO_H + +/* Define to 1 if you have the `copysign' function. */ +#undef HAVE_COPYSIGN + +/* Define to 1 if you have the `ctermid' function. */ +#undef HAVE_CTERMID + +/* Define if you have the 'ctermid_r' function. */ +#undef HAVE_CTERMID_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_CURSES_H + +/* Define if you have the 'is_term_resized' function. */ +#undef HAVE_CURSES_IS_TERM_RESIZED + +/* Define if you have the 'resizeterm' function. */ +#undef HAVE_CURSES_RESIZETERM + +/* Define if you have the 'resize_term' function. */ +#undef HAVE_CURSES_RESIZE_TERM + +/* Define to 1 if you have the declaration of `isfinite', and to 0 if you + don't. */ +#undef HAVE_DECL_ISFINITE + +/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. + */ +#undef HAVE_DECL_ISINF + +/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. + */ +#undef HAVE_DECL_ISNAN + +/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't. + */ +#undef HAVE_DECL_TZNAME + +/* Define to 1 if you have the device macros. */ +#undef HAVE_DEVICE_MACROS + +/* Define if we have /dev/ptc. */ +#undef HAVE_DEV_PTC + +/* Define if we have /dev/ptmx. */ +#undef HAVE_DEV_PTMX + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRECT_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the `dlopen' function. */ +#define HAVE_DLOPEN 1 + +/* Define to 1 if you have the `dup2' function. */ +#undef HAVE_DUP2 + +/* Defined when any dynamic module loading is enabled. */ +#define HAVE_DYNAMIC_LOADING 1 + +/* Define if you have the 'epoll' functions. */ +#undef HAVE_EPOLL + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the `execv' function. */ +#undef HAVE_EXECV + +/* Define to 1 if you have the `expm1' function. */ +#undef HAVE_EXPM1 + +/* Define if you have the 'fchdir' function. */ +#undef HAVE_FCHDIR + +/* Define to 1 if you have the `fchmod' function. */ +#undef HAVE_FCHMOD + +/* Define to 1 if you have the `fchown' function. */ +#undef HAVE_FCHOWN + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the 'fdatasync' function. */ +#undef HAVE_FDATASYNC + +/* Define to 1 if you have the `finite' function. */ +#undef HAVE_FINITE + +/* Define if you have the 'flock' function. */ +#undef HAVE_FLOCK + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `forkpty' function. */ +#undef HAVE_FORKPTY + +/* Define to 1 if you have the `fpathconf' function. */ +#undef HAVE_FPATHCONF + +/* Define to 1 if you have the `fseek64' function. */ +#undef HAVE_FSEEK64 + +/* Define to 1 if you have the `fseeko' function. */ +#undef HAVE_FSEEKO + +/* Define to 1 if you have the `fstatvfs' function. */ +#undef HAVE_FSTATVFS + +/* Define if you have the 'fsync' function. */ +#undef HAVE_FSYNC + +/* Define to 1 if you have the `ftell64' function. */ +#undef HAVE_FTELL64 + +/* Define to 1 if you have the `ftello' function. */ +#undef HAVE_FTELLO + +/* Define to 1 if you have the `ftime' function. */ +#undef HAVE_FTIME + +/* Define to 1 if you have the `ftruncate' function. */ +#undef HAVE_FTRUNCATE + +/* Define to 1 if you have the `gai_strerror' function. */ +#undef HAVE_GAI_STRERROR + +/* Define if you have the getaddrinfo function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define this if you have flockfile(), getc_unlocked(), and funlockfile() */ +#undef HAVE_GETC_UNLOCKED + +/* Define to 1 if you have the `getgroups' function. */ +#undef HAVE_GETGROUPS + +/* Define to 1 if you have the `gethostbyname' function. */ +#undef HAVE_GETHOSTBYNAME + +/* Define this if you have some version of gethostbyname_r() */ +#undef HAVE_GETHOSTBYNAME_R + +/* Define this if you have the 3-arg version of gethostbyname_r(). */ +#undef HAVE_GETHOSTBYNAME_R_3_ARG + +/* Define this if you have the 5-arg version of gethostbyname_r(). */ +#undef HAVE_GETHOSTBYNAME_R_5_ARG + +/* Define this if you have the 6-arg version of gethostbyname_r(). */ +#undef HAVE_GETHOSTBYNAME_R_6_ARG + +/* Define to 1 if you have the `getitimer' function. */ +#undef HAVE_GETITIMER + +/* Define to 1 if you have the `getloadavg' function. */ +#undef HAVE_GETLOADAVG + +/* Define to 1 if you have the `getlogin' function. */ +#undef HAVE_GETLOGIN + +/* Define to 1 if you have the `getnameinfo' function. */ +#undef HAVE_GETNAMEINFO + +/* Define if you have the 'getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the `getpeername' function. */ +#undef HAVE_GETPEERNAME + +/* Define to 1 if you have the `getpgid' function. */ +#undef HAVE_GETPGID + +/* Define to 1 if you have the `getpgrp' function. */ +#undef HAVE_GETPGRP + +/* Define to 1 if you have the `getpid' function. */ +#undef HAVE_GETPID + +/* Define to 1 if you have the `getpriority' function. */ +#undef HAVE_GETPRIORITY + +/* Define to 1 if you have the `getpwent' function. */ +#undef HAVE_GETPWENT + +/* Define to 1 if you have the `getsid' function. */ +#undef HAVE_GETSID + +/* Define to 1 if you have the `getspent' function. */ +#undef HAVE_GETSPENT + +/* Define to 1 if you have the `getspnam' function. */ +#undef HAVE_GETSPNAM + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `getwd' function. */ +#undef HAVE_GETWD + +/* Define to 1 if you have the header file. */ +#undef HAVE_GRP_H + +/* Define if you have the 'hstrerror' function. */ +#undef HAVE_HSTRERROR + +/* Define to 1 if you have the `hypot' function. */ +#undef HAVE_HYPOT + +/* Define to 1 if you have the header file. */ +#undef HAVE_IEEEFP_H + +/* Define if you have the 'inet_aton' function. */ +#undef HAVE_INET_ATON + +/* Define if you have the 'inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_IO_H + +/* Define to 1 if you have the `kill' function. */ +#undef HAVE_KILL + +/* Define to 1 if you have the `killpg' function. */ +#undef HAVE_KILLPG + +/* Define if you have the 'kqueue' functions. */ +#undef HAVE_KQUEUE + +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* Defined to enable large file support when an off_t is bigger than a long + and long long is available and at least as big as an off_t. You may need to + add some flags for configuration and compilation to enable this mode. (For + Solaris and Linux, the necessary defines are already defined.) */ +#undef HAVE_LARGEFILE_SUPPORT + +/* Define to 1 if you have the `lchflags' function. */ +#undef HAVE_LCHFLAGS + +/* Define to 1 if you have the `lchmod' function. */ +#undef HAVE_LCHMOD + +/* Define to 1 if you have the `lchown' function. */ +#undef HAVE_LCHOWN + +/* Define to 1 if you have the `dl' library (-ldl). */ +#undef HAVE_LIBDL + +/* Define to 1 if you have the `dld' library (-ldld). */ +#undef HAVE_LIBDLD + +/* Define to 1 if you have the `ieee' library (-lieee). */ +#undef HAVE_LIBIEEE + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBINTL_H + +/* Define if you have the readline library (-lreadline). */ +#undef HAVE_LIBREADLINE + +/* Define to 1 if you have the `resolv' library (-lresolv). */ +#undef HAVE_LIBRESOLV + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBUTIL_H + +/* Define if you have the 'link' function. */ +#undef HAVE_LINK + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_NETLINK_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_TIPC_H + +/* Define to 1 if you have the `log1p' function. */ +#undef HAVE_LOG1P + +/* Define this if you have the type long double. */ +#undef HAVE_LONG_DOUBLE + +/* Define this if you have the type long long. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define this if you have the makedev macro. */ +#undef HAVE_MAKEDEV + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mkfifo' function. */ +#undef HAVE_MKFIFO + +/* Define to 1 if you have the `mknod' function. */ +#undef HAVE_MKNOD + +/* Define to 1 if you have the `mktime' function. */ +#undef HAVE_MKTIME + +/* Define to 1 if you have the `mremap' function. */ +#undef HAVE_MREMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSES_H + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETPACKET_PACKET_H + +/* Define to 1 if you have the `nice' function. */ +#undef HAVE_NICE + +/* Define to 1 if you have the `openpty' function. */ +#undef HAVE_OPENPTY + +/* Define if compiling using MacOS X 10.5 SDK or later. */ +#undef HAVE_OSX105_SDK + +/* Define to 1 if you have the `pathconf' function. */ +#undef HAVE_PATHCONF + +/* Define to 1 if you have the `pause' function. */ +#undef HAVE_PAUSE + +/* Define to 1 if you have the `plock' function. */ +#undef HAVE_PLOCK + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PROCESS_H + +/* Define if your compiler supports function prototype */ +#undef HAVE_PROTOTYPES + +/* Define if you have GNU PTH threads. */ +#undef HAVE_PTH + +/* Defined for Solaris 2.6 bug in pthread header. */ +#undef HAVE_PTHREAD_DESTRUCTOR + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTHREAD_H + +/* Define to 1 if you have the `pthread_init' function. */ +#undef HAVE_PTHREAD_INIT + +/* Define to 1 if you have the `pthread_sigmask' function. */ +#undef HAVE_PTHREAD_SIGMASK + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTY_H + +/* Define to 1 if you have the `putenv' function. */ +#undef HAVE_PUTENV + +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define if you have readline 2.1 */ +#undef HAVE_RL_CALLBACK + +/* Define if you can turn off readline's signal handling. */ +#undef HAVE_RL_CATCH_SIGNAL + +/* Define if you have readline 2.2 */ +#undef HAVE_RL_COMPLETION_APPEND_CHARACTER + +/* Define if you have readline 4.0 */ +#undef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK + +/* Define if you have readline 4.2 */ +#undef HAVE_RL_COMPLETION_MATCHES + +/* Define if you have readline 4.0 */ +#undef HAVE_RL_PRE_INPUT_HOOK + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `setegid' function. */ +#undef HAVE_SETEGID + +/* Define to 1 if you have the `seteuid' function. */ +#undef HAVE_SETEUID + +/* Define to 1 if you have the `setgid' function. */ +#undef HAVE_SETGID + +/* Define if you have the 'setgroups' function. */ +#undef HAVE_SETGROUPS + +/* Define to 1 if you have the `setitimer' function. */ +#undef HAVE_SETITIMER + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `setpgid' function. */ +#undef HAVE_SETPGID + +/* Define to 1 if you have the `setpgrp' function. */ +#undef HAVE_SETPGRP + +/* Define to 1 if you have the `setregid' function. */ +#undef HAVE_SETREGID + +/* Define to 1 if you have the `setreuid' function. */ +#undef HAVE_SETREUID + +/* Define to 1 if you have the `setsid' function. */ +#undef HAVE_SETSID + +/* Define to 1 if you have the `setuid' function. */ +#undef HAVE_SETUID + +/* Define to 1 if you have the `setvbuf' function. */ +#undef HAVE_SETVBUF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SHADOW_H + +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the `siginterrupt' function. */ +#undef HAVE_SIGINTERRUPT + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the `sigrelse' function. */ +#undef HAVE_SIGRELSE + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define if sockaddr has sa_len member */ +#undef HAVE_SOCKADDR_SA_LEN + +/* struct sockaddr_storage (sys/socket.h) */ +#undef HAVE_SOCKADDR_STORAGE + +/* Define if you have the 'socketpair' function. */ +#undef HAVE_SOCKETPAIR + +/* Define if your compiler provides ssize_t */ +#undef HAVE_SSIZE_T + +/* Define to 1 if you have the `statvfs' function. */ +#undef HAVE_STATVFS + +/* Define if you have struct stat.st_mtim.tv_nsec */ +#undef HAVE_STAT_TV_NSEC + +/* Define if you have struct stat.st_mtimensec */ +#undef HAVE_STAT_TV_NSEC2 + +/* Define if your compiler supports variable length function prototypes (e.g. + void fprintf(FILE *, char *, ...);) *and* */ +#define HAVE_STDARG_PROTOTYPES 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STROPTS_H + +/* Define to 1 if `st_birthtime' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BIRTHTIME + +/* Define to 1 if `st_blksize' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLKSIZE + +/* Define to 1 if `st_blocks' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLOCKS + +/* Define to 1 if `st_flags' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_FLAGS + +/* Define to 1 if `st_gen' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_GEN + +/* Define to 1 if `st_rdev' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_RDEV + +/* Define to 1 if `tm_zone' is member of `struct tm'. */ +#undef HAVE_STRUCT_TM_TM_ZONE + +/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#undef HAVE_ST_BLOCKS + +/* Define if you have the 'symlink' function. */ +#undef HAVE_SYMLINK + +/* Define to 1 if you have the `sysconf' function. */ +#undef HAVE_SYSCONF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSEXITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_AUDIOIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_BSDTTY_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EPOLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EVENT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_LOADAVG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_LOCK_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MKDEV_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MODEM_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STATVFS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TERMIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIMES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the `tcgetpgrp' function. */ +#undef HAVE_TCGETPGRP + +/* Define to 1 if you have the `tcsetpgrp' function. */ +#undef HAVE_TCSETPGRP + +/* Define to 1 if you have the `tempnam' function. */ +#undef HAVE_TEMPNAM + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_THREAD_H + +/* Define to 1 if you have the `timegm' function. */ +#undef HAVE_TIMEGM + +/* Define to 1 if you have the `times' function. */ +#undef HAVE_TIMES + +/* Define to 1 if you have the `tmpfile' function. */ +#undef HAVE_TMPFILE + +/* Define to 1 if you have the `tmpnam' function. */ +#undef HAVE_TMPNAM + +/* Define to 1 if you have the `tmpnam_r' function. */ +#undef HAVE_TMPNAM_R + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +#undef HAVE_TM_ZONE + +/* Define to 1 if you have the `truncate' function. */ +#undef HAVE_TRUNCATE + +/* Define to 1 if you don't have `tm_zone' but do have the external array + `tzname'. */ +#undef HAVE_TZNAME + +/* Define this if you have tcl and TCL_UTF_MAX==6 */ +#undef HAVE_UCS4_TCL + +/* Define to 1 if the system has the type `uintptr_t'. */ +#undef HAVE_UINTPTR_T + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* Define if you have a useable wchar_t type defined in wchar.h; useable means + wchar_t must be an unsigned type with at least 16 bits. (see + Include/unicodeobject.h). */ +#undef HAVE_USABLE_WCHAR_T + +/* Define to 1 if you have the `utimes' function. */ +#undef HAVE_UTIMES + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if you have the `wait3' function. */ +#undef HAVE_WAIT3 + +/* Define to 1 if you have the `wait4' function. */ +#undef HAVE_WAIT4 + +/* Define to 1 if you have the `waitpid' function. */ +#undef HAVE_WAITPID + +/* Define if the compiler provides a wchar.h header file. */ +#undef HAVE_WCHAR_H + +/* Define to 1 if you have the `wcscoll' function. */ +#undef HAVE_WCSCOLL + +/* Define if tzset() actually switches the local timezone in a meaningful way. + */ +#undef HAVE_WORKING_TZSET + +/* Define if the zlib library has inflateCopy */ +#undef HAVE_ZLIB_COPY + +/* Define to 1 if you have the `_getpty' function. */ +#undef HAVE__GETPTY + +/* Define if you are using Mach cthreads directly under /include */ +#undef HURD_C_THREADS + +/* Define if you are using Mach cthreads under mach / */ +#undef MACH_C_THREADS + +/* Define to 1 if `major', `minor', and `makedev' are declared in . + */ +#undef MAJOR_IN_MKDEV + +/* Define to 1 if `major', `minor', and `makedev' are declared in + . */ +#undef MAJOR_IN_SYSMACROS + +/* Define if mvwdelch in curses.h is an expression. */ +#undef MVWDELCH_IS_EXPRESSION + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Defined if PTHREAD_SCOPE_SYSTEM supported. */ +#undef PTHREAD_SYSTEM_SCHED_SUPPORTED + +/* Define to printf format modifier for Py_ssize_t */ +#undef PY_FORMAT_SIZE_T + +/* Define as the integral type used for Unicode representation. */ +#define PY_UNICODE_TYPE unsigned short + +/* Define if you want to build an interpreter with many run-time checks. */ +#undef Py_DEBUG + +/* Defined if Python is built as a shared library. */ +#undef Py_ENABLE_SHARED + +/* Define as the size of the unicode type. */ +#define Py_UNICODE_SIZE 2 + +/* Define if you want to have a Unicode type. */ +#define Py_USING_UNICODE 1 + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define if setpgrp() must be called as setpgrp(0, 0). */ +#undef SETPGRP_HAVE_ARG + +/* Define this to be extension of shared libraries (including the dot!). */ +#undef SHLIB_EXT + +/* Define if i>>j for signed int i does not extend the sign bit when i < 0 */ +#undef SIGNED_RIGHT_SHIFT_ZERO_FILLS + +/* The size of `double', as computed by sizeof. */ +#undef SIZEOF_DOUBLE + +/* The size of `float', as computed by sizeof. */ +#undef SIZEOF_FLOAT + +/* The size of `fpos_t', as computed by sizeof. */ +#undef SIZEOF_FPOS_T + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT GENODE_SIZEOF_INT + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG GENODE_SIZEOF_LONG + +/* The size of `long double', as computed by sizeof. */ +#undef SIZEOF_LONG_DOUBLE + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in an off_t. */ +#undef SIZEOF_OFF_T + +/* The size of `pid_t', as computed by sizeof. */ +#undef SIZEOF_PID_T + +/* The number of bytes in a pthread_t. */ +#undef SIZEOF_PTHREAD_T + +/* The size of `short', as computed by sizeof. */ +#undef SIZEOF_SHORT + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T GENODE_SIZEOF_LONG + +/* The number of bytes in a time_t. */ +#undef SIZEOF_TIME_T + +/* The size of `uintptr_t', as computed by sizeof. */ +#undef SIZEOF_UINTPTR_T + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P GENODE_SIZEOF_LONG + +/* The size of `wchar_t', as computed by sizeof. */ +#undef SIZEOF_WCHAR_T + +/* The size of `_Bool', as computed by sizeof. */ +#undef SIZEOF__BOOL + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and + (which you can't on SCO ODT 3.0). */ +#undef SYS_SELECT_WITH_SYS_TIME + +/* Define if tanh(-0.) is -0., or if platform doesn't have signed zeros */ +#undef TANH_PRESERVES_ZERO_SIGN + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Define if you want to use MacPython modules on MacOSX in unix-Python. */ +#undef USE_TOOLBOX_OBJECT_GLUE + +/* Define if a va_list is an array of some kind */ +#undef VA_LIST_IS_ARRAY + +/* Define if you want SIGFPE handled (see Include/pyfpe.h). */ +#undef WANT_SIGFPE_HANDLER + +/* Define if you want wctype.h functions to be used instead of the one + supplied by Python itself. (see Include/unicodectype.h). */ +#undef WANT_WCTYPE_FUNCTIONS + +/* Define if WINDOW in curses.h offers a field _flags. */ +#undef WINDOW_HAS_FLAGS + +/* Define if you want documentation strings in extension modules */ +#undef WITH_DOC_STRINGS + +/* Define if you want to use the new-style (Openstep, Rhapsody, MacOS) dynamic + linker (dyld) instead of the old-style (NextStep) dynamic linker (rld). + Dyld is necessary to support frameworks. */ +#undef WITH_DYLD + +/* Define to 1 if libintl is needed for locale functions. */ +#undef WITH_LIBINTL + +/* Define if you want to produce an OpenStep/Rhapsody framework (shared + library plus accessory files). */ +#undef WITH_NEXT_FRAMEWORK + +/* Define if you want to compile in Python-specific mallocs */ +#undef WITH_PYMALLOC + +/* Define if you want to compile in rudimentary thread support */ +#undef WITH_THREAD + +/* Define to profile with the Pentium timestamp counter */ +#undef WITH_TSC + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if arithmetic is subject to x87-style double rounding issue */ +#undef X87_DOUBLE_ROUNDING + +/* Define to 1 if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Define on OpenBSD to activate all library features */ +#undef _BSD_SOURCE + +/* Define on Irix to enable u_int */ +#undef _BSD_TYPES + +/* Define on Darwin to activate all library features */ +#undef _DARWIN_C_SOURCE + +/* This must be set to 64 on some systems to enable large file support. */ +#undef _FILE_OFFSET_BITS + +/* Define on Linux to activate all library features */ +#undef _GNU_SOURCE + +/* This must be defined on some systems to enable large file support. */ +#undef _LARGEFILE_SOURCE + +/* Define on NetBSD to activate all library features */ +#undef _NETBSD_SOURCE + +/* Define _OSF_SOURCE to get the makedev macro. */ +#undef _OSF_SOURCE + +/* Define to activate features from IEEE Stds 1003.1-2001 */ +#undef _POSIX_C_SOURCE + +/* Define if you have POSIX threads, and your system does not define that. */ +#undef _POSIX_THREADS + +/* Define to force use of thread-safe errno, h_errno, and other functions */ +#undef _REENTRANT + +/* Define to the level of X/Open that your system supports */ +#undef _XOPEN_SOURCE + +/* Define to activate Unix95-and-earlier features */ +#undef _XOPEN_SOURCE_EXTENDED + +/* Define on FreeBSD to activate all library features */ +#undef __BSD_VISIBLE + +/* Define to 1 if type `char' is unsigned and you are not using gcc. */ +#ifndef __CHAR_UNSIGNED__ +# undef __CHAR_UNSIGNED__ +#endif + +/* Defined on Solaris to see additional function prototypes. */ +#undef __EXTENSIONS__ + +/* Define to 'long' if doesn't define. */ +#undef clock_t + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `int' if does not define. */ +#undef mode_t + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to empty if the keyword does not work. */ +#undef signed + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if does not define. */ +#undef socklen_t + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define to empty if the keyword does not work. */ +#undef volatile + + +/* Define the macros needed if on a UnixWare 7.x system. */ +#if defined(__USLC__) && defined(__SCO_VERSION__) +#define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ +#endif + +#endif /*Py_PYCONFIG_H*/ + diff --git a/libports/include/python/x86_32/genode_defs.h b/libports/include/python/x86_32/genode_defs.h new file mode 100644 index 000000000..fc7a142d7 --- /dev/null +++ b/libports/include/python/x86_32/genode_defs.h @@ -0,0 +1,20 @@ +/* + * \brief Data type size definitions (32 Bit) + * \author Sebastian Sumpf + * \date 2010-02-17 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PYTHON__GENODE_DEFS_H_ +#define _PYTHON__GENODE_DEFS_H_ + +#define GENODE_SIZEOF_LONG 4 +#define GENODE_SIZEOF_INT 4 + +#endif /* _PYTHON__GENODE_DEFS_H_ */ diff --git a/libports/include/python/x86_64/genode_defs.h b/libports/include/python/x86_64/genode_defs.h new file mode 100644 index 000000000..4a20f25f0 --- /dev/null +++ b/libports/include/python/x86_64/genode_defs.h @@ -0,0 +1,20 @@ +/* + * \brief Data type size definitions (32 Bit) + * \author Sebastian Sumpf + * \date 2010-02-17 + */ + +/* + * Copyright (C) 2010-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PYTHON__GENODE_DEFS_H_ +#define _PYTHON__GENODE_DEFS_H_ + +#define GENODE_SIZEOF_LONG 8 +#define GENODE_SIZEOF_INT 4 + +#endif /* _PYTHON__GENODE_DEFS_H_ */ diff --git a/libports/include/readline/config.h b/libports/include/readline/config.h new file mode 100644 index 000000000..6690ea622 --- /dev/null +++ b/libports/include/readline/config.h @@ -0,0 +1,269 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Maintained by hand. */ + +/* Define NO_MULTIBYTE_SUPPORT to not compile in support for multibyte + characters, even if the OS supports them. */ +/* #undef NO_MULTIBYTE_SUPPORT */ + +/* Define if on MINIX. */ +/* #undef _MINIX */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +#define VOID_SIGHANDLER 1 + +/* Characteristics of the compiler. */ +/* #undef sig_atomic_t */ + +/* #undef size_t */ + +/* #undef ssize_t */ + +/* #undef const */ + +/* #undef volatile */ + +#define PROTOTYPES 1 + +/* #undef __CHAR_UNSIGNED__ */ + +/* Define if the `S_IS*' macros in do not work properly. */ +/* #undef STAT_MACROS_BROKEN */ + +/* Define if you have the fcntl function. */ +#define HAVE_FCNTL 1 + +/* Define if you have the getpwent function. */ +#define HAVE_GETPWENT 1 + +/* Define if you have the getpwnam function. */ +#define HAVE_GETPWNAM 1 + +/* Define if you have the getpwuid function. */ +#define HAVE_GETPWUID 1 + +/* Define if you have the isascii function. */ +#define HAVE_ISASCII 1 + +/* Define if you have the iswctype function. */ +#define HAVE_ISWCTYPE 1 + +/* Define if you have the iswlower function. */ +#define HAVE_ISWLOWER 1 + +/* Define if you have the iswupper function. */ +#define HAVE_ISWUPPER 1 + +/* Define if you have the isxdigit function. */ +#define HAVE_ISXDIGIT 1 + +/* Define if you have the kill function. */ +#define HAVE_KILL 1 + +/* Define if you have the lstat function. */ +#define HAVE_LSTAT 1 + +/* Define if you have the mbrlen function. */ +#define HAVE_MBRLEN 1 + +/* Define if you have the mbrtowc function. */ +#define HAVE_MBRTOWC 1 + +/* Define if you have the mbsrtowcs function. */ +#define HAVE_MBSRTOWCS 1 + +/* Define if you have the memmove function. */ +#define HAVE_MEMMOVE 1 + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the setenv function. */ +#define HAVE_SETENV 1 + +/* Define if you have the setlocale function. */ +#define HAVE_SETLOCALE 1 + +/* Define if you have the strcasecmp function. */ +#define HAVE_STRCASECMP 1 + +/* Define if you have the strcoll function. */ +#define HAVE_STRCOLL 1 + +/* #undef STRCOLL_BROKEN */ + +/* Define if you have the strpbrk function. */ +#define HAVE_STRPBRK 1 + +/* Define if you have the tcgetattr function. */ +#define HAVE_TCGETATTR 1 + +/* Define if you have the towlower function. */ +#define HAVE_TOWLOWER 1 + +/* Define if you have the towupper function. */ +#define HAVE_TOWUPPER 1 + +/* Define if you have the vsnprintf function. */ +#define HAVE_VSNPRINTF 1 + +/* Define if you have the wcrtomb function. */ +#define HAVE_WCRTOMB 1 + +/* Define if you have the wcscoll function. */ +#define HAVE_WCSCOLL 1 + +/* Define if you have the wctype function. */ +#define HAVE_WCTYPE 1 + +/* Define if you have the wcwidth function. */ +#define HAVE_WCWIDTH 1 + +#define STDC_HEADERS 1 + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_PTE_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_PTEM_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STREAM_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_TERMCAP_H */ + +/* Define if you have the header file. */ +#define HAVE_TERMIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_VARARGS_H */ + +/* Define if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +#define HAVE_MBSTATE_T 1 + +/* Define if you have wchar_t in . */ +#define HAVE_WCHAR_T 1 + +/* Define if you have wctype_t in . */ +#define HAVE_WCTYPE_T 1 + +/* Define if you have wint_t in . */ +#define HAVE_WINT_T 1 + +/* Define if you have and nl_langinfo(CODESET). */ +#define HAVE_LANGINFO_CODESET 1 + +/* Definitions pulled in from aclocal.m4. */ +#define VOID_SIGHANDLER 1 + +#define GWINSZ_IN_SYS_IOCTL 1 + +#define STRUCT_WINSIZE_IN_SYS_IOCTL 1 + +/* #undef STRUCT_WINSIZE_IN_TERMIOS */ + +/* #undef TIOCSTAT_IN_SYS_IOCTL */ + +#define FIONREAD_IN_SYS_IOCTL 1 + +/* #undef SPEED_T_IN_SYS_TYPES */ + +#define HAVE_GETPW_DECLS 1 + +/* #undef STRUCT_DIRENT_HAS_D_INO */ + +/* #undef STRUCT_DIRENT_HAS_D_FILENO */ + +/* #undef HAVE_BSD_SIGNALS */ + +#define HAVE_POSIX_SIGNALS 1 + +/* #undef HAVE_USG_SIGHOLD */ + +/* #undef MUST_REINSTALL_SIGHANDLERS */ + +/* #undef HAVE_POSIX_SIGSETJMP */ + +/* #undef CTYPE_NON_ASCII */ + +/* modify settings or make new ones based on what autoconf tells us. */ + +/* Ultrix botches type-ahead when switching from canonical to + non-canonical mode, at least through version 4.3 */ +#if !defined (HAVE_TERMIOS_H) || !defined (HAVE_TCGETATTR) || defined (ultrix) +# define TERMIOS_MISSING +#endif + +#if defined (__STDC__) && defined (HAVE_STDARG_H) +# define PREFER_STDARG +# define USE_VARARGS +#else +# if defined (HAVE_VARARGS_H) +# define PREFER_VARARGS +# define USE_VARARGS +# endif +#endif diff --git a/libports/lib/import/import-gmp.mk b/libports/lib/import/import-gmp.mk new file mode 100644 index 000000000..f6dbe8a03 --- /dev/null +++ b/libports/lib/import/import-gmp.mk @@ -0,0 +1,14 @@ +REP_INC_DIR += include/gmp + +ifeq ($(filter-out $(SPECS),x86),) + ifeq ($(filter-out $(SPECS),32bit),) + REP_INC_DIR += include/gmp/x86_32 + endif # 32bit + + ifeq ($(filter-out $(SPECS),64bit),) + REP_INC_DIR += include/gmp/x86_64 + endif # 32bit + +else +REQUIRES += x86 +endif # x86 diff --git a/libports/lib/import/import-jpeg.mk b/libports/lib/import/import-jpeg.mk new file mode 100644 index 000000000..93a3ca5be --- /dev/null +++ b/libports/lib/import/import-jpeg.mk @@ -0,0 +1,3 @@ +JPEG = jpeg-7 +REP_INC_DIR += contrib/$(JPEG) \ + include/jpeg diff --git a/libports/lib/import/import-libc.mk b/libports/lib/import/import-libc.mk new file mode 100644 index 000000000..142247e02 --- /dev/null +++ b/libports/lib/import/import-libc.mk @@ -0,0 +1,51 @@ +# +# Add generic libc headers to standard include search paths +# +REP_INC_DIR += include/libc + +# +# Genode-specific supplements to standard libc headers +# +REP_INC_DIR += include/libc-genode + +# +# Add platform-specific libc headers to standard include search paths +# +ifeq ($(filter-out $(SPECS),x86),) + ifeq ($(filter-out $(SPECS),32bit),) + LIBC_REP_INC_DIR = include/libc-i386 + endif # 32bit + + ifeq ($(filter-out $(SPECS),64bit),) + LIBC_REP_INC_DIR = include/libc-amd64 + endif # 32bit + LIBC_REP_INC_DIR += include/libc-x86 +endif # x86 + +ifeq ($(filter-out $(SPECS),arm),) + LIBC_REP_INC_DIR = include/libc-arm +endif # ARM + +# +# If we found no valid include path for the configured target platform, +# we have to prevent the build system from building the target. This is +# done by adding an artificial requirement. +# +ifeq ($(LIBC_REP_INC_DIR),) + REQUIRES += libc_support_for_your_target_platform +endif + +REP_INC_DIR += $(LIBC_REP_INC_DIR) + +# +# Prevent gcc headers from defining __size_t. This definition is done in +# machine/_types.h. +# +CC_OPT += -D__FreeBSD__=8 + +# +# Prevent gcc-4.4.5 from generating code for the family of 'sin' and 'cos' +# functions because the gcc-generated code would actually call 'sincos' +# or 'sincosf', which is a GNU extension, not provided by our libc. +# +CC_OPT += -fno-builtin-sin -fno-builtin-cos -fno-builtin-sinf -fno-builtin-cosf diff --git a/libports/lib/import/import-libpng.mk b/libports/lib/import/import-libpng.mk new file mode 100644 index 000000000..4e78f03e4 --- /dev/null +++ b/libports/lib/import/import-libpng.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/libpng diff --git a/libports/lib/import/import-lwip.mk b/libports/lib/import/import-lwip.mk new file mode 100644 index 000000000..90092d31c --- /dev/null +++ b/libports/lib/import/import-lwip.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/lwip diff --git a/libports/lib/import/import-mpfr.mk b/libports/lib/import/import-mpfr.mk new file mode 100644 index 000000000..398c59ade --- /dev/null +++ b/libports/lib/import/import-mpfr.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/mpfr diff --git a/libports/lib/import/import-ncurses.mk b/libports/lib/import/import-ncurses.mk new file mode 100644 index 000000000..e575780f3 --- /dev/null +++ b/libports/lib/import/import-ncurses.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/ncurses diff --git a/libports/lib/import/import-python.mk b/libports/lib/import/import-python.mk new file mode 100644 index 000000000..1f74bdc16 --- /dev/null +++ b/libports/lib/import/import-python.mk @@ -0,0 +1,13 @@ +PYTHON = python-2.6.4 +REP_INC_DIR += include/python + +ifeq ($(filter-out $(SPECS),x86),) + ifeq ($(filter-out $(SPECS),32bit),) + REP_INC_DIR += include/python/x86_32 + endif # 32bit + + ifeq ($(filter-out $(SPECS),64bit),) + REP_INC_DIR += include/python/x86_64 + endif # 64bit +endif # x86 + diff --git a/libports/lib/import/import-zlib.mk b/libports/lib/import/import-zlib.mk new file mode 100644 index 000000000..37af131b3 --- /dev/null +++ b/libports/lib/import/import-zlib.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/zlib diff --git a/libports/lib/mk/arm/libc-gen.mk b/libports/lib/mk/arm/libc-gen.mk new file mode 100644 index 000000000..91e2e2f58 --- /dev/null +++ b/libports/lib/mk/arm/libc-gen.mk @@ -0,0 +1,18 @@ +include $(REP_DIR)/lib/mk/libc-gen.inc + +LIBC_GEN_ARM_DIR = $(LIBC_DIR)/libc/arm/gen + +#FILTER_OUT_S += rfork_thread.S sigsetjmp.S setjmp.S _setjmp.S divsi3.S +FILTER_OUT_S += rfork_thread.S sigsetjmp.S divsi3.S +FILTER_OUT_C += _set_tp.c fabs.c frexp.c modf.c + +SRC_S += $(filter-out $(FILTER_OUT_S),$(notdir $(wildcard $(LIBC_GEN_ARM_DIR)/*.S))) +SRC_C += $(filter-out $(FILTER_OUT_C),$(notdir $(wildcard $(LIBC_GEN_ARM_DIR)/*.c))) + +# fix missing include prefix for 'ucontext.h', should be 'sys/ucontext.h' +CC_OPT_makecontext = -I$(REP_DIR)/include/libc/sys + +# needed to compile setjmp.S +CC_OPT += -DSOFTFLOAT + +vpath % $(LIBC_GEN_ARM_DIR) diff --git a/libports/lib/mk/arm/libm.mk b/libports/lib/mk/arm/libm.mk new file mode 100644 index 000000000..bfebe9a60 --- /dev/null +++ b/libports/lib/mk/arm/libm.mk @@ -0,0 +1,19 @@ +# +# Cannot be compiled for ARM because this file relies on the 'xbits' +# member of union 'IEEEl2bits', which is not present in the ARM version. +# +FILTER_OUT = e_acosl.c e_asinl.c e_atan2l.c e_hypotl.c \ + s_atanl.c + +# +# Cannot be compiled for ARM because "Unsupported long double format" +# +FILTER_OUT += s_cosl.c s_frexpl.c s_nextafterl.c s_nexttoward.c \ + s_rintl.c s_scalbnl.c s_sinl.c s_tanl.c + +# +# Cannot be compiled for ARM because 'split' is undeclared +# +FILTER_OUT += s_fmal.c + +include $(REP_DIR)/lib/mk/libm.mk diff --git a/libports/lib/mk/ffat.mk b/libports/lib/mk/ffat.mk new file mode 100644 index 000000000..577e61344 --- /dev/null +++ b/libports/lib/mk/ffat.mk @@ -0,0 +1,12 @@ +# +# FAT File System Module +# + +LIBS = dde_linux26_usbstorage + +INC_DIR += $(REP_DIR)/src/lib/ffat/contrib + +SRC_C = ff.c diskio.c + +vpath % $(REP_DIR)/contrib/ff007e/src +vpath % $(REP_DIR)/src/lib/ffat/ diff --git a/libports/lib/mk/ffat_block.mk b/libports/lib/mk/ffat_block.mk new file mode 100644 index 000000000..6f6d06687 --- /dev/null +++ b/libports/lib/mk/ffat_block.mk @@ -0,0 +1,13 @@ +# +# FAT File System Module using a Block session as disk I/O backend +# + +INC_DIR += $(REP_DIR)/src/lib/ffat/contrib + +SRC_C = ff.c +SRC_CC = diskio_block.cc + +LIBS = signal + +vpath % $(REP_DIR)/contrib/ff007e/src +vpath % $(REP_DIR)/src/lib/ffat/ diff --git a/libports/lib/mk/freetype.mk b/libports/lib/mk/freetype.mk new file mode 100644 index 000000000..fb7bc71bd --- /dev/null +++ b/libports/lib/mk/freetype.mk @@ -0,0 +1,51 @@ +FREETYPE = freetype-2.3.9 +FREETYPE_DIR = $(REP_DIR)/contrib/$(FREETYPE) +LIBS += libc + +# add local freetype headers to include-search path +INC_DIR += $(FREETYPE_DIR)/src/base + +# use our custom freetype config files +CC_DEF += -DFT_CONFIG_CONFIG_H="" +CC_DEF += -DFT2_BUILD_LIBRARY +CC_DEF += -DFT_CONFIG_MODULES_H="" + +# sources from freetype 'src/base/' directory +SRC_C = \ + ftbase.c ftbbox.c ftbdf.c ftbitmap.c ftcid.c ftdebug.c ftfstype.c \ + ftgasp.c ftglyph.c ftgxval.c ftinit.c ftlcdfil.c ftmm.c ftotval.c \ + ftpatent.c ftpfr.c ftstroke.c ftsynth.c fttype1.c ftwinfnt.c ftxf86.c \ + ftsystem.c + +# sources from other freetype directories +SRC_C += \ + truetype.c type1.c cff.c type1cid.c pfr.c type42.c winfnt.c pcf.c bdf.c \ + sfnt.c autofit.c pshinter.c raster.c smooth.c ftcache.c ftgzip.c ftlzw.c \ + psaux.c psmodule.c + +# dim build noise for contrib code +CC_OPT_autofit += -Wno-unused-but-set-variable +CC_OPT_cff += -Wno-unused-but-set-variable + +vpath %.c $(FREETYPE_DIR)/src/base +vpath truetype.c $(FREETYPE_DIR)/src/truetype +vpath type1.c $(FREETYPE_DIR)/src/type1 +vpath cff.c $(FREETYPE_DIR)/src/cff +vpath type1cid.c $(FREETYPE_DIR)/src/cid +vpath pfr.c $(FREETYPE_DIR)/src/pfr +vpath type42.c $(FREETYPE_DIR)/src/type42 +vpath winfnt.c $(FREETYPE_DIR)/src/winfonts +vpath pcf.c $(FREETYPE_DIR)/src/pcf +vpath bdf.c $(FREETYPE_DIR)/src/bdf +vpath sfnt.c $(FREETYPE_DIR)/src/sfnt +vpath autofit.c $(FREETYPE_DIR)/src/autofit +vpath pshinter.c $(FREETYPE_DIR)/src/pshinter +vpath raster.c $(FREETYPE_DIR)/src/raster +vpath smooth.c $(FREETYPE_DIR)/src/smooth +vpath ftcache.c $(FREETYPE_DIR)/src/cache +vpath ftgzip.c $(FREETYPE_DIR)/src/gzip +vpath ftlzw.c $(FREETYPE_DIR)/src/lzw +vpath psaux.c $(FREETYPE_DIR)/src/psaux +vpath psmodule.c $(FREETYPE_DIR)/src/psnames + +SHARED_LIB = yes diff --git a/libports/lib/mk/gallium-aux.mk b/libports/lib/mk/gallium-aux.mk new file mode 100644 index 000000000..bc5ec5c41 --- /dev/null +++ b/libports/lib/mk/gallium-aux.mk @@ -0,0 +1,52 @@ +include $(REP_DIR)/lib/mk/gallium.inc + +GALLIUM_AUX_SRC_DIR = $(GALLIUM_SRC_DIR)/auxiliary + +SUBDIRS = cso_cache draw indices os pipebuffer rbug rtasm tgsi translate util vl + +# collect all source codes in 'SUBDIRS' +SRC_C := $(foreach subdir,$(SUBDIRS),$(wildcard $(GALLIUM_AUX_SRC_DIR)/$(subdir)/*.c)) + +# strip leading directories - keep only the file names +SRC_C := $(notdir $(SRC_C)) + +# add sources normally generated in 'indices' subdirectory +SRC_C += u_indices_gen.c u_unfilled_gen.c + +# add sources normally generated in 'util' subdirectory +SRC_C += u_format_access.c u_format_table.c + +# remove non-needed files from list +SRC_C := $(filter-out u_indices.c u_unfilled_indices.c u_debug_memory.c,$(SRC_C)) + +# definition of 'log2' that is missing in FreeBSD's libc +CC_OPT_u_math = -D'log2(x)=(log(x)/log(2))' + +# dim build noise +CC_OPT_draw_vertex += -Wno-unused-but-set-variable +CC_OPT_draw_vs_varient += -Wno-enum-compare +CC_OPT_rbug_context += -Wno-unused-but-set-variable +CC_OPT_rbug_core += -Wno-unused-but-set-variable +CC_OPT_rbug_texture += -Wno-unused-but-set-variable +CC_OPT_tgsi_build += -Wno-uninitialized +CC_OPT_tgsi_build += -Wno-unused-but-set-variable +CC_OPT_u_cpu_detect += -Wno-pointer-sign +CC_OPT_u_debug_stack += -Wno-unused-but-set-variable +CC_OPT_u_format_access += -Wno-unused +CC_OPT_vl_mpeg12_mc_renderer += -Wno-enum-compare + +u_%_gen.c: $(GALLIUM_SRC_DIR)/indices/u_%_gen.py + $(MSG_CONVERT)$@ + @python $< > $@ + +# +# To generate 'u_format_pack.h' as well, so we explicitly state that +# 'u_format_access.c' depends on it. +# +u_format_access.c: u_format_pack.h + +u_format_%.c u_format_%.h: $(GALLIUM_AUX_SRC_DIR)/util/u_format_%.py + $(MSG_CONVERT)$@ + @python $< $(GALLIUM_AUX_SRC_DIR)/util/u_format.csv > $@ + +vpath %.c $(addprefix $(GALLIUM_AUX_SRC_DIR)/,$(SUBDIRS)) diff --git a/libports/lib/mk/gallium-egl.mk b/libports/lib/mk/gallium-egl.mk new file mode 100644 index 000000000..a56c6fda0 --- /dev/null +++ b/libports/lib/mk/gallium-egl.mk @@ -0,0 +1,44 @@ +include $(REP_DIR)/lib/mk/gallium.inc + +EGL_ST_SRC_DIR := $(MESA_DIR)/src/gallium/state_trackers/egl +INC_DIR += $(EGL_ST_SRC_DIR) +INC_DIR += $(MESA_DIR)/src/egl/main +INC_DIR += $(MESA_DIR)/src/gallium +CC_OPT += -DRTLD_NODELETE=0 + +# generic driver code +SRC_C := $(notdir $(wildcard $(EGL_ST_SRC_DIR)/common/*.c)) +vpath %.c $(EGL_ST_SRC_DIR)/common + +# state tracker declarations for OpenGL ES1 and ES2 +SRC_C += st_es1.c st_es2.c +vpath %.c $(MESA_DIR)/src/gallium/state_trackers/es + +# state tracker declarations for OpenGL +SRC_C += st_opengl.c +vpath st_opengl.c $(REP_DIR)/src/lib/egl + +# Genode-specific driver code +SRC_CC += driver.cc select_driver.cc +vpath driver.cc $(REP_DIR)/src/lib/egl +vpath select_driver.cc $(REP_DIR)/src/lib/egl +LIBS += blit + +# MESA state tracker code +MESA_ST_SRC_DIR := $(MESA_DIR)/src/mesa/state_tracker +INC_DIR += $(MESA_ST_SRC_DIR) +INC_DIR += $(MESA_DIR)/src/mesa/main +INC_DIR += $(MESA_DIR)/src/mesa + +SRC_C += $(notdir $(wildcard $(MESA_ST_SRC_DIR)/*.c)) +vpath %.c $(MESA_ST_SRC_DIR) + +# dim warning noise +CC_OPT_st_atom_pixeltransfer += -Wno-unused-but-set-variable +CC_OPT_st_cb_drawpixels += -Wno-unused-but-set-variable +CC_OPT_st_cb_texture += -Wno-strict-aliasing +CC_OPT_st_cb_texture += -Wno-unused-but-set-variable +CC_OPT_st_framebuffer += -Wno-strict-aliasing +CC_OPT_st_program += -Wno-unused-but-set-variable +CC_OPT_st_texture += -Wno-unused-but-set-variable + diff --git a/libports/lib/mk/gallium-failover.mk b/libports/lib/mk/gallium-failover.mk new file mode 100644 index 000000000..32b43b8a7 --- /dev/null +++ b/libports/lib/mk/gallium-failover.mk @@ -0,0 +1,5 @@ +include $(REP_DIR)/lib/mk/gallium.inc + +SRC_C := $(notdir $(wildcard $(GALLIUM_SRC_DIR)/drivers/failover/*.c)) + +vpath %.c $(GALLIUM_SRC_DIR)/drivers/failover diff --git a/libports/lib/mk/gallium-i915.mk b/libports/lib/mk/gallium-i915.mk new file mode 100644 index 000000000..a2d9728b9 --- /dev/null +++ b/libports/lib/mk/gallium-i915.mk @@ -0,0 +1,30 @@ +# +# Gallium driver for i915, loaded as needed at runtime (via 'dlopen') +# + +include $(REP_DIR)/lib/mk/gallium.inc + +# i915 driver +SRC_C := $(notdir $(wildcard $(GALLIUM_SRC_DIR)/drivers/i915/*.c)) +vpath %.c $(GALLIUM_SRC_DIR)/drivers/i915 + +# dummy stub for trace driver +SRC_C += dummy_trace.c +vpath dummy_trace.c $(REP_DIR)/src/lib/gallium +INC_DIR += $(GALLIUM_SRC_DIR)/drivers/trace + +SRC_CC += query_device_id.cc +vpath query_device_id.cc $(REP_DIR)/src/lib/gallium/i915 + +# libdrm includes +LIBDRM_DIR = $(REP_DIR)/contrib/libdrm-2.4.21 +INC_DIR += $(LIBDRM_DIR)/include/drm $(LIBDRM_DIR)/intel + +# interface to i915 drm device +SRC_C += $(notdir $(wildcard $(GALLIUM_SRC_DIR)/winsys/drm/intel/gem/*.c)) +vpath %.c $(GALLIUM_SRC_DIR)/winsys/drm/intel/gem + +LIBS += cxx libdrm +LIBS += gpu_i915_drv + +SHARED_LIB = yes diff --git a/libports/lib/mk/gallium-identity.mk b/libports/lib/mk/gallium-identity.mk new file mode 100644 index 000000000..ed8529bc0 --- /dev/null +++ b/libports/lib/mk/gallium-identity.mk @@ -0,0 +1,5 @@ +include $(REP_DIR)/lib/mk/gallium.inc + +SRC_C := $(notdir $(wildcard $(GALLIUM_SRC_DIR)/drivers/identity/*.c)) + +vpath %.c $(GALLIUM_SRC_DIR)/drivers/identity diff --git a/libports/lib/mk/gallium-softpipe.mk b/libports/lib/mk/gallium-softpipe.mk new file mode 100644 index 000000000..12d9f6d94 --- /dev/null +++ b/libports/lib/mk/gallium-softpipe.mk @@ -0,0 +1,5 @@ +include $(REP_DIR)/lib/mk/gallium.inc + +SRC_C := $(notdir $(wildcard $(GALLIUM_SRC_DIR)/drivers/softpipe/*.c)) + +vpath %.c $(GALLIUM_SRC_DIR)/drivers/softpipe diff --git a/libports/lib/mk/gallium-trace.mk b/libports/lib/mk/gallium-trace.mk new file mode 100644 index 000000000..a6aed5cf2 --- /dev/null +++ b/libports/lib/mk/gallium-trace.mk @@ -0,0 +1,5 @@ +include $(REP_DIR)/lib/mk/gallium.inc + +SRC_C := $(notdir $(wildcard $(GALLIUM_SRC_DIR)/drivers/trace/*.c)) + +vpath %.c $(GALLIUM_SRC_DIR)/drivers/trace diff --git a/libports/lib/mk/gallium.inc b/libports/lib/mk/gallium.inc new file mode 100644 index 000000000..6c7ba2362 --- /dev/null +++ b/libports/lib/mk/gallium.inc @@ -0,0 +1,28 @@ +MESA = Mesa-7.8.1 +MESA_DIR = $(REP_DIR)/contrib/$(MESA) +GALLIUM_SRC_DIR = $(MESA_DIR)/src/gallium + +LIBS += cxx libc libm + +INC_DIR += $(GALLIUM_SRC_DIR)/include \ + $(GALLIUM_SRC_DIR)/auxiliary \ + $(GALLIUM_SRC_DIR)/auxiliary/util \ + $(GALLIUM_SRC_DIR)/drivers + +CC_OPT += -U__linux__ + +# +# Prevent double definition of 'ushort' and 'uint' in 'pipe/p_compiler.h' and +# 'libc/sys/types.h'. By defining '__USE_MISC', we suppress the first one. +# However, because the libc headers are not included by all gallium sources +# that include 'p_compiler.h', we unconditionally include 'sys/types.h'. +# +CC_OPT += -D__USE_MISC -include sys/types.h + +# +# Detect missing preparation of Mesa library, skip unprepared library +# +ifeq ($(wildcard $(REP_DIR)/include/GL),) +REQUIRES = prepare_mesa +endif + diff --git a/libports/lib/mk/gallium.mk b/libports/lib/mk/gallium.mk new file mode 100644 index 000000000..9452a6bb3 --- /dev/null +++ b/libports/lib/mk/gallium.mk @@ -0,0 +1,8 @@ +# +# Aggregate all libraries needed to build a gallium-based GL application +# +LIBS = libc libm mesa mesa-egl gallium-aux gallium-egl gallium-softpipe + +SHARED_LIB = yes + +include $(REP_DIR)/lib/mk/gallium.inc diff --git a/libports/lib/mk/gmp-mpf.mk b/libports/lib/mk/gmp-mpf.mk new file mode 100644 index 000000000..8f7e69899 --- /dev/null +++ b/libports/lib/mk/gmp-mpf.mk @@ -0,0 +1,5 @@ +SRC_C += $(notdir $(wildcard $(GMP_DIR)/mpf/*.c)) + +include $(REP_DIR)/lib/mk/gmp.inc + +vpath %.c $(GMP_DIR)/mpf diff --git a/libports/lib/mk/gmp-mpq.mk b/libports/lib/mk/gmp-mpq.mk new file mode 100644 index 000000000..6db64a0c3 --- /dev/null +++ b/libports/lib/mk/gmp-mpq.mk @@ -0,0 +1,5 @@ +SRC_C += $(notdir $(wildcard $(GMP_DIR)/mpq/*.c)) + +include $(REP_DIR)/lib/mk/gmp.inc + +vpath %.c $(GMP_DIR)/mpq diff --git a/libports/lib/mk/gmp-mpz.mk b/libports/lib/mk/gmp-mpz.mk new file mode 100644 index 000000000..2c1e4a8b7 --- /dev/null +++ b/libports/lib/mk/gmp-mpz.mk @@ -0,0 +1,5 @@ +SRC_C += $(notdir $(wildcard $(GMP_DIR)/mpz/*.c)) + +include $(REP_DIR)/lib/mk/gmp.inc + +vpath %.c $(GMP_DIR)/mpz diff --git a/libports/lib/mk/gmp.inc b/libports/lib/mk/gmp.inc new file mode 100644 index 000000000..b0a02e8fe --- /dev/null +++ b/libports/lib/mk/gmp.inc @@ -0,0 +1,13 @@ +GMP_DIR = $(REP_DIR)/contrib/gmp-4.3.2 + +ifeq ($(wildcard $(GMP_DIR)),) +REQUIRES += prepare_gmp +endif + +include $(REP_DIR)/lib/import/import-gmp.mk + +LIBS += libc + +CC_OPT += -DHAVE_CONFIG_H -D__GMP_WITHIN_GMP + +INC_DIR += $(REP_DIR)/include/gcc diff --git a/libports/lib/mk/gmp.mk b/libports/lib/mk/gmp.mk new file mode 100644 index 000000000..42152c1a6 --- /dev/null +++ b/libports/lib/mk/gmp.mk @@ -0,0 +1,25 @@ +include $(REP_DIR)/lib/mk/gmp.inc + +LIBS += gmp-mpn gmp-mpf gmp-mpz gmp-mpq + +# +# Source codes from gmp base directory +# +SRC_C += assert.c compat.c errno.c extract-dbl.c invalid.c \ + memory.c mp_bpl.c mp_clz_tab.c mp_dv_tab.c \ + mp_minv_tab.c mp_get_fns.c mp_set_fns.c rand.c randclr.c \ + randdef.c randiset.c randlc2s.c randlc2x.c randmt.c \ + randmts.c rands.c randsd.c randsdui.c randbui.c randmui.c \ + version.c tal-reent.c + +# +# Source codes from subdirectories +# +SRC_C += $(notdir $(wildcard $(GMP_DIR)/printf/*.c)) +SRC_C += $(notdir $(wildcard $(GMP_DIR)/scanf/*.c)) + +vpath %.c $(GMP_DIR) +vpath %.c $(GMP_DIR)/printf +vpath %.c $(GMP_DIR)/scanf + +SHARED_LIB = yes diff --git a/libports/lib/mk/history.mk b/libports/lib/mk/history.mk new file mode 100644 index 000000000..055abf6c4 --- /dev/null +++ b/libports/lib/mk/history.mk @@ -0,0 +1,24 @@ +READLINE = readline-6.0 +READLINE_DIR = $(REP_DIR)/contrib/$(READLINE) +LIBS += libc + +# use our customized 'config.h' +INC_DIR += $(REP_DIR)/include/readline + +# add local readline headers to include-search path +INC_DIR += $(READLINE_DIR) + +CC_DEF += -DHAVE_CONFIG_H +CC_DEF += -DRL_LIBRARY_VERSION='"6.0"' + +# dim build noise for contrib code +CC_WARN = -Wno-unused-but-set-variable + +# sources from readline base directory +SRC_C = \ + history.c histexpand.c histfile.c histsearch.c shell.c mbutil.c \ + xmalloc.c + +vpath %.c $(READLINE_DIR) + +SHARED_LIB = yes diff --git a/libports/lib/mk/jpeg.mk b/libports/lib/mk/jpeg.mk new file mode 100644 index 000000000..0ca844a8a --- /dev/null +++ b/libports/lib/mk/jpeg.mk @@ -0,0 +1,27 @@ +JPEG = jpeg-7 +JPEG_DIR = $(REP_DIR)/contrib/$(JPEG) +LIBS += libc + +# use our customized 'jconfig.h' file +INC_DIR += $(REP_DIR)/include/jpeg + +# add local jpeg headers to include search path +INC_DIR += $(JPEG_DIR) + +SRC_C = \ + jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c \ + jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c \ + jcparam.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdarith.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdpostct.c \ + jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c jfdctint.c \ + jidctflt.c jidctfst.c jidctint.c jquant1.c jquant2.c jutils.c jmemmgr.c \ + jmemnobs.c + +# prevent warnings about the word 'main' used as variable name +CC_OPT_jdmainct += -Wno-main +CC_OPT_jcmainct += -Wno-main + +vpath %.c $(JPEG_DIR) + +SHARED_LIB = yes diff --git a/libports/lib/mk/libc-common.inc b/libports/lib/mk/libc-common.inc new file mode 100644 index 000000000..39869865b --- /dev/null +++ b/libports/lib/mk/libc-common.inc @@ -0,0 +1,26 @@ +LIBC_DIR = $(REP_DIR)/contrib/libc-8.2.0 + +# local libc includes +INC_DIR += $(LIBC_DIR)/libc/locale +INC_DIR += $(LIBC_DIR)/libc/include +INC_DIR += $(LIBC_DIR)/libc/stdio +INC_DIR += $(LIBC_DIR)/gdtoa + +#CC_OPT += -DGENODE_RELEASE + +# +# Use default warning level rather than -Wall because we do not want to touch +# the imported source code to improve build aesthetics +# +CC_WARN = + +# +# Generate position independent code to allow linking of static libc code into +# shared libraries (define is evaluated by assembler files) +# +CC_OPT += -DPIC + +# +# Use libc import rules also for building the libc itself +# +include $(REP_DIR)/lib/import/import-libc.mk diff --git a/libports/lib/mk/libc-gdtoa.mk b/libports/lib/mk/libc-gdtoa.mk new file mode 100644 index 000000000..338e661bf --- /dev/null +++ b/libports/lib/mk/libc-gdtoa.mk @@ -0,0 +1,13 @@ +GDTOA_DIR = $(LIBC_DIR)/gdtoa +LIBC_GDTOA_DIR = $(LIBC_DIR)/libc/gdtoa + +FILTER_OUT = arithchk.c strtodnrp.c qnan.c +FILTER_OUT += machdep_ldisQ.c machdep_ldisx.c + +SRC_C = $(filter-out $(FILTER_OUT),$(notdir $(wildcard $(GDTOA_DIR)/*.c))) \ + $(filter-out $(FILTER_OUT),$(notdir $(wildcard $(LIBC_GDTOA_DIR)/*.c))) + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(GDTOA_DIR) +vpath %.c $(LIBC_GDTOA_DIR) diff --git a/libports/lib/mk/libc-gen.inc b/libports/lib/mk/libc-gen.inc new file mode 100644 index 000000000..edf8af319 --- /dev/null +++ b/libports/lib/mk/libc-gen.inc @@ -0,0 +1,13 @@ +LIBC_GEN_DIR = $(LIBC_DIR)/libc/gen + +# this file produces a warning about a missing header file, lets drop it +FILTER_OUT_C += getosreldate.c sem.c valloc.c + +SRC_C = $(filter-out $(FILTER_OUT_C),$(notdir $(wildcard $(LIBC_GEN_DIR)/*.c))) + +# 'sysconf.c' includes the local 'stdtime/tzfile.h' +INC_DIR += $(REP_DIR)/src/lib/libc/stdtime + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(LIBC_GEN_DIR) diff --git a/libports/lib/mk/libc-inet.mk b/libports/lib/mk/libc-inet.mk new file mode 100644 index 000000000..9ca30d75d --- /dev/null +++ b/libports/lib/mk/libc-inet.mk @@ -0,0 +1,9 @@ +LIBC_INET_DIR = $(LIBC_DIR)/libc/inet + +FILTER_OUT_C += nsap_addr.c + +SRC_C = $(filter-out $(FILTER_OUT_C),$(notdir $(wildcard $(LIBC_INET_DIR)/*.c))) + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(LIBC_INET_DIR) diff --git a/libports/lib/mk/libc-locale.mk b/libports/lib/mk/libc-locale.mk new file mode 100644 index 000000000..0e43ff82e --- /dev/null +++ b/libports/lib/mk/libc-locale.mk @@ -0,0 +1,7 @@ +LIBC_LOCALE_DIR = $(LIBC_DIR)/libc/locale + +SRC_C = $(notdir $(wildcard $(LIBC_LOCALE_DIR)/*.c)) + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(LIBC_LOCALE_DIR) diff --git a/libports/lib/mk/libc-stdio.mk b/libports/lib/mk/libc-stdio.mk new file mode 100644 index 000000000..781b0128b --- /dev/null +++ b/libports/lib/mk/libc-stdio.mk @@ -0,0 +1,7 @@ +LIBC_STDIO_DIR = $(LIBC_DIR)/libc/stdio + +SRC_C = $(notdir $(wildcard $(LIBC_STDIO_DIR)/*.c)) + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(LIBC_STDIO_DIR) diff --git a/libports/lib/mk/libc-stdlib.mk b/libports/lib/mk/libc-stdlib.mk new file mode 100644 index 000000000..38ce537db --- /dev/null +++ b/libports/lib/mk/libc-stdlib.mk @@ -0,0 +1,9 @@ +LIBC_STDLIB_DIR = $(LIBC_DIR)/libc/stdlib +FILTER_OUT = exit.c atexit.c malloc.c + +#SRC_C = $(notdir $(wildcard $(LIBC_STDLIB_DIR)/*.c)) +SRC_C = $(filter-out $(FILTER_OUT),$(notdir $(wildcard $(LIBC_STDLIB_DIR)/*.c))) + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(LIBC_STDLIB_DIR) diff --git a/libports/lib/mk/libc-stdtime.mk b/libports/lib/mk/libc-stdtime.mk new file mode 100644 index 000000000..3243c33ee --- /dev/null +++ b/libports/lib/mk/libc-stdtime.mk @@ -0,0 +1,7 @@ +LIBC_STDTIME_DIR = $(LIBC_DIR)/libc/stdtime + +SRC_C = $(filter-out $(FILTER_OUT),$(notdir $(wildcard $(LIBC_STDTIME_DIR)/*.c))) + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(LIBC_STDTIME_DIR) diff --git a/libports/lib/mk/libc-string.mk b/libports/lib/mk/libc-string.mk new file mode 100644 index 000000000..4372fbdba --- /dev/null +++ b/libports/lib/mk/libc-string.mk @@ -0,0 +1,18 @@ +# +# Portion of the string library that is used by both the freestanding string +# library and the complete libc +# + +# +# These files would infect the freestanding string library with the locale +# library +# +FILTER_OUT = strcoll.c strxfrm.c wcscoll.c wcsxfrm.c + +LIBC_STRING_DIR = $(LIBC_DIR)/libc/string + +SRC_C = $(filter-out $(FILTER_OUT),$(notdir $(wildcard $(LIBC_STRING_DIR)/*.c))) + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath %.c $(LIBC_STRING_DIR) diff --git a/libports/lib/mk/libc.mk b/libports/lib/mk/libc.mk new file mode 100644 index 000000000..8e29221dc --- /dev/null +++ b/libports/lib/mk/libc.mk @@ -0,0 +1,32 @@ +# +# C Library including string, locale +# + +LIBS = libc-string libc-locale libc-stdlib libc-stdio libc-gen libc-gdtoa \ + libc-inet libc-stdtime +LIBS += timed_semaphore cxx + +# +# Back end +# +SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc readlink.cc munmap.cc \ + issetugid.cc errno.cc gai_strerror.cc ioctl.cc clock_gettime.cc \ + gettimeofday.cc malloc.cc progname.cc fd_alloc.cc file_operations.cc \ + plugin.cc plugin_registry.cc select.cc exit.cc environ.cc + +# +# Files from string library that are not included in libc-raw_string because +# they depend on the locale library. +# +SRC_C += strcoll.c strxfrm.c wcscoll.c wcsxfrm.c + +include $(REP_DIR)/lib/mk/libc-common.inc + +vpath % $(REP_DIR)/src/lib/libc +vpath % $(LIBC_DIR)/libc/string + +# +# Shared library, for libc we need symbol versioning +# +SHARED_LIB = yes +LD_OPT += --version-script=$(REP_DIR)/src/lib/libc/Version.def diff --git a/libports/lib/mk/libc_ffat.mk b/libports/lib/mk/libc_ffat.mk new file mode 100644 index 000000000..ad229671e --- /dev/null +++ b/libports/lib/mk/libc_ffat.mk @@ -0,0 +1,6 @@ +SRC_CC = plugin.cc +LIBS += libc ffat_block + +vpath plugin.cc $(REP_DIR)/src/lib/libc_ffat + +SHARED_LIB = yes diff --git a/libports/lib/mk/libc_lock_pipe.mk b/libports/lib/mk/libc_lock_pipe.mk new file mode 100644 index 000000000..4ed5238e4 --- /dev/null +++ b/libports/lib/mk/libc_lock_pipe.mk @@ -0,0 +1,6 @@ +SRC_CC = plugin.cc +LIBS += libc + +vpath %.cc $(REP_DIR)/src/lib/libc_lock_pipe + +SHARED_LIB = yes diff --git a/libports/lib/mk/libc_log.mk b/libports/lib/mk/libc_log.mk new file mode 100644 index 000000000..0df2b0729 --- /dev/null +++ b/libports/lib/mk/libc_log.mk @@ -0,0 +1,6 @@ +SRC_CC = plugin.cc +LIBS += libc + +vpath plugin.cc $(REP_DIR)/src/lib/libc_log + +SHARED_LIB = yes diff --git a/libports/lib/mk/libc_lwip.mk b/libports/lib/mk/libc_lwip.mk new file mode 100644 index 000000000..d6e9e2069 --- /dev/null +++ b/libports/lib/mk/libc_lwip.mk @@ -0,0 +1,5 @@ +SRC_CC = init.cc plugin.cc + +vpath %.cc $(REP_DIR)/src/lib/libc_lwip + +LIBS += lwip libc diff --git a/libports/lib/mk/libc_lwip_loopback.mk b/libports/lib/mk/libc_lwip_loopback.mk new file mode 100644 index 000000000..4d6cab0ac --- /dev/null +++ b/libports/lib/mk/libc_lwip_loopback.mk @@ -0,0 +1,5 @@ +SRC_CC = init.cc + +vpath %.cc $(REP_DIR)/src/lib/libc_lwip_loopback + +LIBS += lwip libc libc_lwip diff --git a/libports/lib/mk/libc_lwip_nic_dhcp.mk b/libports/lib/mk/libc_lwip_nic_dhcp.mk new file mode 100644 index 000000000..926db97fa --- /dev/null +++ b/libports/lib/mk/libc_lwip_nic_dhcp.mk @@ -0,0 +1,5 @@ +SRC_CC = init.cc + +vpath %.cc $(REP_DIR)/src/lib/libc_lwip_nic_dhcp + +LIBS += lwip libc libc_lwip diff --git a/libports/lib/mk/libc_terminal.mk b/libports/lib/mk/libc_terminal.mk new file mode 100644 index 000000000..237ef81ab --- /dev/null +++ b/libports/lib/mk/libc_terminal.mk @@ -0,0 +1,6 @@ +SRC_CC = plugin.cc +LIBS += libc + +vpath plugin.cc $(REP_DIR)/src/lib/libc_terminal + +SHARED_LIB = yes diff --git a/libports/lib/mk/libdrm.mk b/libports/lib/mk/libdrm.mk new file mode 100644 index 000000000..bf7137f7c --- /dev/null +++ b/libports/lib/mk/libdrm.mk @@ -0,0 +1,11 @@ +SRC_C = intel_bufmgr_gem.c intel_bufmgr.c ioctl.cc + +LIBDRM_DIR := $(REP_DIR)/contrib/libdrm-2.4.21 +INC_DIR += $(LIBDRM_DIR) $(LIBDRM_DIR)/include/drm $(LIBDRM_DIR)/intel + +LIBS += libc cxx +CC_OPT += -U__linux__ +CC_OPT += -DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1 + +vpath %.c $(LIBDRM_DIR)/intel +vpath ioctl.cc $(REP_DIR)/src/lib/libdrm diff --git a/libports/lib/mk/libm.mk b/libports/lib/mk/libm.mk new file mode 100644 index 000000000..cad66a525 --- /dev/null +++ b/libports/lib/mk/libm.mk @@ -0,0 +1,79 @@ +LIBC_DIR = $(REP_DIR)/contrib/libc-8.2.0 +LIBM_DIR = $(LIBC_DIR)/msun +LIBS = libc + +# +# finding 'math_private.h' +# +INC_DIR += $(LIBM_DIR)/src + +# +# finding 'invtrig.h', included by 'e_acosl.c' +# +INC_DIR += $(LIBM_DIR)/ld80 + +# +# finding 'fpmath.h', included by 'invtrig.h' +# +INC_DIR += $(LIBC_DIR)/libc/include + +FILTER_OUT += s_exp2l.c + +# +# Files that are included by other sources (e.g., 's_sin.c'). Hence, we need +# to remove them from the build. Otherwise, we would end up with doubly +# defined symbols (and compiler warnings since those files are apparently +# not meant to be compiled individually). +# +FILTER_OUT += e_rem_pio2.c e_rem_pio2f.c + +# +# Disable warnings for selected files, i.e., to suppress +# 'is static but used in inline function which is not static' +# messages +# +CC_OPT_s_tanf = -w +CC_OPT_s_tan = -w +CC_OPT_s_sin = -w +CC_OPT_s_cos = -w +CC_OPT_s_cosf = -w +CC_OPT_s_sinf = -w +CC_OPT_k_cosf = -w +CC_OPT_k_sinf = -w +CC_OPT_k_tanf = -w + +# +# Work-around to get over doubly defined symbols produced by several sources +# that include 'e_rem_pio2.c' and 'e_rem_pio2f.c'. To avoid symbol clashes, +# we rename each occurrence by adding the basename of the compilation unit +# as suffix. +# +CC_OPT_s_sin += -D__ieee754_rem_pio2=__ieee754_rem_pio2_s_sin +CC_OPT_s_cos += -D__ieee754_rem_pio2=__ieee754_rem_pio2_s_cos +CC_OPT_s_sinf += -D__ieee754_rem_pio2f=__ieee754_rem_pio2f_s_sinf +CC_OPT_s_sinf += -D__kernel_cosdf=__kernel_cosdf_sinf +CC_OPT_s_cosf += -D__ieee754_rem_pio2f=__ieee754_rem_pio2f_s_cosf +CC_OPT_s_cosf += -D__kernel_sindf=__kernel_sindf_cosf +CC_OPT_s_tanf += -D__ieee754_rem_pio2f=__ieee754_rem_pio2f_s_tanf + +# +# Use default warning level rather than -Wall because we do not want to touch +# the imported source code to improve build aesthetics. +# +CC_WARN = + +SRC_C = $(wildcard $(LIBM_DIR)/src/*.c) \ + $(wildcard $(LIBM_DIR)/ld80/*.c) \ + $(wildcard $(LIBM_DIR)/bsdsrc/*.c) +SRC_C := $(filter-out $(FILTER_OUT),$(notdir $(SRC_C))) + +# +# 'e_rem_pio2.c' uses '__inline' +# +CC_OPT += -D__inline=inline + +vpath %.c $(LIBM_DIR)/src +vpath %.c $(LIBM_DIR)/ld80 +vpath %.c $(LIBM_DIR)/bsdsrc + +SHARED_LIB = yes diff --git a/libports/lib/mk/libpng.mk b/libports/lib/mk/libpng.mk new file mode 100644 index 000000000..72be3b718 --- /dev/null +++ b/libports/lib/mk/libpng.mk @@ -0,0 +1,16 @@ +LIBPNG = libpng-1.4.1 +LIBPNG_DIR = $(REP_DIR)/contrib/$(LIBPNG) +LIBS += libc libm zlib + +# find 'config.h' +INC_DIR += $(REP_DIR)/src/lib/libpng + +CC_DEF += -DHAVE_CONFIG_H -DPNG_CONFIGURE_LIBPNG + +SRC_C = png.c pngset.c pngget.c pngrutil.c pngtrans.c pngwutil.c \ + pngread.c pngrio.c pngwio.c pngwrite.c pngrtran.c pngwtran.c \ + pngmem.c pngerror.c pngpread.c + +vpath %.c $(LIBPNG_DIR) + +SHARED_LIB = yes diff --git a/libports/lib/mk/lwip.mk b/libports/lib/mk/lwip.mk new file mode 100644 index 000000000..06dffa1db --- /dev/null +++ b/libports/lib/mk/lwip.mk @@ -0,0 +1,45 @@ +# +# lwIP TCP/IP library +# +# The library implementes TCP and UDP as well as DNS and DHCP. +# + +LWIP_DIR = $(REP_DIR)/contrib/lwip-1.3.2 + +# Genode platform files +SRC_CC = nic.cc printf.cc sys_arch.cc + +# Core files +SRC_C = init.c mem.c memp.c netif.c pbuf.c stats.c udp.c raw.c sys.c \ + tcp.c tcp_in.c tcp_out.c dhcp.c dns.c + +# IPv4 files +SRC_C += icmp.c inet.c ip_addr.c ip.c ip_frag.c inet_chksum.c + +# API files +SRC_C += err.c api_lib.c api_msg.c netbuf.c netdb.c netifapi.c sockets.c \ + tcpip.c + +# Network interface files +SRC_C += etharp.c loopif.c + +LIBS = thread cxx alarm signal libc timed_semaphore + +D_OPTS = ERRNO +D_OPTS := $(addprefix -D,$(D_OPTS)) +CC_DEF += $(D_OPTS) + +INC_DIR += $(REP_DIR)/include/lwip \ + $(LWIP_DIR)/src/include \ + $(LWIP_DIR)/src/include/ipv4 \ + $(LWIP_DIR)/src/include/api \ + $(LWIP_DIR)/src/include/netif \ + $(REP_DIR)/src/lib/lwip/include + +vpath %.cc $(REP_DIR)/src/lib/lwip/platform +vpath %.c $(LWIP_DIR)/src/core +vpath %.c $(LWIP_DIR)/src/core/ipv4 +vpath %.c $(LWIP_DIR)/src/api +vpath %.c $(LWIP_DIR)/src/netif + +SHARED_LIB = yes diff --git a/libports/lib/mk/mesa-egl.mk b/libports/lib/mk/mesa-egl.mk new file mode 100644 index 000000000..7f4869e5a --- /dev/null +++ b/libports/lib/mk/mesa-egl.mk @@ -0,0 +1,13 @@ +include $(REP_DIR)/lib/mk/mesa.inc + +SRC_C := $(notdir $(wildcard $(MESA_DIR)/src/egl/main/*.c)) + +CC_OPT += -D_EGL_DRIVER_SEARCH_DIR=\"\" +CC_OPT += -D_EGL_DEFAULT_DISPLAY=\"\" +CC_OPT += -U__unix__ -U__unix + +# dim warning noise +CC_OPT_eglconfig += -Wno-uninitialized + +vpath %.c $(MESA_DIR)/src/egl/main + diff --git a/libports/lib/mk/mesa.inc b/libports/lib/mk/mesa.inc new file mode 100644 index 000000000..5173920ad --- /dev/null +++ b/libports/lib/mk/mesa.inc @@ -0,0 +1,21 @@ +MESA = Mesa-7.8.1 +MESA_DIR = $(REP_DIR)/contrib/$(MESA) +MESA_SRC_DIR = $(MESA_DIR)/src/mesa + +ifeq ($(wildcard $(MESA_DIR)),) +REQUIRES += prepare_mesa +endif + +LIBS += cxx libc libm + +# +# Prevent warning about non-existing 'fpu_control.h' included +# by 'mesa/main/compiler.h' if '__linux__' is defined. +# +CC_OPT += -U__linux__ + +INC_DIR += $(MESA_DIR)/src/mesa \ + $(MESA_DIR)/src/gallium/include \ + $(MESA_DIR)/src/gallium/auxiliary + +#SHARED_LIB = yes diff --git a/libports/lib/mk/mesa.mk b/libports/lib/mk/mesa.mk new file mode 100644 index 000000000..1f987ff67 --- /dev/null +++ b/libports/lib/mk/mesa.mk @@ -0,0 +1,65 @@ +include $(REP_DIR)/lib/mk/mesa.inc + +MESA_SUBDIRS = main math vbo shader shader/slang glapi + +# collect all source codes in 'MESA_SUBDIRS' +SRC_C := $(foreach subdir,$(MESA_SUBDIRS),$(wildcard $(MESA_SRC_DIR)/$(subdir)/*.c)) + +# prevent definition conflicts in lex.yy.c with libc +CC_OPT_lex_yy += -DFLEXINT_H -include inttypes.h -Dflex_int32_t=int32_t -Dflex_int16_t=int16_t + +# dim warning noise for compiling contrib code +CC_OPT_bufferobj += -Wno-unused-but-set-variable +CC_OPT_dlist += -Wno-unused-but-set-variable +CC_OPT_glapi += -Wno-strict-aliasing +CC_OPT_lex_yy += -Wno-unused-function +CC_OPT_prog_print += -Wno-format +CC_OPT_program += -Wno-enum-compare +CC_OPT_shader_api += -Wno-unused-but-set-variable +CC_OPT_slang_emit += -Wno-unused-but-set-variable +CC_OPT_st_cb_texture += -Wno-strict-aliasing +CC_OPT_texcompress_s3tc += -Wno-unused-but-set-variable +CC_OPT_varray += -Wno-format + +# glsl library +GLSL_SRC_DIR = $(MESA_DIR)/src/glsl +GLSL_SUBDIRS = pp cl +SRC_C += $(foreach subdir,$(GLSL_SUBDIRS),$(wildcard $(GLSL_SRC_DIR)/$(subdir)/*.c)) + +# strip leading directories - keep only the file names +SRC_C := $(notdir $(SRC_C)) + +# remove non-needed files from list +SRC_C := $(filter-out vsnprintf.c,$(SRC_C)) + +vpath %.c $(addprefix $(MESA_SRC_DIR)/,$(MESA_SUBDIRS)) +vpath %.c $(addprefix $(GLSL_SRC_DIR)/,$(GLSL_SUBDIRS)) + +# +# Compile built-in fragment and vertex shaders +# +# The shader programs are compiled to header files in a +# 'library/' subdirectory, which are then included by mesa's +# 'shader/slang' module. +# +SRC_GC := $(wildcard $(MESA_SRC_DIR)/shader/slang/library/*.gc) +GEN_GC_H := $(notdir $(SRC_GC:.gc=_gc.h)) + +# make sure that the shaders are compiled prior the mesa source codes +$(SRC_C:.c=.o): $(addprefix library/,$(GEN_GC_H)) + +$(addprefix library/,$(GEN_GC_H)): library + +library: + $(VERBOSE)mkdir -p $@ + +library/%_gc.h: %.gc + $(MSG_CONVERT)$@ + $(VERBOSE)$(REP_DIR)/tool/mesa/glsl/compiler fragment $< $@ + +library/slang_vertex_builtin_gc.h: slang_vertex_builtin.gc + $(MSG_CONVERT)$@ + $(VERBOSE)$(REP_DIR)/tool/mesa/glsl/compiler vertex $< $@ + +vpath %.gc $(MESA_SRC_DIR)/shader/slang/library + diff --git a/libports/lib/mk/mpfr.mk b/libports/lib/mk/mpfr.mk new file mode 100644 index 000000000..553834677 --- /dev/null +++ b/libports/lib/mk/mpfr.mk @@ -0,0 +1,20 @@ +MPFR_DIR = $(REP_DIR)/contrib/mpfr-3.0.0 + +ifeq ($(wildcard $(MPFR_DIR)),) +REQUIRES += prepare_mpfr +endif + +# mpfr depends on gmp, which is only supported on x86 yet +REQUIRES += x86 + +LIBS = libc gmp +CC_OPT += -DHAVE_STDARG -DHAVE_VA_COPY -DHAVE_INTTYPES_H +INC_DIR += $(REP_DIR)/include/mpfr + +MPFR_SRC_C := $(notdir $(wildcard $(MPFR_DIR)/*.c)) +FILTER_OUT := ansi2knr.c jyn_asympt.c round_raw_generic.c speed.c tuneup.c +SRC_C := $(filter-out $(FILTER_OUT),$(MPFR_SRC_C)) + +vpath %.c $(MPFR_DIR) + +SHARED_LIB = 1 diff --git a/libports/lib/mk/ncurses.mk b/libports/lib/mk/ncurses.mk new file mode 100644 index 000000000..6caf40e57 --- /dev/null +++ b/libports/lib/mk/ncurses.mk @@ -0,0 +1,34 @@ +NCURSES = ncurses-5.9 +NCURSES_DIR = $(REP_DIR)/contrib/$(NCURSES) +NCURSES_SRC_DIR = $(NCURSES_DIR)/ncurses + +# files from the 'ncurses/base/' subdirectory +ALL_BASE_SRC_C = $(notdir $(wildcard $(NCURSES_SRC_DIR)/base/*.c)) +SRC_C += $(filter-out sigaction.c lib_driver.c,$(ALL_BASE_SRC_C)) +vpath %.c $(NCURSES_SRC_DIR)/base + +# files from the 'ncurses/tinfo/' subdirectory +ALL_TINFO_SRC_C = $(notdir $(wildcard $(NCURSES_SRC_DIR)/tinfo/*.c)) +SRC_C += $(filter-out make_hash.c make_keys.c tinfo_driver.c,$(ALL_TINFO_SRC_C)) +vpath %.c $(NCURSES_SRC_DIR)/tinfo + +# files from the 'ncurses/tty/' subdirectory +ALL_TTY_SRC_C = $(notdir $(wildcard $(NCURSES_SRC_DIR)/tty/*.c)) +SRC_C += $(ALL_TTY_SRC_C) +vpath %.c $(NCURSES_SRC_DIR)/tty + +# files from the 'ncurses/trace/' subdirectory +SRC_C += $(notdir $(addprefix $(NCURSES_SRC_DIR)/trace/,lib_trace.c varargs.c visbuf.c)) +vpath %.c $(NCURSES_SRC_DIR)/trace + +# files generated by 'make prepare' +SRC_C += $(notdir $(wildcard $(REP_DIR)/src/lib/ncurses/*.c)) +vpath %.c $(REP_DIR)/src/lib/ncurses + +INC_DIR += $(NCURSES_SRC_DIR) +INC_DIR += $(REP_DIR)/include/ncurses + +LIBS += libc + +SHARED_LIB = yes + diff --git a/libports/lib/mk/python.inc b/libports/lib/mk/python.inc new file mode 100644 index 000000000..4cc365c85 --- /dev/null +++ b/libports/lib/mk/python.inc @@ -0,0 +1,143 @@ +PYTHON = python-2.6.4 +PYTHON_DIR = $(REP_DIR)/contrib/$(PYTHON) +LIBS += libc libm +SHARED_LIB = yes + +# use our custom 'pyconfig.h' file +INC_DIR += $(REP_DIR)/include/python + +# Python headres +INC_DIR += $(PYTHON_DIR)/Include + +D_OPTS = Py_BUILD_CORE NDBEUG PREFIX='""' EXEC_PREFIX='"lib"' VERSION='"2.6"' +F_OPTS = no-strict-aliasing wrapv +D_OPTS := $(addprefix -D,$(D_OPTS)) +F_OPTS := $(addprefix -f,$(F_OPTS)) +CC_DEF += $(F_OPTS) $(D_OPTS) + +# libc back-end +SRC_CC = libc_plugin.cc libc_plugin_init.cc + +# python.c +# +SRC_C = \ + dynload_shlib.c \ + dup.c \ + getbuildinfo.c \ + acceler.c \ + grammar1.c \ + listnode.c \ + node.c \ + parser.c \ + parsetok.c \ + bitset.c \ + metagrammar.c \ + firstsets.c \ + grammar.c \ + pgen.c \ + myreadline.c \ + tokenizer.c \ + abstract.c \ + boolobject.c \ + bufferobject.c \ + bytes_methods.c \ + bytearrayobject.c \ + cellobject.c \ + classobject.c \ + cobject.c \ + codeobject.c \ + complexobject.c \ + descrobject.c \ + enumobject.c \ + exceptions.c \ + genobject.c \ + fileobject.c \ + floatobject.c \ + frameobject.c \ + funcobject.c \ + intobject.c \ + iterobject.c \ + listobject.c \ + longobject.c \ + dictobject.c \ + methodobject.c \ + moduleobject.c \ + object.c \ + obmalloc.c \ + rangeobject.c \ + setobject.c \ + sliceobject.c \ + stringobject.c \ + structseq.c \ + tupleobject.c \ + typeobject.c \ + weakrefobject.c \ + unicodeobject.c \ + unicodectype.c \ + _warnings.c \ + Python-ast.c \ + asdl.c \ + ast.c \ + bltinmodule.c \ + ceval.c \ + compile.c \ + codecs.c \ + errors.c \ + frozen.c \ + frozenmain.c \ + future.c \ + getargs.c \ + getcompiler.c \ + getcopyright.c \ + getmtime.c \ + getplatform.c \ + getversion.c \ + graminit.c \ + import.c \ + importdl.c \ + marshal.c \ + modsupport.c \ + mystrtoul.c \ + mysnprintf.c \ + peephole.c \ + pyarena.c \ + pyfpe.c \ + pymath.c \ + pystate.c \ + pythonrun.c \ + structmember.c \ + symtable.c \ + sysmodule.c \ + traceback.c \ + getopt.c \ + pystrcmp.c \ + pystrtod.c \ + formatter_unicode.c \ + formatter_string.c \ + config.c \ + getpath.c \ + main.c \ + gcmodule.c \ + signalmodule.c \ + posixmodule.c \ + errnomodule.c \ + pwdmodule.c \ + _sre.c \ + _codecsmodule.c \ + zipimport.c \ + symtablemodule.c \ + xxsubtype.c + +CC_C_OPT = -Wno-implicit-function-declaration \ + -Wno-int-to-pointer-cast \ + -Wno-unused-but-set-variable \ + -Wno-unused-function \ + -Wno-unused-variable + +vpath %.c $(PYTHON_DIR)/Modules +vpath %.c $(PYTHON_DIR)/Objects +vpath %.c $(PYTHON_DIR)/Parser +vpath %.c $(PYTHON_DIR)/Python +vpath %.c $(REP_DIR)/src/lib/python +vpath %.cc $(REP_DIR)/src/lib/python + diff --git a/libports/lib/mk/readline.mk b/libports/lib/mk/readline.mk new file mode 100644 index 000000000..3702e2057 --- /dev/null +++ b/libports/lib/mk/readline.mk @@ -0,0 +1,30 @@ +READLINE = readline-6.0 +READLINE_DIR = $(REP_DIR)/contrib/$(READLINE) +LIBS += libc + +# use our customized 'config.h' +INC_DIR += $(REP_DIR)/include/readline + +# add local readline headers to include-search path +INC_DIR += $(READLINE_DIR)/src/base + +CC_DEF += -DHAVE_CONFIG_H +CC_DEF += -DRL_LIBRARY_VERSION='"6.0"' + +# dim build noise for contrib code +CC_WARN = -Wno-unused-but-set-variable + +# sources from readline base directory +SRC_C = \ + readline.c vi_mode.c funmap.c keymaps.c parens.c search.c rltty.c \ + complete.c bind.c isearch.c display.c signals.c util.c kill.c undo.c \ + macro.c input.c callback.c terminal.c text.c nls.c misc.c xmalloc.c \ + history.c histexpand.c histfile.c histsearch.c shell.c mbutil.c tilde.c \ + compat.c + +SRC_CC += genode.cc + +vpath %.c $(READLINE_DIR) +vpath genode.cc $(REP_DIR)/src/lib/readline + +SHARED_LIB = yes diff --git a/libports/lib/mk/sdl.mk b/libports/lib/mk/sdl.mk new file mode 100644 index 000000000..580ed1bf7 --- /dev/null +++ b/libports/lib/mk/sdl.mk @@ -0,0 +1,131 @@ +# +# SDL library +# + +SDL = SDL-1.2.13 +SDL_DIR = $(REP_DIR)/contrib/$(SDL) + +# +# XXX libSDL does not build on ARM because a compile-time assertion fails: +# +# +# SDL_stdinc.h:133:1: error: size of array ‘SDL_dummy_enum’ is negative +# +REQUIRES = x86 + +# build shared object +SHARED_LIB = yes + +# backends +SRC_CC = SDL_genode_fb_video.cc \ + SDL_genode_fb_events.cc + +INC_DIR += $(REP_DIR)/include/SDL \ + $(REP_DIR)/src/lib/sdl \ + $(REP_DIR)/src/lib/sdl/video + +# main files +SRC_C = SDL.c \ + SDL_error.c \ + SDL_fatal.c + +INC_DIR += $(REP_DIR)/src/lib/sdl +#INC_DIR += $(REP_DIR)/include/SDL_genode \ +# $(REP_DIR)/include/SDL_genode/SDL \ +# $(REP_DIR)/include/SDL_contrib \ +# $(REP_DIR)/include/SDL_contrib/SDL \ +# $(REP_DIR)/src/lib/sdl/src \ + +# stdlib files +SRC_C += SDL_getenv.c \ + SDL_string.h + +# thread subsystem +SRC_C += SDL_thread.c \ + SDL_systhread.c \ + SDL_sysmutex.c \ + SDL_syssem.c + +# cpuinfo subsystem +SRC_C += SDL_cpuinfo.c + +# timer subsystem +SRC_C += SDL_systimer.c \ + SDL_timer.c + +# video subsystem +SRC_C += SDL_blit_0.c \ + SDL_blit.c \ + SDL_cursor.c \ + SDL_RLEaccel.c \ + SDL_video.c \ + SDL_yuv_sw.c \ + SDL_blit_1.c \ + SDL_blit_N.c \ + SDL_gamma.c \ + SDL_stretch.c \ + SDL_yuv.c \ + SDL_blit_A.c \ + SDL_bmp.c \ + SDL_pixels.c \ + SDL_surface.c \ + SDL_yuv_mmx.c +INC_DIR += $(SDL_DIR)/src/video + +# event subsystem +SRC_C += SDL_events.c \ + SDL_keyboard.c \ + SDL_mouse.c \ + SDL_resize.c \ + SDL_active.c \ + SDL_quit.c +INC_DIR += $(SDL_DIR)/src/events + +# audio subsystem +SRC_C += SDL_audio.c \ + SDL_audiocvt.c \ + SDL_audiodev.c \ + SDL_mixer.c \ + SDL_mixer_m68k.c \ + SDL_mixer_MMX.c \ + SDL_mixer_MMX_VC.c \ + SDL_wave.c \ + SDL_dummyaudio.c + +# file I/O subsystem +SRC_C += SDL_rwops.c + +# joystick subsystem +SRC_C += SDL_joystick.c \ + SDL_sysjoystick.c +INC_DIR += $(SDL_DIR)/src/joystick + +# we need libc +LIBS = libc + +# dim build noise for contrib code +CC_OPT_SDL_RLEaccel += -Wno-unused-but-set-variable +CC_OPT_SDL_bmp += -Wno-unused-but-set-variable +CC_OPT_SDL_stretch += -Wno-unused-but-set-variable +CC_OPT_SDL_wave += -Wno-unused-but-set-variable + +# backend pathes +vpath % $(REP_DIR)/src/lib/sdl +vpath % $(REP_DIR)/src/lib/sdl/video + +# contribution pathes +vpath %.c $(SDL_DIR)/src +vpath %.c $(SDL_DIR)/src/stdlib +vpath %.c $(SDL_DIR)/src/video +vpath %.c $(SDL_DIR)/src/video/dummy +vpath %.c $(SDL_DIR)/src/audio +vpath %.c $(SDL_DIR)/src/audio/dummy +vpath %.c $(SDL_DIR)/src/thread +vpath %.c $(SDL_DIR)/src/thread/generic +vpath %.c $(SDL_DIR)/src/timer +vpath %.c $(SDL_DIR)/src/timer/dummy +vpath %.c $(SDL_DIR)/src/events +vpath %.c $(SDL_DIR)/src/cpuinfo +vpath %.c $(SDL_DIR)/src/file +vpath %.c $(SDL_DIR)/src/joystick +vpath %.c $(SDL_DIR)/src/joystick/dummy diff --git a/libports/lib/mk/test-ldso.mk b/libports/lib/mk/test-ldso.mk new file mode 100644 index 000000000..9ede73aa3 --- /dev/null +++ b/libports/lib/mk/test-ldso.mk @@ -0,0 +1,7 @@ +SRC_CC = test-rtld.cc +SHARED_LIB = yes +INC_DIR += $(REP_DIR)/src/test/ldso/include + +LIBS = test-ldso2 + +vpath test-rtld.cc $(REP_DIR)/src/test/ldso/lib diff --git a/libports/lib/mk/test-ldso2.mk b/libports/lib/mk/test-ldso2.mk new file mode 100644 index 000000000..f52b69100 --- /dev/null +++ b/libports/lib/mk/test-ldso2.mk @@ -0,0 +1,5 @@ +SRC_CC = test_lib.cc +SHARED_LIB = yes +INC_DIR += $(REP_DIR)/src/test/ldso/include + +vpath test_lib.cc $(REP_DIR)/src/test/ldso/lib diff --git a/libports/lib/mk/x86_32/gmp-mpn.mk b/libports/lib/mk/x86_32/gmp-mpn.mk new file mode 100644 index 000000000..ae16b549f --- /dev/null +++ b/libports/lib/mk/x86_32/gmp-mpn.mk @@ -0,0 +1,54 @@ + +GMP_MPN_DIR = $(GMP_DIR)/mpn + +FILTER_OUT = popham.c pre_divrem_1.c pre_mod_1.c sizeinbase.c udiv_w_sdiv.c + +SRC_C += $(notdir $(wildcard $(REP_DIR)/src/lib/gmp/mpn/x86/*.c)) +SRC_C += $(filter-out $(FILTER_OUT),$(notdir $(wildcard $(GMP_MPN_DIR)/generic/*.c))) + +SRC_ASM = copyd.asm copyi.asm hamdist.asm popcount.asm udiv.asm umul.asm + +CC_OPT_divrem_1 = -DOPERATION_divrem_1 + +include $(REP_DIR)/lib/mk/gmp.inc + +PWD := $(shell pwd) + +SRC_O += $(SRC_ASM:.asm=.o) + +# +# Create execution environment for the m4-ccas tool, which is used by the gmp +# library to assemble asm files to object files. Make sure to execute this rule +# only from the actual build pass (not when called from 'dep_lib.mk'). This +# way, the 'm4env' gets created within the local build directory of the +# library, not the global build directory. +# +ifeq ($(called_from_lib_mk),yes) +all: m4env +endif + +m4env: + echo "RULE m4env" + $(VERBOSE)mkdir -p $@/mpn/x86 + $(VERBOSE)ln -s $(REP_DIR)/src/lib/gmp/config.m4 m4env + $(VERBOSE)ln -s $(GMP_MPN_DIR)/asm-defs.m4 m4env/mpn + $(VERBOSE)ln -s $(GMP_MPN_DIR)/x86/x86-defs.m4 m4env/mpn/x86 + +# +# Create object files out of asm files +# +ifneq ($(VERBOSE),) +M4_OUTPUT_FILTER = > /dev/null +endif + +%.o: %.asm + $(MSG_ASSEM)$@ + $(VERBOSE)cd m4env/mpn; \ + $(GMP_MPN_DIR)/m4-ccas --m4=m4 $(CC) $(CC_MARCH) -std=gnu99 -fPIC -DPIC $(CC_OPT_$*) $(INCLUDES) -c $< -o $(PWD)/$@ \ + $(M4_OUTPUT_FILTER) + +vpath %.c $(REP_DIR)/src/lib/gmp/mpn/x86 +vpath %.c $(GMP_MPN_DIR)/generic +vpath %.asm $(GMP_MPN_DIR)/x86 +vpath %.asm $(GMP_MPN_DIR)/x86/pentium + diff --git a/libports/lib/mk/x86_32/libc-gen.mk b/libports/lib/mk/x86_32/libc-gen.mk new file mode 100644 index 000000000..e4b763ed6 --- /dev/null +++ b/libports/lib/mk/x86_32/libc-gen.mk @@ -0,0 +1,23 @@ +include $(REP_DIR)/lib/mk/libc-gen.inc + +LIBC_GEN_I386_DIR = $(LIBC_DIR)/libc/i386/gen + +FILTER_OUT_S += rfork_thread.S sigsetjmp.S +FILTER_OUT_C += _set_tp.c + +# +# Filter functions conflicting with libm +# +FILTER_OUT_S += fabs.S modf.S +FILTER_OUT_C += frexp.c + +SRC_S = $(filter-out $(FILTER_OUT_S),$(notdir $(wildcard $(LIBC_GEN_I386_DIR)/*.S))) +SRC_C += flt_rounds.c + +# +# Additional functions for shared libraries +# +SRC_C += makecontext.c + +vpath %.c $(LIBC_GEN_I386_DIR) +vpath %.S $(LIBC_GEN_I386_DIR) diff --git a/libports/lib/mk/x86_32/python.mk b/libports/lib/mk/x86_32/python.mk new file mode 100644 index 000000000..a7c08a6a8 --- /dev/null +++ b/libports/lib/mk/x86_32/python.mk @@ -0,0 +1,3 @@ +include $(REP_DIR)/lib/mk/python.inc + +INC_DIR += $(REP_DIR)/include/python/x86_32 diff --git a/libports/lib/mk/x86_64/libc-gen.mk b/libports/lib/mk/x86_64/libc-gen.mk new file mode 100644 index 000000000..31293d8f5 --- /dev/null +++ b/libports/lib/mk/x86_64/libc-gen.mk @@ -0,0 +1,18 @@ +include $(REP_DIR)/lib/mk/libc-gen.inc + +LIBC_GEN_AMD64_DIR = $(LIBC_DIR)/libc/amd64/gen + +FILTER_OUT_S += rfork_thread.S sigsetjmp.S +FILTER_OUT_C += _set_tp.c + +# +# Filter functions conflicting with libm +# +FILTER_OUT_S += fabs.S modf.S +FILTER_OUT_C += frexp.c + +SRC_S = $(filter-out $(FILTER_OUT_S),$(notdir $(wildcard $(LIBC_GEN_AMD64_DIR)/*.S))) +SRC_C += flt_rounds.c + +vpath %.c $(LIBC_GEN_AMD64_DIR) +vpath %.S $(LIBC_GEN_AMD64_DIR) diff --git a/libports/lib/mk/x86_64/python.mk b/libports/lib/mk/x86_64/python.mk new file mode 100644 index 000000000..123b6f398 --- /dev/null +++ b/libports/lib/mk/x86_64/python.mk @@ -0,0 +1,3 @@ +include $(REP_DIR)/lib/mk/python.inc + +INC_DIR += $(REP_DIR)/include/python/x86_64 diff --git a/libports/lib/mk/zlib.mk b/libports/lib/mk/zlib.mk new file mode 100644 index 000000000..079fae853 --- /dev/null +++ b/libports/lib/mk/zlib.mk @@ -0,0 +1,12 @@ +ZLIB = zlib-1.2.5 +ZLIB_DIR = $(REP_DIR)/contrib/$(ZLIB) +LIBS += libc +INC_DIR += $(ZLIB_DIR) +SRC_C = adler32.c compress.c crc32.c deflate.c gzclose.c \ + gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c \ + inftrees.c trees.c uncompr.c zutil.c +CC_WARN = + +vpath %.c $(ZLIB_DIR) + +SHARED_LIB = yes diff --git a/libports/ports/ffat.mk b/libports/ports/ffat.mk new file mode 100644 index 000000000..093cca1c7 --- /dev/null +++ b/libports/ports/ffat.mk @@ -0,0 +1,43 @@ +FFAT = ff007e +FFAT_ZIP = $(FFAT).zip + +# +# Download archive from genode.org instead of the original location +# 'http://elm-chan.org/fsw/ff/ff007e.zip' because the elm-chan webserver +# does not like wget. +# +FFAT_URL = http://genode.org/files/$(FFAT_ZIP) + +# +# Interface to top-level prepare Makefile +# +PORTS += ffat-0.07e + +prepare-ffat: $(CONTRIB_DIR)/$(FFAT) + +$(CONTRIB_DIR)/$(FFAT): clean-ffat + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(FFAT_ZIP): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(FFAT_URL) && touch $@ + +FFAT_HEADERS := ff.h diskio.h integer.h ffconf.h +FFAT_DELETE := diskio.c +FFAT_PATCH := config.patch + +include/ffat: + $(VERBOSE)mkdir -p $@ + +$(CONTRIB_DIR)/$(FFAT): $(DOWNLOAD_DIR)/$(FFAT_ZIP) include/ffat + $(VERBOSE)unzip $< -d $(CONTRIB_DIR)/$(FFAT) && touch $@ + @# create symbolic links for public headers from contrib dir + $(VERBOSE)for i in $(FFAT_HEADERS); do \ + ln -sf ../../$(CONTRIB_DIR)/$(FFAT)/src/$$i include/ffat/; done + $(VERBOSE)rm $(addprefix $(CONTRIB_DIR)/$(FFAT)/src/,$(FFAT_DELETE)) + $(VERBOSE)patch -d $(CONTRIB_DIR)/$(FFAT) -p1 -i ../../src/lib/ffat/config.patch + +clean-ffat: + $(VERBOSE)rm -f $(addprefix include/ffat/,$(FFAT_HEADERS)) + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(FFAT) diff --git a/libports/ports/freetype.mk b/libports/ports/freetype.mk new file mode 100644 index 000000000..fbbab8190 --- /dev/null +++ b/libports/ports/freetype.mk @@ -0,0 +1,32 @@ +FREETYPE = freetype-2.3.9 +FREETYPE_TGZ = $(FREETYPE).tar.gz +FREETYPE_URL = http://mirrors.zerg.biz/nongnu/freetype/$(FREETYPE_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(FREETYPE) + +prepare-freetype: $(CONTRIB_DIR)/$(FREETYPE) include/freetype include/ft2build.h + +$(CONTRIB_DIR)/$(FREETYPE): clean-freetype + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(FREETYPE_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(FREETYPE_URL) && touch $@ + +$(CONTRIB_DIR)/$(FREETYPE): $(DOWNLOAD_DIR)/$(FREETYPE_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + +include/freetype: + $(VERBOSE)ln -s ../$(CONTRIB_DIR)/$(FREETYPE)/include/freetype $@ + +include/ft2build.h: + $(VERBOSE)ln -s ../$(CONTRIB_DIR)/$(FREETYPE)/$@ $@ + +clean-freetype: + $(VERBOSE)rm -rf include/freetype + $(VERBOSE)rm -f include/ft2build.h + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(FREETYPE) diff --git a/libports/ports/gmp.mk b/libports/ports/gmp.mk new file mode 100644 index 000000000..f92044949 --- /dev/null +++ b/libports/ports/gmp.mk @@ -0,0 +1,39 @@ +GMP = gmp-4.3.2 +GMP_TBZ2 = $(GMP).tar.bz2 +GMP_URL = ftp://ftp.gmplib.org/pub/$(GMP)/$(GMP_TBZ2) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(GMP) + +GMP_INCLUDES = include/gmp/gmp-impl.h +GMP_INCLUDES += include/gmp/x86_32/gmp-mparam.h +GMP_M4_SRC += src/lib/gmp/mpn/asm-defs.m4 + +prepare-gmp: $(CONTRIB_DIR)/$(GMP) $(GMP_INCLUDES) $(GMP_M4_SRC) + +$(CONTRIB_DIR)/$(GMP): clean-gmp + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(GMP_TBZ2): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(GMP_URL) && touch $@ + +$(CONTRIB_DIR)/$(GMP): $(DOWNLOAD_DIR)/$(GMP_TBZ2) + $(VERBOSE)tar xfj $< -C $(CONTRIB_DIR) && touch $@ + +include/gmp/gmp-impl.h: + $(VERBOSE)ln -sf ../../$(CONTRIB_DIR)/$(GMP)/gmp-impl.h $@ + +include/gmp/x86_32/gmp-mparam.h: $(CONTRIB_DIR)/$(GMP)/mpn/x86/gmp-mparam.h + $(VERBOSE)ln -sf ../../../$< $@ + +src/lib/gmp/mpn/asm-defs.m4: $(CONTRIB_DIR)/$(GMP)/mpn/asm-defs.m4 + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)ln -sf ../../../../$< $@ + +clean-gmp: + $(VERBOSE)rm -f $(GMP_INCLUDES) + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(GMP) diff --git a/libports/ports/jpeg.mk b/libports/ports/jpeg.mk new file mode 100644 index 000000000..52560adbe --- /dev/null +++ b/libports/ports/jpeg.mk @@ -0,0 +1,24 @@ +JPEG = jpeg-7 +JPEG_TGZ = jpegsrc.v7.tar.gz +JPEG_URL = http://www.ijg.org/files/$(JPEG_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(JPEG) + +prepare-jpeg: $(CONTRIB_DIR)/$(JPEG) + +$(CONTRIB_DIR)/$(JPEG): clean-jpeg + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(JPEG_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(JPEG_URL) && touch $@ + +$(CONTRIB_DIR)/$(JPEG): $(DOWNLOAD_DIR)/$(JPEG_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + +clean-jpeg: + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(JPEG) diff --git a/libports/ports/libc.mk b/libports/ports/libc.mk new file mode 100644 index 000000000..65681cbad --- /dev/null +++ b/libports/ports/libc.mk @@ -0,0 +1,468 @@ +LIBC := libc-8.2.0 + +# +# Interface to top-level prepare Makefile +# +PORTS += $(LIBC) + +# +# Subdirectories to check out from FreeBSD's Subversion repository +# +LIBC_SVN_BASE = http://svn.freebsd.org/base/release/8.2.0 + +LIBC_CONTRIB_SUB_DIRS = libc include sys_sys sys_netinet sys_netinet6 \ + sys_bsm sys_vm sys_arm sys_i386 sys_amd64 \ + msun gdtoa + +LIBC_SVN_libc = lib/libc +LIBC_SVN_include = include +LIBC_SVN_sys_sys = sys/sys +LIBC_SVN_sys_netinet = sys/netinet +LIBC_SVN_sys_netinet6 = sys/netinet6 +LIBC_SVN_sys_bsm = sys/bsm +LIBC_SVN_sys_vm = sys/vm +LIBC_SVN_sys_arm = sys/arm/include +LIBC_SVN_sys_i386 = sys/i386/include +LIBC_SVN_sys_amd64 = sys/amd64/include +LIBC_SVN_msun = lib/msun +LIBC_SVN_gdtoa = contrib/gdtoa + +LIBC_DIRS_TO_CHECKOUT = $(addprefix $(CONTRIB_DIR)/$(LIBC)/,$(LIBC_CONTRIB_SUB_DIRS)) + +$(LIBC_DIRS_TO_CHECKOUT): + $(ECHO) "checking out '$(LIBC_SVN_$(notdir $@))' to '$@'" + $(VERBOSE)mkdir -p $(CONTRIB_DIR)/$(LIBC) + $(VERBOSE)svn export $(LIBC_SVN_BASE)/$(LIBC_SVN_$(notdir $@)) $@ + +checkout-libc: $(LIBC_DIRS_TO_CHECKOUT) + +# +# Files coming from the include directory +# +LIBC_IMPORT_INCLUDES = include/libc/strings.h \ + include/libc/limits.h \ + include/libc/string.h \ + include/libc/ctype.h \ + include/libc/_ctype.h \ + include/libc/runetype.h \ + include/libc/stdlib.h \ + include/libc/stdio.h \ + include/libc/signal.h \ + include/libc/unistd.h \ + include/libc/wchar.h \ + include/libc/time.h \ + include/libc/sysexits.h \ + include/libc/arpa/inet.h \ + include/libc/arpa/nameser.h \ + include/libc/arpa/nameser_compat.h \ + include/libc/resolv.h \ + include/libc/wctype.h \ + include/libc/fcntl.h \ + include/libc/locale.h \ + include/libc/langinfo.h \ + include/libc/regex.h \ + include/libc/paths.h \ + include/libc/inttypes.h \ + include/libc/fstab.h \ + include/libc/netdb.h \ + include/libc/ar.h \ + include/libc/stdint.h \ + include/libc/ieeefp.h + +# +# Files from include directory needed for stdlib +# +# We have to make sure to shadow all gcc headers to avoid conflicts. +# +LIBC_IMPORT_INCLUDES += include/libc/pthread.h \ + include/libc/sched.h \ + include/libc/err.h \ + include/libc/getopt.h \ + include/libc/search.h \ + include/libc/ktrace.h \ + include/libc/varargs.h \ + include/libc/stddef.h \ + include/libc/stdbool.h \ + include/libc/assert.h \ + include/libc/monetary.h \ + include/libc/printf.h \ + include/libc/math.h + +# +# Files from include directory needed for gen lib +# +LIBC_IMPORT_INCLUDES += include/libc/vis.h \ + include/libc/libgen.h \ + include/libc/dirent.h \ + include/libc/dlfcn.h \ + include/libc/link.h \ + include/libc/fmtmsg.h \ + include/libc/fnmatch.h \ + include/libc/fts.h \ + include/libc/ftw.h \ + include/libc/db.h \ + include/libc/grp.h \ + include/libc/nsswitch.h \ + include/libc/pthread_np.h \ + include/libc/syslog.h \ + include/libc/pwd.h \ + include/libc/utmp.h \ + include/libc/ttyent.h \ + include/libc/stringlist.h \ + include/libc/glob.h \ + include/libc/termios.h \ + include/libc/a.out.h \ + include/libc/elf-hints.h \ + include/libc/nlist.h \ + include/libc/spawn.h \ + include/libc/readpassphrase.h \ + include/libc/semaphore.h \ + include/libc/_semaphore.h \ + include/libc/setjmp.h \ + include/libc/elf.h \ + include/libc/ulimit.h \ + include/libc/utime.h \ + include/libc/wordexp.h + +# +# Files from sys/vm needed for gen lib +# +LIBC_IMPORT_INCLUDES += include/libc/vm/vm_param.h \ + include/libc/vm/vm.h \ + include/libc/vm/pmap.h + +# +# Files coming from the sys/netinet and sys/netinet6 directories +# +LIBC_IMPORT_INCLUDES += include/libc/netinet/in.h \ + include/libc/netinet6/in6.h \ + include/libc/netinet/tcp.h + +# +# Files coming from the sys/sys directory +# +LIBC_IMPORT_INCLUDES += include/libc/sys/_types.h \ + include/libc/sys/limits.h \ + include/libc/sys/cdefs.h \ + include/libc/sys/_null.h \ + include/libc/sys/types.h \ + include/libc/sys/_pthreadtypes.h \ + include/libc/sys/syslimits.h \ + include/libc/sys/select.h \ + include/libc/sys/_sigset.h \ + include/libc/sys/_timeval.h \ + include/libc/sys/timespec.h \ + include/libc/sys/_timespec.h \ + include/libc/sys/stat.h \ + include/libc/sys/signal.h \ + include/libc/sys/unistd.h \ + include/libc/sys/time.h \ + include/libc/sys/param.h \ + include/libc/sys/stdint.h \ + include/libc/errno.h + +# +# Files from sys/sys needed for stdlib and stdio and gen lib +# +LIBC_IMPORT_INCLUDES += include/libc/sys/queue.h \ + include/libc/sys/mman.h \ + include/libc/sys/stddef.h \ + include/libc/sys/sysctl.h \ + include/libc/sys/uio.h \ + include/libc/sys/_iovec.h \ + include/libc/sys/ktrace.h \ + include/libc/sys/ioctl.h \ + include/libc/sys/ttycom.h \ + include/libc/sys/ioccom.h \ + include/libc/sys/filio.h \ + include/libc/sys/sockio.h \ + include/libc/sys/wait.h \ + include/libc/sys/file.h \ + include/libc/sys/fcntl.h \ + include/libc/sys/resource.h \ + include/libc/sys/disklabel.h \ + include/libc/sys/link_elf.h \ + include/libc/sys/endian.h \ + include/libc/sys/mount.h \ + include/libc/sys/ucred.h \ + include/libc/sys/dirent.h \ + include/libc/sys/cpuset.h \ + include/libc/sys/socket.h \ + include/libc/sys/un.h \ + include/libc/sys/ttydefaults.h \ + include/libc/sys/imgact_aout.h \ + include/libc/sys/elf32.h \ + include/libc/sys/elf64.h \ + include/libc/sys/elf_generic.h \ + include/libc/sys/elf_common.h \ + include/libc/sys/nlist_aout.h \ + include/libc/sys/ipc.h \ + include/libc/sys/sem.h \ + include/libc/sys/exec.h \ + include/libc/sys/_lock.h \ + include/libc/sys/_mutex.h \ + include/libc/sys/statvfs.h \ + include/libc/sys/ucontext.h \ + include/libc/sys/syslog.h \ + include/libc/sys/times.h \ + include/libc/sys/utsname.h \ + include/libc/sys/elf.h \ + include/libc/sys/mtio.h + +# +# Files coming from the sys/arm/include directory +# +LIBC_IMPORT_INCLUDES += include/libc-arm/machine/_types.h \ + include/libc-arm/machine/endian.h \ + include/libc-arm/machine/_limits.h \ + include/libc-arm/machine/signal.h \ + include/libc-arm/machine/trap.h \ + include/libc-arm/machine/_stdint.h \ + include/libc-arm/machine/pte.h \ + include/libc-arm/machine/cpuconf.h \ + include/libc-arm/machine/sysarch.h \ + include/libc-arm/machine/armreg.h \ + include/libc-arm/machine/ieee.h \ + include/libc-arm/machine/frame.h \ + include/libc-arm/machine/sigframe.h \ + include/libc-arm/machine/vm.h \ + include/libc-arm/stdarg.h \ + include/libc-arm/float.h + + +# +# Files coming from the sys/i386/include directory +# +LIBC_IMPORT_INCLUDES += include/libc-i386/machine/_types.h \ + include/libc-i386/machine/endian.h \ + include/libc-i386/machine/_limits.h \ + include/libc-i386/machine/signal.h \ + include/libc-i386/machine/trap.h \ + include/libc-i386/machine/_inttypes.h \ + include/libc-i386/machine/_stdint.h \ + include/libc-i386/machine/param.h \ + include/libc-i386/machine/vm.h \ + include/libc-i386/machine/specialreg.h \ + include/libc-i386/stdarg.h \ + include/libc-i386/float.h + +# +# Files coming from the sys/amd64/include directory +# +LIBC_IMPORT_INCLUDES += include/libc-amd64/machine/_types.h \ + include/libc-amd64/machine/endian.h \ + include/libc-amd64/machine/_limits.h \ + include/libc-amd64/machine/signal.h \ + include/libc-amd64/machine/trap.h \ + include/libc-amd64/machine/_inttypes.h \ + include/libc-amd64/machine/_stdint.h \ + include/libc-amd64/machine/param.h \ + include/libc-amd64/machine/vm.h \ + include/libc-amd64/machine/specialreg.h \ + include/libc-amd64/stdarg.h \ + include/libc-amd64/float.h + +# +# Files from sys/arm/include needed for stdlib and stdio +# +LIBC_IMPORT_INCLUDES += include/libc-arm/machine/cpufunc.h \ + include/libc-arm/machine/vmparam.h \ + include/libc-arm/machine/atomic.h \ + include/libc-arm/arith.h \ + include/libc-arm/_fpmath.h \ + +# +# Files from sys/i386/include needed for stdlib and stdio +# +LIBC_IMPORT_INCLUDES += include/libc-i386/machine/cpufunc.h \ + include/libc-i386/machine/vmparam.h \ + include/libc-i386/machine/atomic.h \ + include/libc-i386/arith.h \ + include/libc-i386/_fpmath.h \ + +# +# Files from sys/amd64/include needed for stdlib and stdio +# +LIBC_IMPORT_INCLUDES += include/libc-amd64/machine/cpufunc.h \ + include/libc-amd64/machine/vmparam.h \ + include/libc-amd64/machine/atomic.h \ + include/libc-amd64/arith.h \ + include/libc-amd64/_fpmath.h \ + +# +# Files from sys/arm/include needed for gen lib +# +LIBC_IMPORT_INCLUDES += include/libc-arm/machine/elf.h \ + include/libc-arm/machine/exec.h \ + include/libc-arm/machine/reloc.h \ + include/libc-arm/machine/pmap.h \ + include/libc-arm/machine/ucontext.h \ + include/libc-arm/machine/setjmp.h \ + include/libc-arm/machine/asm.h \ + include/libc-arm/machine/param.h \ + include/libc-arm/machine/_inttypes.h \ + include/libc-arm/machine/ieeefp.h \ + include/libc-arm/SYS.h + +# +# Files from sys/i386/include needed for gen lib +# +LIBC_IMPORT_INCLUDES += include/libc-i386/machine/elf.h \ + include/libc-i386/machine/exec.h \ + include/libc-i386/machine/reloc.h \ + include/libc-i386/machine/pmap.h \ + include/libc-i386/machine/ucontext.h \ + include/libc-i386/machine/setjmp.h \ + include/libc-i386/machine/asm.h \ + include/libc-i386/machine/ieeefp.h \ + include/libc-i386/SYS.h + +# +# Files from sys/amd64/include needed for gen lib +# +LIBC_IMPORT_INCLUDES += include/libc-amd64/machine/elf.h \ + include/libc-amd64/machine/exec.h \ + include/libc-amd64/machine/reloc.h \ + include/libc-amd64/machine/pmap.h \ + include/libc-amd64/machine/ucontext.h \ + include/libc-amd64/machine/setjmp.h \ + include/libc-amd64/machine/asm.h \ + include/libc-amd64/machine/ieeefp.h \ + include/libc-amd64/SYS.h + +# +# Files needed for math lib +# +LIBC_IMPORT_INCLUDES += include/libc/complex.h + +# +# Files from libc/arm needed for gdtoa lib +# +LIBC_IMPORT_INCLUDES += include/libc-arm/gd_qnan.h + +# +# Files from libc/i386 needed for gdtoa lib +# +LIBC_IMPORT_INCLUDES += include/libc-i386/gd_qnan.h + +# +# Files from libc/amd64 needed for gdtoa lib +# +LIBC_IMPORT_INCLUDES += include/libc-amd64/gd_qnan.h + +# +# Files from msun/arm needed for gdtoa lib +# +LIBC_IMPORT_INCLUDES += include/libc-arm/fenv.h + +# +# Files from msun/i387 needed for gdtoa lib +# +LIBC_IMPORT_INCLUDES += include/libc-i386/fenv.h + +# +# Files from msun/amd64 needed for gdtoa lib +# +LIBC_IMPORT_INCLUDES += include/libc-amd64/fenv.h + +# +# Files from sys/bsm for gen lib +# +LIBC_IMPORT_INCLUDES += include/libc/bsm/audit.h + +## +# Shortcut for creating a symlink +# +# \param $(1) prefix prepended to symlink origin, used for creating relative +# symlinks +# +libc_gen_symlink_subsub = $(VERBOSE)mkdir -p $(dir $@); ln -sf ../../$< $@ +libc_gen_symlink_subsubsub = $(VERBOSE)mkdir -p $(dir $@); ln -sf ../../../$< $@ + +include/libc/arpa/%.h: $(CONTRIB_DIR)/$(LIBC)/include/arpa/%.h + $(libc_gen_symlink_subsubsub) + +include/libc/%.h: $(CONTRIB_DIR)/$(LIBC)/include/%.h + $(libc_gen_symlink_subsub) + +include/libc/netinet/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_netinet/%.h + $(libc_gen_symlink_subsubsub) + +include/libc/netinet6/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_netinet6/%.h + $(libc_gen_symlink_subsubsub) + +include/libc/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_sys/%.h + $(libc_gen_symlink_subsub) + +include/libc/%.h: $(CONTRIB_DIR)/$(LIBC)/msun/src/%.h + $(libc_gen_symlink_subsub) + +include/libc-arm/%.h: $(CONTRIB_DIR)/$(LIBC)/msun/arm/%.h + $(libc_gen_symlink_subsub) + +include/libc-i386/%.h: $(CONTRIB_DIR)/$(LIBC)/msun/i387/%.h + $(libc_gen_symlink_subsub) + +include/libc-amd64/%.h: $(CONTRIB_DIR)/$(LIBC)/msun/amd64/%.h + $(libc_gen_symlink_subsub) + +include/libc/sys/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_sys/%.h + $(libc_gen_symlink_subsubsub) + +include/libc-arm/machine/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_arm/%.h + $(libc_gen_symlink_subsubsub) + +include/libc-i386/machine/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_i386/%.h + $(libc_gen_symlink_subsubsub) + +include/libc-amd64/machine/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_amd64/%.h + $(libc_gen_symlink_subsubsub) + +include/libc-arm/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_arm/%.h + $(libc_gen_symlink_subsub) + +include/libc-i386/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_i386/%.h + $(libc_gen_symlink_subsub) + +include/libc-amd64/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_amd64/%.h + $(libc_gen_symlink_subsub) + +include/libc-arm/%.h: $(CONTRIB_DIR)/$(LIBC)/libc/arm/%.h + $(libc_gen_symlink_subsub) + +include/libc-i386/%.h: $(CONTRIB_DIR)/$(LIBC)/libc/i386/%.h + $(libc_gen_symlink_subsub) + +include/libc-amd64/%.h: $(CONTRIB_DIR)/$(LIBC)/libc/amd64/%.h + $(libc_gen_symlink_subsub) + +include/libc/bsm/audit.h: $(CONTRIB_DIR)/$(LIBC)/sys_bsm/audit.h + $(libc_gen_symlink_subsubsub) + +include/libc/vm/%.h: $(CONTRIB_DIR)/$(LIBC)/sys_vm/%.h + $(libc_gen_symlink_subsubsub) + +apply_patches-libc: checkout-libc + $(VERBOSE)find ./src/lib/libc/patches/ -name "*.patch" |\ + xargs -ixxx sh -c "patch -p0 -r - -N -d $(CONTRIB_DIR)/$(LIBC) < xxx" || true + +# +# Use new make instance for symlink creation. Otherwise the implicit rules +# above do not work as expected (because the dependent names do not exist +# at the invokation time of the original make instance and are created +# as side effect of the 'LIBC_DIRS_TO_CHECKOUT' out rule). +# +create_include_symlinks-libc: checkout-libc + $(VERBOSE)make -s $(LIBC_IMPORT_INCLUDES) + +prepare-libc: create_include_symlinks-libc apply_patches-libc + +clean_include_symlinks-libc: + $(VERBOSE)find include -type l -delete + +clean_include_subdirs-libc: clean_include_symlinks-libc + $(VERBOSE)find include -type d -empty -delete + +clean-libc: clean_include_subdirs-libc + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(LIBC) + diff --git a/libports/ports/libdrm.mk b/libports/ports/libdrm.mk new file mode 100644 index 000000000..f0e306162 --- /dev/null +++ b/libports/ports/libdrm.mk @@ -0,0 +1,27 @@ +LIBDRM_VERSION = 2.4.21 +LIBDRM = libdrm-$(LIBDRM_VERSION) +LIBDRM_DIR = libdrm-$(LIBDRM_VERSION) +LIBDRM_TBZ2 = $(LIBDRM).tar.bz2 +LIBDRM_URL = http://dri.freedesktop.org/libdrm/$(LIBDRM_TBZ2) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(LIBDRM) + +prepare-libdrm: $(CONTRIB_DIR)/$(LIBDRM_DIR) + +$(CONTRIB_DIR)/$(LIBDRM_DIR): clean-libdrm + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(LIBDRM_TBZ2): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(LIBDRM_URL) && touch $@ + +$(CONTRIB_DIR)/$(LIBDRM_DIR): $(DOWNLOAD_DIR)/$(LIBDRM_TBZ2) + $(VERBOSE)tar xfj $< -C $(CONTRIB_DIR) && touch $@ + +clean-libdrm: + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(LIBDRM_DIR) + diff --git a/libports/ports/libpng.mk b/libports/ports/libpng.mk new file mode 100644 index 000000000..b104858e3 --- /dev/null +++ b/libports/ports/libpng.mk @@ -0,0 +1,32 @@ +LIBPNG = libpng-1.4.1 +LIBPNG_TGZ = libpng-1.4.1.tar.gz +LIBPNG_URL = http://prdownloads.sourceforge.net/libpng/$(LIBPNG_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(LIBPNG) + +prepare-libpng: $(CONTRIB_DIR)/$(LIBPNG) include/libpng + +$(CONTRIB_DIR)/$(LIBPNG): clean-libpng + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(LIBPNG_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(LIBPNG_URL) && touch $@ + +$(CONTRIB_DIR)/$(LIBPNG): $(DOWNLOAD_DIR)/$(LIBPNG_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + +LIBPNG_INCLUDES = pngconf.h png.h pngpriv.h + +include/libpng: + $(VERBOSE)mkdir -p $@ + $(VERBOSE)for i in $(LIBPNG_INCLUDES); do \ + ln -sf ../../$(CONTRIB_DIR)/$(LIBPNG)/$$i $@; done + +clean-libpng: + $(VERBOSE)rm -rf include/libpng + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(LIBPNG) diff --git a/libports/ports/lwip.mk b/libports/ports/lwip.mk new file mode 100644 index 000000000..e5f0d2faa --- /dev/null +++ b/libports/ports/lwip.mk @@ -0,0 +1,37 @@ +LWIP = lwip-1.3.2 +LWIP_ZIP = $(LWIP).zip +LWIP_URL = http://mirrors.zerg.biz/nongnu/lwip/$(LWIP_ZIP) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(LWIP) + +prepare-lwip: $(CONTRIB_DIR)/$(LWIP) include/lwip/lwip include/lwip/netif + +$(CONTRIB_DIR)/$(LWIP): clean-lwip + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(LWIP_ZIP): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(LWIP_URL) && touch $@ + +$(CONTRIB_DIR)/$(LWIP): $(DOWNLOAD_DIR)/$(LWIP_ZIP) + $(VERBOSE)unzip $< -d $(CONTRIB_DIR) && touch $@ + $(VERBOSE)patch -d $(CONTRIB_DIR) -p0 -i ../src/lib/lwip/libc_select_notify.patch + $(VERBOSE)patch -d $(CONTRIB_DIR) -p0 -i ../src/lib/lwip/errno.patch + +include/lwip/lwip: + $(VERBOSE)mkdir -p $@ + $(VERBOSE)ln -s $(addprefix ../../../, $(wildcard $(CONTRIB_DIR)/$(LWIP)/src/include/lwip/*.h)) -t $@ + $(VERBOSE)ln -s $(addprefix ../../../, $(wildcard $(CONTRIB_DIR)/$(LWIP)/src/include/ipv4/lwip/*.h)) -t $@ + +include/lwip/netif: + $(VERBOSE)mkdir -p $@ + $(VERBOSE)ln -s $(addprefix ../../../, $(wildcard $(CONTRIB_DIR)/$(LWIP)/src/include/netif/*.h)) -t $@ + +clean-lwip: + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(LWIP) + $(VERBOSE)rm -rf include/lwip/lwip + $(VERBOSE)rm -rf include/lwip/netif diff --git a/libports/ports/mesa.mk b/libports/ports/mesa.mk new file mode 100644 index 000000000..0cea4d1be --- /dev/null +++ b/libports/ports/mesa.mk @@ -0,0 +1,48 @@ +MESA_VERSION = 7.8.1 +MESA = MesaLib-$(MESA_VERSION) +MESA_DIR = Mesa-$(MESA_VERSION) +MESA_TGZ = $(MESA).tar.gz +MESA_URL = ftp://ftp.freedesktop.org/pub/mesa/7.8.1/$(MESA_TGZ) + +# +# Interface to top-level prepare Makefile +# +# Register Mesa port as lower case to be consistent with the +# other libraries. +# +PORTS += mesa-$(MESA_VERSION) + +MESA_INCLUDE_SYMLINKS = $(addprefix include/,GL KHR EGL/egl.h EGL/eglext.h) + +prepare-mesa: $(CONTRIB_DIR)/$(MESA_DIR) tool/mesa/glsl $(MESA_INCLUDE_SYMLINKS) + +$(CONTRIB_DIR)/$(MESA_DIR): clean-mesa + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(MESA_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(MESA_URL) && touch $@ + +$(CONTRIB_DIR)/$(MESA_DIR): $(DOWNLOAD_DIR)/$(MESA_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) + $(VERBOSE)patch -N -p0 -d $(CONTRIB_DIR)/$(MESA_DIR) < src/lib/gallium/p_state_config.patch + $(VERBOSE)touch $@ + +tool/mesa/glsl: + $(VERBOSE)make -C tool/mesa + +$(MESA_INCLUDE_SYMLINKS): + $(VERBOSE)ln -sf $(realpath $(CONTRIB_DIR)/$(MESA_DIR)/$@) $@ && touch $@ + +clean_tool_mesa: + $(VERBOSE)make -C tool/mesa clean + +clean_mesa_include_symlinks: + $(VERBOSE)rm -f $(MESA_INCLUDE_SYMLINKS) + +clean-mesa: clean_tool_mesa clean_mesa_include_symlinks + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(MESA_DIR) + + + diff --git a/libports/ports/mpfr.mk b/libports/ports/mpfr.mk new file mode 100644 index 000000000..02c98d465 --- /dev/null +++ b/libports/ports/mpfr.mk @@ -0,0 +1,35 @@ +MPFR = mpfr-3.0.0 +MPFR_TGZ = $(MPFR).tar.gz +MPFR_URL = http://www.mpfr.org/$(MPFR)/$(MPFR_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(MPFR) + +MPFR_INCLUDES = include/mpfr/mpfr.h include/mpfr/mparam.h + +prepare-mpfr: $(CONTRIB_DIR)/$(MPFR) $(MPFR_INCLUDES) + +$(CONTRIB_DIR)/$(MPFR): clean-mpfr + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(MPFR_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(MPFR_URL) && touch $@ + +$(CONTRIB_DIR)/$(MPFR): $(DOWNLOAD_DIR)/$(MPFR_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + +include/mpfr/mpfr.h: + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)ln -sf ../../$(CONTRIB_DIR)/$(MPFR)/mpfr.h $@ + +include/mpfr/mparam.h: + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)ln -sf ../../$(CONTRIB_DIR)/$(MPFR)/mparam_h.in $@ + +clean-mpfr: + $(VERBOSE)rm -f $(MPFR_INCLUDES) + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(MPFR) diff --git a/libports/ports/ncurses.mk b/libports/ports/ncurses.mk new file mode 100644 index 000000000..7d391f409 --- /dev/null +++ b/libports/ports/ncurses.mk @@ -0,0 +1,185 @@ +NCURSES := ncurses-5.9 +NCURSES_TGZ := $(NCURSES).tar.gz +NCURSES_URL := http://ftp.gnu.org/pub/gnu/ncurses/$(NCURSES_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(NCURSES) + +NCURSES_SYMLINKED_INC := nc_alloc.h nc_panel.h nc_tparm.h term_entry.h \ + tic.h hashed_db.h capdefaults.c +NCURSES_GENERATED_INC := curses.h ncurses_def.h ncurses_dll.h term.h \ + unctrl.h termcap.h parametrized.h hashsize.h \ + init_keytry.h keys.list make_keys MKterm.h.awk + +NCURSES_GENERATED_SRC := names.c unctrl.c fallback.c comp_captab.c codes.c \ + make_hash make_keys + +NCURSES_GEN_SYMLINKS := $(addprefix include/ncurses/,$(NCURSES_SYMLINKED_INC)) + +NCURSES_GEN_FILES := $(addprefix include/ncurses/,$(NCURSES_GENERATED_INC)) \ + $(addprefix src/lib/ncurses/,$(NCURSES_GENERATED_SRC)) + +prepare-ncurses: $(NCURSES_GEN_SYMLINKS) $(NCURSES_GEN_FILES) + +$(CONTRIB_DIR)/$(NCURSES): clean-ncurses + +$(NCURSES_GEN_SYMLINKS) $(NCURSES_GEN_FILES): $(CONTRIB_DIR)/$(NCURSES) src/lib/ncurses + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(NCURSES_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(NCURSES_URL) && touch $@ + +$(CONTRIB_DIR)/$(NCURSES): $(DOWNLOAD_DIR)/$(NCURSES_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + +src/lib/ncurses: + $(VERBOSE)mkdir -p $@ + +# +# Create symlinks to ncurses contrib dir +# +$(NCURSES_GEN_SYMLINKS): + $(VERBOSE)for i in $(NCURSES_SYMLINKED_INC); do \ + ln -sf ../../$(CONTRIB_DIR)/$(NCURSES)/include/$$i include/ncurses/$$i; done + +# +# Produce generated includes +# + +NCURSES_SUBST := \ + "@NCURSES_MAJOR@/5" \ + "@NCURSES_MINOR@/9" \ + "@NCURSES_PATCH@/20110404" \ + "@NCURSES_MOUSE_VERSION@/1" \ + "@NCURSES_CONST@/\/*nothing*\/" \ + "@NCURSES_INLINE@/inline" \ + "@NCURSES_OPAQUE@/0" \ + "@NCURSES_INTEROP_FUNCS@/0" \ + "@NCURSES_SIZE_T@/short" \ + "@NCURSES_TPARM_VARARGS@/1" \ + "@NCURSES_CH_T@/chtype" \ + "@NCURSES_LIBUTF8@/0" \ + "@NCURSES_OSPEED@/short" \ + "@NCURSES_WCHAR_T@/0" \ + "@NCURSES_WINT_T@/0" \ + "@NCURSES_SBOOL@/char" \ + "@NCURSES_XNAMES@/1" \ + "@HAVE_TERMIOS_H@/1" \ + "@HAVE_TCGETATTR@/1" \ + "@NCURSES_CCHARW_MAX@/5" \ + "@NCURSES_EXT_COLORS@/0" \ + "@NCURSES_EXT_FUNCS@/1" \ + "@NCURSES_SP_FUNCS@/0" \ + "@NCURSES_OK_WCHAR_T@/" \ + "@NCURSES_WRAP_PREFIX@/_nc_" \ + "@cf_cv_header_stdbool_h@/1" \ + "@cf_cv_enable_opaque@/NCURSES_OPAQUE" \ + "@cf_cv_enable_reentrant@/0" \ + "@cf_cv_enable_lp64@/0" \ + "@cf_cv_typeof_chtype@/long" \ + "@cf_cv_typeof_mmask_t@/long" \ + "@cf_cv_type_of_bool@/unsigned char" \ + "@cf_cv_1UL@/1UL" \ + "@USE_CXX_BOOL@/defined(__cplusplus)" \ + "@BROKEN_LINKER@/0" \ + "@NEED_WCHAR_H@/0" \ + "@GENERATED_EXT_FUNCS@/generated" \ + "@HAVE_TERMIO_H@/1" \ + "@HAVE_VSSCANF@/1" + +NCURSES_INCLUDE_DIR := $(CONTRIB_DIR)/$(NCURSES)/include + +NCURSES_SRC_DIR := $(CONTRIB_DIR)/$(NCURSES)/ncurses + +apply_substitutions = $(VERBOSE)for i in $(NCURSES_SUBST); do sed -i "s/$$i/g" $(1); done + +# +# Generate files in 'include/ncurses/' +# + +include/ncurses/curses.h: + $(VERBOSE)cp $(CONTRIB_DIR)/$(NCURSES)/include/curses.h.in $@ + $(call apply_substitutions,$@) + $(VERBOSE)AWK=mawk sh $(NCURSES_INCLUDE_DIR)/MKkey_defs.sh $(NCURSES_INCLUDE_DIR)/Caps >> $@ + $(VERBOSE)cat $(NCURSES_INCLUDE_DIR)/curses.tail >> $@ + +include/ncurses/ncurses_def.h: + $(VERBOSE)AWK=mawk sh $(NCURSES_INCLUDE_DIR)/MKncurses_def.sh $(NCURSES_INCLUDE_DIR)/ncurses_defs > $@ + +include/ncurses/parametrized.h: + $(VERBOSE)AWK=mawk sh $(NCURSES_INCLUDE_DIR)/MKparametrized.sh $(NCURSES_INCLUDE_DIR)/Caps > $@ + +include/ncurses/hashsize.h: $(NCURSES_INCLUDE_DIR)/MKhashsize.sh + $(VERBOSE)AWK=mawk sh $< $(NCURSES_INCLUDE_DIR)/Caps > $@ + +include/ncurses/keys.list: + $(VERBOSE)AWK=mawk sh $(NCURSES_SRC_DIR)/tinfo/MKkeys_list.sh $(NCURSES_INCLUDE_DIR)/Caps | sort > $@ + +include/ncurses/init_keytry.h: src/lib/ncurses/make_keys include/ncurses/keys.list + $(VERBOSE)src/lib/ncurses/make_keys include/ncurses/keys.list > $@ + +include/ncurses/term.h: include/ncurses/MKterm.h.awk + $(VERBOSE)mawk -f $< $(NCURSES_INCLUDE_DIR)/Caps > $@ + +include/ncurses/MKterm.h.awk: $(NCURSES_INCLUDE_DIR)/MKterm.h.awk.in + $(VERBOSE)cp $< $@ + $(call apply_substitutions,$@) + +include/ncurses/ncurses_dll.h: $(NCURSES_INCLUDE_DIR)/ncurses_dll.h.in + $(VERBOSE)cp $< $@ + $(call apply_substitutions,$@) + +include/ncurses/termcap.h: $(NCURSES_INCLUDE_DIR)/termcap.h.in + $(VERBOSE)cp $< $@ + $(call apply_substitutions,$@) + +include/ncurses/unctrl.h: $(NCURSES_INCLUDE_DIR)/unctrl.h.in + $(VERBOSE)cp $< $@ + $(call apply_substitutions,$@) + +# +# Generate files in 'src/lib/ncurses/' +# + +src/lib/ncurses/names.c: + $(VERBOSE)mawk -f $(NCURSES_SRC_DIR)/tinfo/MKnames.awk bigstrings=1 $(NCURSES_INCLUDE_DIR)/Caps > $@ + +src/lib/ncurses/codes.c: + $(VERBOSE)mawk -f $(NCURSES_SRC_DIR)/tinfo/MKcodes.awk bigstrings=1 $(NCURSES_INCLUDE_DIR)/Caps > $@ + +src/lib/ncurses/fallback.c: $(NCURSES_SRC_DIR)/tinfo/MKfallback.sh + $(VERBOSE)sh -e $< x $(CONTRIB_DIR)/$(NCURSES)/misc/terminfo.src tic linux > $@ + #$(VERBOSE)sh -e $< /usr/share/terminfo $(NCURSES_SRC_DIR)/misc/terminfo.src /usr/bin/tic > $@ + +src/lib/ncurses/unctrl.c: + $(VERBOSE)echo | mawk -f $(NCURSES_SRC_DIR)/base/MKunctrl.awk bigstrings=1 > $@ + +src/lib/ncurses/comp_captab.c: src/lib/ncurses/make_hash + $(VERBOSE)cd $(dir $@); sh -e $(realpath $(NCURSES_SRC_DIR))/tinfo/MKcaptab.sh mawk 1 $(realpath $(NCURSES_SRC_DIR))/tinfo/MKcaptab.awk $(realpath $(NCURSES_INCLUDE_DIR))/Caps > $(notdir $@) + +src/lib/ncurses/make_keys: $(NCURSES_SRC_DIR)/tinfo/make_keys.c + $(VERBOSE)$(CC) -o $@ -DHAVE_CONFIG_H -Iinclude/ncurses -Isrc/lib/ncurses -I$(NCURSES_SRC_DIR) $< + +src/lib/ncurses/make_hash: $(NCURSES_SRC_DIR)/tinfo/make_hash.c + $(VERBOSE)$(CC) -o $@ -DHAVE_CONFIG_H -Iinclude/ncurses -Isrc/lib/ncurses -I$(NCURSES_SRC_DIR) $< + +src/lib/ncurses/make_keys: src/lib/ncurses/names.c + + +# +# Clean rules +# + +clean-ncurses: clean_ncurses_symlinks clean_ncurses_gen_files + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(NCURSES) + +clean_ncurses_symlinks: + $(VERBOSE)rm -f $(NCURSES_GEN_SYMLINKS) + +clean_ncurses_gen_files: + $(VERBOSE)rm -f $(NCURSES_GEN_FILES) + diff --git a/libports/ports/python.mk b/libports/ports/python.mk new file mode 100644 index 000000000..b7cdb9f76 --- /dev/null +++ b/libports/ports/python.mk @@ -0,0 +1,33 @@ +PYTHON = python-2.6.4 +PYTHON_TGZ = Python-2.6.4.tgz +PYTHON_URL = http://www.python.org/ftp/python/2.6.4/$(PYTHON_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(PYTHON) + +prepare-python: $(CONTRIB_DIR)/$(PYTHON) include/python2.6 + +$(CONTRIB_DIR)/$(PYTHON): clean-python + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(PYTHON_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(PYTHON_URL) && touch $@ + +$(CONTRIB_DIR)/$(PYTHON): $(DOWNLOAD_DIR)/$(PYTHON_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) + @# rename Python subdirectory to lower case to be consistent + @# with the other libs + $(VERBOSE)mv $(CONTRIB_DIR)/Python-2.6.4 $@ + $(VERBOSE)touch $@ + $(VERBOSE)patch -p0 -i src/lib/python/posixmodule.patch + +include/python2.6: + $(VERBOSE)ln -s ../$(CONTRIB_DIR)/$(PYTHON)/Include $@ + +clean-python: + $(VERBOSE)rm -rf include/python2.6 + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(PYTHON) diff --git a/libports/ports/readline.mk b/libports/ports/readline.mk new file mode 100644 index 000000000..bbf5528b5 --- /dev/null +++ b/libports/ports/readline.mk @@ -0,0 +1,30 @@ +READLINE = readline-6.0 +READLINE_TGZ = $(READLINE).tar.gz +READLINE_URL = ftp://ftp.gnu.org/gnu/readline/$(READLINE_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(READLINE) + +prepare-readline: $(CONTRIB_DIR)/$(READLINE) + +$(CONTRIB_DIR)/$(READLINE): clean-readline + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(READLINE_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(READLINE_URL) && touch $@ + +READLINE_HEADERS := rlstdc.h rltypedefs.h keymaps.h tilde.h + +$(CONTRIB_DIR)/$(READLINE): $(DOWNLOAD_DIR)/$(READLINE_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + @# create symbolic links for public headers from contrib dir + $(VERBOSE)for i in $(READLINE_HEADERS); do \ + ln -sf ../../$(CONTRIB_DIR)/$(READLINE)/$$i include/readline/; done + +clean-readline: + $(VERBOSE)rm -f $(addprefix include/readline/,$(READLINE_HEADERS)) + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(READLINE) diff --git a/libports/ports/sdl.mk b/libports/ports/sdl.mk new file mode 100644 index 000000000..8ae569564 --- /dev/null +++ b/libports/ports/sdl.mk @@ -0,0 +1,41 @@ +SDL_VERSION = 1.2.13 +SDL = SDL-$(SDL_VERSION) +SDL_TGZ = $(SDL).tar.gz +SDL_URL = http://www.libsdl.org/release/$(SDL_TGZ) + +# +# Interface to top-level prepare Makefile +# +# Register SDL port as lower case to be consitent with the +# other libraries. +# +PORTS += sdl-$(SDL_VERSION) + +prepare-sdl: $(CONTRIB_DIR)/$(SDL) include/SDL + +$(CONTRIB_DIR)/$(SDL): clean-sdl + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(SDL_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(SDL_URL) && touch $@ + +$(CONTRIB_DIR)/$(SDL): $(DOWNLOAD_DIR)/$(SDL_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + $(VERBOSE)rm -f $@/include/SDL_config.h + $(VERBOSE)patch -p0 -i src/lib/sdl/SDL_video.patch + +# +# Install SDL headers +# +include/SDL: + $(VERBOSE)mkdir -p $@ + $(VERBOSE)for i in `find $(CONTRIB_DIR)/$(SDL)/include -name *.h`; do \ + ln -fs ../../$$i include/SDL/; done + $(VERBOSE)ln -fs ../../src/lib/sdl/SDL_config.h $@ + $(VERBOSE)ln -fs ../../src/lib/sdl/SDL_config_genode.h $@ + +clean-sdl: + $(VERBOSE)rm -rf include/SDL + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(SDL) diff --git a/libports/ports/zlib.mk b/libports/ports/zlib.mk new file mode 100644 index 000000000..03f8eef62 --- /dev/null +++ b/libports/ports/zlib.mk @@ -0,0 +1,32 @@ +ZLIB = zlib-1.2.5 +ZLIB_TGZ = $(ZLIB).tar.gz +ZLIB_URL = http://zlib.net/$(ZLIB_TGZ) + +# +# Interface to top-level prepare Makefile +# +PORTS += $(ZLIB) + +prepare-zlib: $(CONTRIB_DIR)/$(ZLIB) include/zlib + +$(CONTRIB_DIR)/$(ZLIB):clean-zlib + +# +# Port-specific local rules +# +$(DOWNLOAD_DIR)/$(ZLIB_TGZ): + $(VERBOSE)wget -c -P $(DOWNLOAD_DIR) $(ZLIB_URL) && touch $@ + +$(CONTRIB_DIR)/$(ZLIB): $(DOWNLOAD_DIR)/$(ZLIB_TGZ) + $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ + +ZLIB_INCLUDES = zconf.h zlib.h + +include/zlib: + $(VERBOSE)mkdir -p $@ + $(VERBOSE)for i in $(ZLIB_INCLUDES); do \ + ln -sf ../../$(CONTRIB_DIR)/$(ZLIB)/$$i $@; done + +clean-zlib: + $(VERBOSE)rm -rf include/zlib + $(VERBOSE)rm -rf $(CONTRIB_DIR)/$(ZLIB) diff --git a/libports/run/eglgears.run b/libports/run/eglgears.run new file mode 100644 index 000000000..8476ba5f6 --- /dev/null +++ b/libports/run/eglgears.run @@ -0,0 +1,130 @@ +if {![have_spec x86]} { + puts "Run script is only supported on x86"; exit 0 } + +build { + core init + drivers/timer + server/nitpicker server/nit_fb + app/launchpad + app/eglgears + drivers/framebuffer drivers/pci drivers/input + lib/gallium +} + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + +} + +append_if [have_spec sdl] config { + + + + + + + } + +append_if [have_spec pci] config { + + + + } + +append_if [have_spec vesa] config { + + + + } + +append_if [have_spec ps2] config { + + + + } + +append config { + + + + + + + + + + + + + 100Minit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +set boot_modules { + core init ld.lib.so timer nitpicker nit_fb + launchpad eglgears + gallium.lib.so libc.lib.so libm.lib.so libc_log.lib.so +} + +lappend_if [have_spec linux] boot_modules fb_sdl +lappend_if [have_spec pci] boot_modules pci_drv +lappend_if [have_spec vesa] boot_modules vesa_drv +lappend_if [have_spec ps2] boot_modules ps2_drv +lappend_if [have_spec i915] boot_modules gallium-i915.lib.so + +build_boot_image $boot_modules + +append qemu_args " -m 768" + +run_genode_until forever + diff --git a/libports/run/libc_ffat.run b/libports/run/libc_ffat.run new file mode 100644 index 000000000..5cfe04433 --- /dev/null +++ b/libports/run/libc_ffat.run @@ -0,0 +1,126 @@ +# +# \brief Test for using the libc_ffat plugin +# \author Christian Prochaska +# \date 2011-05-27 +# + +if {[catch { exec which mkfs.vfat } ]} { + puts stderr "Error: mkfs.vfat not installed, aborting test"; exit } + +if {[have_spec linux]} { + puts "Run script does not support this platform"; exit } + +# +# Build +# + +build { + core init + drivers/pci + drivers/atapi + drivers/timer + drivers/sd_card + test/libc_ffat +} + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + +} + +append_if [have_spec pci] config { + + + + + + + + + +} + +append_if [have_spec pl180] config { + + + + +} + +append config { + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init timer + ld.lib.so libc.lib.so libc_log.lib.so libc_ffat.lib.so + test-libc_ffat +} + +append_if [have_spec pci] boot_modules { pci_drv atapi_drv } +append_if [have_spec pl180] boot_modules { sd_card_drv } + +build_boot_image $boot_modules + +# +# Execute test case +# + +set disk_image "bin/test.hda" +set cmd "dd if=/dev/zero of=$disk_image bs=1024 count=65536" +puts "creating disk image: $cmd" +catch { exec sh -c $cmd } + +set cmd "mkfs.vfat -F32 $disk_image" +puts "formating disk image with vfat file system: $cmd" +catch { exec sh -c $cmd } + +# +# Qemu +# +append qemu_args " -m 128 -nographic " +append_if [have_spec pci] qemu_args " -hda $disk_image -boot order=d " +append_if [have_spec pl180] qemu_args " -drive file=$disk_image,if=sd,cache=writeback " + +run_genode_until {.*child exited with exit value 0.*} 60 + +exec rm -f $disk_image + +puts "\ntest succeeded\n" + +# vi: set ft=tcl : diff --git a/libports/run/lwip.run b/libports/run/lwip.run new file mode 100644 index 000000000..1c05ec788 --- /dev/null +++ b/libports/run/lwip.run @@ -0,0 +1,136 @@ +# +# \brief Test for using the lwIP TCP/IP stack +# \author Norman Feske +# \date 2011-05-22 +# +# This test case executes a small HTTP server on Genode running on qemu. When +# the HTTP server is up, a HTTP request to the server is performed using +# 'lynx'. The response is validated against a known pattern. +# +# The test uses qemu's "-net user" option, redirecting Genode's port 80 to the +# host's port 5555. Consequently, it cannot be executed on non-qemu test +# environments (i.e., the test won't work with the Linux version of Genode). +# +# Please make sure to include a nic driver in your build configuration. E.g., +# on the x86 platform, you may enable the 'linux_drivers' repository. +# + +# +# TODO: Add support for Linux via user-level networking (using the +# tun/tap proxy driver at os/src/drivers/nic/linux) +# +if {[have_spec linux]} { + puts "Run script does not support Linux."; exit 0 } + +# +# XXX: Currently, we have no NIC driver for 64-bit +# +if {[have_spec 64bit]} { + puts "Run script does not support 64-bit platforms."; exit 0 } + +requires_installation_of lynx + +# +# Build +# + +build { + core init + drivers/pci drivers/timer drivers/nic + test/lwip/http_srv +} + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + +append_if [have_spec pci] config { + + + + } + +append config { + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init timer + nic_drv + ld.lib.so libc.lib.so libc_log.lib.so lwip.lib.so test-lwip_httpsrv +} + +# platform-specific modules +lappend_if [have_spec pci] boot_modules pci_drv + +build_boot_image $boot_modules + +# +# Execute test case +# + +# qemu config +append qemu_args " -m 128 -nographic " + +append_if [have_spec x86] qemu_args " -net nic,model=e1000 " +append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 " + +append qemu_args " -net user -redir tcp:5555::80 " + +run_genode_until {.*Start the server loop \.\.\..*} 30 + +set uri "http://localhost:5555/" + +puts "http server is up, try to query website $uri" + +set website [exec lynx -dump $uri] + +puts "response:\n$website" + +if {![regexp {Welcome to our lwIP HTTP server!} $website dummy]} { + puts stderr "Query returned unexpected website" + exit 2; +} + +puts "test succeeded" + +# vi: set ft=tcl : diff --git a/libports/run/lwip_lx.run b/libports/run/lwip_lx.run new file mode 100644 index 000000000..8ff85438e --- /dev/null +++ b/libports/run/lwip_lx.run @@ -0,0 +1,68 @@ +# +# Build +# + +build { + core + init drivers/timer drivers/nic + test/lwip/http_srv +} + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +set boot_modules { + core init timer nic_drv + ld.lib.so libc.lib.so libc_log.lib.so lwip.lib.so test-lwip_httpsrv +} + +build_boot_image $boot_modules + +# +# Execute test case +# + +run_genode_until forever + +# vi: set ft=tcl : diff --git a/libports/run/python.run b/libports/run/python.run new file mode 100644 index 000000000..53c19f1a6 --- /dev/null +++ b/libports/run/python.run @@ -0,0 +1,92 @@ +# +# \brief Test for running python +# \author Norman Feske +# \date 2011-11-22 +# + +if {![have_spec x86]} { + puts "Run script is only supported on x86"; exit 0 } + +# +# Build +# + +build { + core init + test/python +} + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + +