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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 $(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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

RC^- 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

$+c1o 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

)qA>?i4=-z%SrFfT+@Z43KQ%13vLx5D8;3QYj8X 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�z3X1p=c2dEbcb(l_?2c_eGY8K8-1>u0s7RsucLK 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 KUa4UH^8Wxb3f9U1 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 zcmeH~`)gZe7{}jQHgvc)O`GQ4+|x8oFG)Ln!*#X%B*#X%B*#X%B*#X%B*#X%B*@1`ez{uzk)*qi?(S65uj*QML29tBDaC}zN z8<|#xh9(LF{d=Bl?@2rXJ^UYkOjk@!EwSYE5=%`lJ(ZX^;fs$SKO7rdcne-!97-Mh zI-Gd+N?&B=tL~wRPlMsT?{@U2UT6))yPEC-xD z8dz;(=)d34CS4&qc8rb9zQ~f3OFD4B0{sX{$*EIfV)7&*0|I)0hW8w%f#e+Z$DW~{ z;VJ4G*e8IWz)wwqVF~=V!T*`Ju{&5(AIS4=6A!@1AOq_CA-F|kK*UO^sZ+u#Ki!9) z*7%zP!vgrJuDMqN_tl#E;8104YhJZKut`;Xu%C&IFS6M9!qf4I6K|sa0+Kvi^wgCbBRND}ez==j1uO)P~X~5zNQ||FI3fais1g)UE3Of z2M>0GkMLXJ}efZD%=?9U=U>%?wXe84Myjd>jBh}fn_K1 z{1#?<206fdpU>u;d$FSVOZY5-zE=*U=bji$AIw$+Gmmxl?a4#-3z7XG*q5`}Z|Nh< zK0?n^m9Jeu?K%Gk$ic3ymVus;DTW!Q>Wj`??vKq%e6AtiG?+R_$b|@97)~Fu$6`l$ zkK8kf-i8qB+Q!i9PUQUy%si3T9`6agPhgi^)lKJIp2pp-s>XX><8gh6`Nhz~W_WP5 zcVwDB)9`&=!1Jvbjz4=foH)>o*DRczTlW@SRSnEu;b-NZ`dMeC|5mxDfhua6$z9vR zR|(w9_^hDZv$gm!yBXv|AK!ov{CSR$34#}y-pI@={qfm+c(N`#Kj^6RGl#nl^6$4- z_-N&>fFFqLQYWsVPYW%garhS%H7 z6Iy~1)*OteTDqb$cz&OU4~y_&u`QICZ4O3vV^3fAc93^ocLh9n73|1;EplHaaCTmM zgSnjaCZ`Dg(>jAKPjB2>{OiJPaB#KmO4R)gyUTZTg}oZ=Joj#%d(_@3>B=16>Wp^J zR;&F6c=tVQuc*VU_i^c=x6$v}%wygO=4{=;zLI+hb-w}jDUH#ZuQl54d(G`~z*b(% z?3i_6HbL*>J!Sj-@65XeuoHTovtLK;r@+4h+AfIe-X}87z-)rfa&ErIVa~7h`#;&g z0eipJV9U=Hc8jx$VYZcEZ+mBzo6mTG_j;M7LO{Qx?oI)9KL>WdPH$tm!VXRb?Le)U zpxb=Do2+iIV}Iv)H#4lmOht?OoKI~_FH+~9ZDqSjX-(a7t)!p&zq z-`{=$_cN&XmfPbWhRoAZ@@oo8JH_Z94INwDwCmAv2W8EVX+qsVv$B5)I! z3B12yUz;ekS&;YbzrWqhz4o3zyFf?4n}G4c3bqgS8mz+Nn9Yf|Wr>z?p$a zqvx_@pjS0|b3WFb3YDc73X4<p7RS7W1p;l-|F7xMXuw^jW0oeiB Y0oeiB0oeiB0oeiB0oeiBfoZ+-iB^DDJC?oH3HF3uO~J^J+X`*qY#tftK^49Kh$?(< z@81>hufY39_5F9gs_(z6V%&~jl(%l&t2Ev|f&VuA8)pvBU|{$Mk94l` za3By}3k;2QsiNL&n=9>z*AL$p`PHlaOY7H;O4Wm}amVY(0ih2x%sk?r=M0$nC;nL~ zvkd>kYlGp@-W1w}Pnj#!#Mo3;EEG4>xs}G}h4S8`<&D?ctMxajeC4Kw9w1~u`sJBt ziGLoSTxjC_^Mj#S7b@%SmZ{9DNN39ech{@P@eOeJS9W<@&#&zgdO*Y7M#unh2H5My z)kAQ&P1RipE7(vhnSuY|JZ3<5i|z342Ea>*ZzHo)O;fuXpL3=lG)MMhJE_jaD�-+D98Js=3PG%oGW?=Qg^!%F40_LLZiMf0Y@4KwJTw{rJ+dB|`0Oizy@ENP^>|=^A zD4>_TzR(ux`a<P z<4g`CvR4MfW;U2dc+3!T!ff~%?AwHj&>5@k&ST8LrAh{uz+eaUeE|{ogJSiA{D5r}Dnd_LX?Gvz^MPK0Zh*nxJ;qEx|8hS(nG5XcabMtkty#%g z|G7;6E%aHD=gzPFi_geuaEO7yXV5V?$SfcaCWrsS!eV0f2{U2Ae;d9ZINiP+X6~=u zzO0XX!2mcpm&)SzxcwiSwk%I6ceijb> zM4N#}KT&=mpr4>W@aQKhK5^(LXfG^OvFImSFUSlu{Y1GLW;dFClI(?<0kxkjy-@o} G(hC4P^gU<* 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 zcmeHJ*-lzf6h#mPhdPuP20==&R4_C_V-qzsj!{9y7)k+|nv~i$MnHpA8*8gEYSKRV z>RYS!)x;0bKky%n!you+*D`37GN_cK58hdec_BjpkJ{(*sk#l5|~ABTsBKaY-%ejOhlZvtDy$;rt!uyuNR`WttG zV~lMB-lg#zsiu;TfM280eA?aJwV$7#`++bJ0it&VHPrg) zMNfEh4x&`aFVGPc9BRFE-x={ z#$}k8n9!`QuBxEF7pSENz354AuYD%-XxL7?qJFA+RnYk0Fp_U%>q9?t9K_Jy-z})7wTJ0e8 zmYtoQ+V%Bybtn{4j*N^b?gDD4r3by}3GW3YHl#|9aUtNWsHljccO7$cb0(~z7Hg=& z`>eqGEWZuNrG{F1(2Jh%b_415Kf;1C%yY7;sw(JoI-8c4mut~SwcG7hc6N3u0C`te zmm(P$BZpjSsHF$J;2EYj6Wro@mY<*h*=RHlq7F0m|C4Yytiri=kv~2UJ|{v>4!P7& zOAmO>B4@h3C!11#adGh^>U_DduwY<4E|*J*{$H#^V($35D99m~8fxhQ^S)#WGK@*l zv$t}&Jb1pHA(vpU2 zo7jWP+8HB^GxG7t4L`nDJhx28Pp7KeKZ>Vs|U=_+8HBIc@5Ky-`sSGuf=2q1qJUd7EAB^{QOg# zY1P-y13q(%F>=5SLHqccKC4I6LjCiyva%0217Dy{E$eM>Z&wiU+O>>PjiC(Yo+Su6mNMPN2FowSvg*Ujl`hk5w ZYZ1;k8Nl}pt;tQ#b5+BG??VUJfj`|!CFuYF literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/forward.rgba b/demo/src/app/scout/data/forward.rgba new file mode 100644 index 0000000000000000000000000000000000000000..7bd57e6fecb77ffe54bef458f0e8ad64e6e77f89 GIT binary patch literal 16384 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 LU{>JwQGp)-2xM%x literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/home.rgba b/demo/src/app/scout/data/home.rgba new file mode 100644 index 0000000000000000000000000000000000000000..e527d08fc0ea531940e777b86576de3596c38cc7 GIT binary patch literal 16384 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 z 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 literal 0 HcmV?d00001 diff --git a/demo/src/app/scout/data/kill_icon.rgba b/demo/src/app/scout/data/kill_icon.rgba new file mode 100644 index 0000000000000000000000000000000000000000..ce9f1268d90c836932d652e7c7a6e065e6caed27 GIT binary patch literal 1024 zcmb7D!41MN3`CTa8R!&9jK%={GeMuM&`*azVhR@FF4)$^6aLi;@`;@XD zuqLdBteas&%WgApX3?l#(OQ#El z8jXf=xm=|{An?0bEH=Oo)V~Y{LlyDm1fh7qi(+1}C5a-qh>$eW%lT zicI`mxL74aGeU(c8oGZHl58tX8W+yWQr;<1q)9>vp@mEXxAT zD1+>CxJ8Bk7d^>jGOc^P-qlzv#$(@BDwPULP)$9WQ3lx@+yjN*K)tQg>2BHWc7f-4 zE))u}i9}+VpqhF#qYSbc@O|#nJ3gfz&1^QC;Pd(Z^QlKOg?~Kv>GB!uU~ktw9?xnd z65;dt{7Si8W(lgPM>ERc`FD{2L*jhB#dQzQw;2wHgK&gatHqB-BW^exa#T}~W|V>d z68X|U8P_H76|-C_m5Mc+%^>zYMDGo3fSc&8VHS5d91^l8K>7agQ}sZv*T0WOqf)2S zxd-Plcmke+hy8wk3;$sknNP@Wkg#BFTP&8NY&N?;nM~*$()p98)2WnBr(apE)+0D~ j7kJ7Ri#?M4e*YJo*Pq~+YJ5KjTCJ8*2Cq?H`Td>&?&apq 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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 pqMvR1&RHxr&6yX_)U& 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 zcmeH}Sx+Kq7=_J@pkBD^#Zb6VRoIfwxZ%Q9C^!lt|v1hT>o9<2s_;yu2-#PDjU#P13@$ZKN-=zaTy|-$I!$G8T zVPRo-cX!ts8yoxez0P2>9i5$>p^c4=`2POBzq`BJdJle0O${-QuCA_NE|-gzN+nfM z6d%qs_11jpIkX1y`Mgmm6!f;Xwpe?6yXkZ~3vbDnZ0HVImKB|wo6}laS|)W}w?;=t zr)z6#Q*XtGHa^;kej8-Z&d%zst*tSz&1f__Juoof#ra}y!I!$h4wF4UKd*s3L3TJC zP6mTPb!cel7hD&AZNAjateK`6MbEup$H|UFBGZ9DKphzwQ9K?G_5b#oe97+U=!lSQ z_dBj>ng!hnvIhqT-8hed44U8ADBP9vf#fcf4EQdn>o*9GW1+Up2x|C#%Zuc&Z!%7X3+~~ z-|zP;s72wIiO;E*V90!}61ZG@E zzaKqEjpxx(ci|cx`u|h@B-`zFN7Cu^IKIQVTPDbc7G_OF*5r(3pXx?$*zYW7o@{0S z_l8kt{__8}f1a}crknn9#)NKi?5qjhwii*%2hS*Ula1>P*5J!0%x~tO_;S8EV`SsI zgu5kX>rTQ)&E75S_v73lf1(fM*tzfy4-bsBXogce;0z1_sHR9d`eB)kI>g(%c zx7@QzLqmgsog~hPnin+}YsjtW1G1vyeixn>!i)LKJ;z7w=j!U}sZ=WUu~Mmg zTwPr)EG;di*4EZ~wzszxeD1}Iwe<4xaxZGGa=GkVTwE-!tgOu7p5CpkEd`%@@LM;J z_l&IJUB~Ys3$JWrW8<^=hwdu$pl@eqr+jsF_4~=m$>)QEgYwzgS^nncrgVLMy?%On z`sMiecnR-${9U@cyIVg$KmX(C=x7!1x!c>@h5P&awad%PFNcSRm5YlD-aG&B@KC}3 Necs#KTle{V)}ML9>OJqdr|$6TX*8#C%D^cDdorM<|Mdi;H0x+K5KPh@ z_OX@$+c$)xhuLiA;_>*WMx&8!wOTKoPUo@T?`ON+?o+eb{MBx^pK0%<*Xupe{4DKf z1VM-q_nq1o&}tE~hPT;l$>DH#vtF-%tX8Yca=FZ4-UZkaCr;e&2$giuz@W&;F6Dz1U0olO%n!#;nL}J+C!lb8xDsL0&Kwnmwen2 z;7jQMN6_o_A2^P)dpsW2=kqa#0Jh+OOFq65-YOL*d-Qe!K6suN_5FT7LtyuSfj&6I zMf?ImP5fuX`@E?y#g07e!nXdcN`1tC_ZReU$Pc~?O2;4lt@`hZUF?yq`lJ8gOX+x} z?{>RI-+OW|+Q4?B{}LpuMj!S6g8akRKhS@5{*KUBn}77oEyaH+$M5xe83MZp%vS!@ z>Z4`}olYm_-{o?#)K@rLdjVU#Gq|WB0ku>*kbm*}>u@;ifk1!_27?Cyw%|zLU$y$U z0Iw|;%T+FybBsnKZaf|z1lWQDF8PQO6yHD6oyjo_lN<~NvBhHXjgSQ9^LdIO&+;?Y z_&)_(aEO~C?F*InFHz~>noQ@BNF-V)6z;3lYPwV^WoiCdsZ<{7_4)%nV;0xtayebA v)zZac@t!y_qtSRFIywFa`NMfYPokIbjQ)_ZxrPnaigQk!(>P_|zskUG-z0_) 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 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 X({ULRriS~2fX_Q=C3vZnfzST|*u5T2 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 zcmcha+brT-u1zh=e7nAbn#sJc-+bOPgQ_8k({i zq`H<+JV=mbXR_)|L+d}Vk|uRas^8ycXESZE%kFsiyga?Sx;nkKwq^iS^3b+h;CN?0s#E^r;-cMwd;^@*@$vEBLPA1*Ak%Vv zeSIk42=$wwKL5Ah)OK4RrvuL2;NajRA0MBeI-PFk^78UGvj02=7sqjL;hF#+9OXDlZ{W;iS@s>Sy$kLIp&#{*^78Wfi;D|AoNvSc_gxQ19l37s zcj9@h+Su4AhWn)$Ao~U0HyzxaH~G<4m6w;NJwHEJ!|zovuIhq<0xdc2@&^S4Y3AnU zQs7VYCPt$%1>>9T%8&gTwOXBcdU_g*`{fmEHd|~)Mn)oKyYl<``f4I0Ba?f3dzDtJ zRc^6Z6hiR)D(*tqm+F*3SrHKt$&~HN@8{>I!FM4gD=SMiFfbq+8XA%@4D%r9SjwO* z%A{;}{YgnlD$EnI#>PfzLqo%Zpkpb6vhM3oNJvnXl$6M-tE;7+KpB+fuAlA^^%cYK z&i&{8{P;g~?D^w_<9{E2W@e_UzrSBLI5_ALltEcErtbRb{p;!JQR4lPo6Tm0Cs0Or zcej!<-SN{Lk)EEOcye+Qi#bz?x!5BpgR*E&qik32ALid|tj&@z_df9^lo=Qp=yLwm zYPFiPv$Hg~pL!FFV_I%*?w!|&H|jfkHOZ~mI=-Ua$`<7E-ETo#(D8;Z(4tg3dVW6w6ydqxiK#u zPEAdHgmWD26FfMd32(yZQc14K$;l~~}#hTTQqt4~$=W}QysNG%{wDJ4+{glCBPGc!krBbo@4%l7f3+SmnKsWxfiOHSTo?!D*SxifR$6N&Dh`wIMD6-Y=(5Jln9>-AED z!61!{jEstpkN=m8h*PW8rmd~5vF+{cQH@5^0P)y=l>pXjC@d`O+TY*T6C7i%@~-tG zA|fb}mzI_`;BvW&d_JF?;8-e^x@bclcbSGHE-r4My1Lr5zP_&P>+8!RIIgLwX}}q9 zK>WcSB@l@tXI3he_UY+q?d0U7ys4>4AUK|yn$p7d0zNI!zttjK)U+TuIoacIIP_TW zL%ZEB)YsQ%5*%|5wo>@KfqvmF??YN_Z0zf@va)9@D=UvDCMM3;eyPtIaE>Ik`iWjlv>`US71+|ewwnQTFmz0!PW@cuz ztE;Puo}QlXw6E5HHu})FxVYGyn3(884l=GKj0dU5zsk+c9ULAWe!8)-p+s$ECX*?o zK5g`&FJl-Bjv;8@xH+TOG@^#1EiEn8OG`_t<>lq&f+cy&>0rRi8DWFJl->4sty-v7h;ii;LM^ zU0p(5U0o*5J!=pW^r0_f7)uUvfzuEDq~GKag<9Fu)6>VTR;zJ#cD8_hWn^ST#x>T~ z)@J?*=*t+!1_FVCwzf7SINyQ07y7^Kjl3g(2KRP8FE7uEGhDQ@vy&eT2D9C6cUDJ7 zhj0@xhOxW5yZOkWh@9XKKtn~XA0PBAQFL^)4R2pkQj(#ns!BCJKCZwyVsDqB$KMEy zVJtZ+D=SsxBscD(jdwf5!|#^|=cQCGmp>{mFF)_I&CSh1IAC8U2f4^eZor9s2q7=( zgyAiEP6ht0R3486yf z19FgyoZt>XLw$`tzZcGojEwi2o150Nv$N-b8yEs!Uf>np*WuESF^qjqE^>mqABeg} zpC$?R%7uTNdHi3_@9phv92^{cBn}S`KLMYQj*f!Bm*eB(Z@_$zwhNzgfBYMMAP3I! zJUPkz8|_Q87-v+Y)9KpHX0yAqv(t$?JOB*h4))t@HYdJu4vhKkN3R&LSS*9Bt*tKF c;pc+S5atKL(FQK|zOcW=mr32fZ@&Ux0p$)M&;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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 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/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/vera24.tff b/demo/src/app/scout/data/vera24.tff new file mode 100644 index 0000000000000000000000000000000000000000..8a332e4174c69714957332f2887130f7293bf5d3 GIT binary patch literal 112576 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 Up@h%^p#?$t<8 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 zcmeHP2V51$_C8#y2!b7KQDbjWG=`u?jV9J8_7Yo+!J1fNL4yVr6?<1y>>4%p7<(5B zYE%RZA}Amtc9bGqF8BPuv-d7s5X*Znd3omV`SHz}Ia79bcE33@vwI;TvI6XYe1H>B z0w@Di0;&VGfx5uwKvSR<&>rXt^a4D9LBKG;6Bq~h0Mmduz#_mOSOaVXwgJBb2Y_Qh z01yaV0ImYzKrHYScn-V(bb!W;$QH;0B?w2W$a;2Mz)!fO9|ya2<#MVu43MGQfb1hR6|c21)@Hfa-uN&=6<| zbOd?={eT~UVSp##4NL)M0P}&Rz$#!PupQV990CG>Gr$GlDsTh113Unp0jU6)<30j* zKt8}3C<#;oT!2r320#;_1<)4g3iJXzfI+}8z!Mk;_yE&@Ilv;oA6Nry1hxUc0|$U( zz-izQAQ%V*ZUHgC10Wtq26TYN0{sQ#0SW*`fRaFYpbAhEa040w&490fc0gyKH_#6l z42%MP0ww{|fZ4!ez#mu*`~qwPb_4r?BftqD5V!<{0e69TAQ{jDR+cy~pb$_3Ck0zFbS9iECyBqn}D6bK_CD)4}<_Wff(Q+@Ek}5goM^W z9-tsl3@8s&1!@6xfX{(uz*j(9pcBvy_!bxd3SWk76F-V|yZrZ$O7J@pt;Mj-X&SZZ3;YF$ao zc}QI;D{-}s#06>@(Na%sL-KM1>b0nK)wa~~a!%^;q;1tMan){GN8-|^nrB*G>X_D3 zuT3p0TI!3IdTRNNQKAT!(sHa^7kmNS;7!YFtgL*D7hXEwRZxQ6X(g z-JBr)Q0!926w;RIu}tr;`b?-=>Z|v_^ca$F+Gb91IY;%HP4lxK-!w0KTs>xXyQI}? zyM%+}LHfPC?}7I{@V*EB-}Qh)w$abrDXYQ8BA1N0uayEaqC>39NKqRn8T2W)#?FlL zI7k$tadJeC<*V8X6@D z_I-)>J@CEz66ByX>lCNLhcH zX;oqsoh^)6YDU58&Xv`8rY@{RKa@9W8DdsnQ=*ilDmzs#kYRl((q^#HwBR?n#2=== z?DAZGYCPbjdN5R*ZkPm6*K5U*Y_%Zc_yu>AgTdu)9cr}KJ*TR8(=o1`goAkeu;l0M+e~tIM8_GqPzX&8dE*94q@e>_;?iQQd z90XHiB3#HZ{BuO>1*P3@i4{B-!%ND9Zsd_x2%EZp*t}{@o-e*6c9*BzPg-t4HP57Q z4WcfWUL^QiQuBzk#GNvs>%fdMZ5h%TZ-dUfcruWFk<@i6v*&s0$?me^th~x?d^90pUtvk7+`zO8n%Q4f)aYQm_S#@12jQg}=SAdu zLqdrS)1PuwB2q8iksR;wdc>lL_Mj|v2uzA!11)XFeura;VI4*g7}7)VzPa#*r)d!b z+sh%D`@lSi9Qbm1UWu}Qq*r`+h0G$j6s_iZbdal3Tm4=QRpR}4hsnFl=!#OUHAW@o zIrLc=Dy?Jw7URbG)cQ!M;y+T2;pHCxn3<5+axRl$nDm&@nn7Cg5Q(_+a0Qt)Tf_2i zNZBo4|Le6rIF-7T4)v0X;i;Q5>%Y64GCgzkdMv;(k$w_bE_|ZhBN2$%oftE-BWa%~ ziGi`&L)0m#?@=O$<-sA#l^K5HbGL$>mF#RmMv<;M9Y{MddPZh}xq)FtXwBKHh1wjH zp-+{@SJTftRTI!Y_q?dlmAI2ms%=8zL_~czAei#dON}l)6Nb^(bH9v!nk26P4Vt73xKELC(?{FGa=TVEBc>TSXsOZJ&^zrffK^C^@u8iqI`R5mA_9Xhkec2o{ z?8zSKAa$wR4tec0cD|3a)w$Vf86rIbM(lukNtSskCbxhOp!=`xxf-fTWT#7FogvSQ zyE<6RU$EAjym3%R?{_zp%c{gnRCi|eqR8@5_?qTinm(#ZlP7t0&!!#HTbc)vUk(4r z=*|wG!|aKB()8swybmL@i3yFQJ&|$i5zOToTevs1n;)gaG-m6ZemA&1bsqZd1;%}; z@mwp!8mIO2^mL|n>HE8Rwxw>Ko=4c5PUye)O{dfZ6 zg=U5)-IR|ps1dDQn(}1%l#Aa46oy5K0ftS)l2_y*0_L`NY*2Z zlsi(bKCS1^lm_bi@Lo)U#H2Wq=Cp*_6KVd4?x5^ozBcX{<4Ih{b0(&w6)+m$jbK>L z$BB-o&i!fIzhW3&?I}6UH57G=8Z%e6PYNY!&MPY=gu^$|A$^&e zskadO*~jN3-*CXkH)%BsQmG{5m6^*mNwYnCF+uH)(&3XO zYI8!u5xi@FI@6|^?AD*hD?Ly|zqG^`RLZ4n6|y+&PSobGIZF3EdJH%GWicrtnf9kN zr}4ma$Fm5@YyRLfGIx4jQAw$RV*=l;gl95SZhUF=#K$sK24qF5o$Z3>Fw`Civuj_f zQS$;1Yf`NYJxGzE@!``UdF=MF4H%&>$19(*X_IPA7*=Wkbteh0SDV)m;#_^5? z_jw`OdYOC~EPQpOPzB7}-B24v#vbHb@I*!`zEWq95C`NZE$AB_>!DjASMzGARIyqa z$@SX_$MBZo>{h&1wxYI892((6v=Kd@tk0J7ie#Cp6|>%I_`+8drDC;9)yT;6RtJk3 zrD_*_=Se4*)rAL=ZJcv(JzAkmp|(@l={+3>L0IoyxTMVFF_4Ci)bc`fy3FIvfE72x9}_uq9b$=pK3j2?xKC@e=cqrTh=3&@b( zh0V%tT?<1{?7nn3&|rP(`udaPocy4O@_|q==f`a16a_Q<1F{w6ocyo^5(SiGMaHZ- znDbGHOpXxS$ns&LVnX=j>XbUVW88v*lTy4KQMPz$v;)~DqCzkipwnDlHROk(P+mSq z7JNne2y;?iCMpC69mpf->O-Q7X&=({d$j2|fBc=Shs;7#!xO7Wsn)#o<_p6!=aw8> z5|+?l1Xg-1`)GZdHO)GhkA~eSfRu?$xS#$AGq$ZfCn2;V72WXU#ZvktBx!u=X?#1n zE#dimO=sL-O#N$zBu&sALcw*)NeckoNMX#PHF#6Z#;w zEfK}a>`HoqFM#**`ed|@hLh)~A425?@f~UGJzcEFKs1ieGtZ=2*re}o#%-t$Pjuq( zW)TNyLrTFsPcG0RU2_=wQGNEa(xS9HI57bT4)sESwQ-oD5NO_cW@U9}*43@BD)Wiu z2zZ)~}gj0E}j9i)bcA1ZHUEHLILwepq z+@T)kj4o7EA=9JkR6GXc#g}fnylS-B^`lfUs-9S>RC~}xW!Rgv=E~hYI=Q6v{P8&k z3vtbvY&2mZ=#n)hjKag75jgDPCRXJ*9xc2?K9zL&UK8<-`M_*dxCNB}W34ypgh(nC zU!^lh7@X!lmT%BjZOa#p%*n{Iw@~Bm>mrAq(%x=`b(ca$0||{>%%+5e0+YZ!n)R6d zf^i9VTvYxUX_%v;InqBwr9su5tMDPbLd6`Fk%qLxFjyFq@9w%RlWv||P3Z*uK4a4M z8oF|MHM-z1OxB%GM$fAoSxEBwr0^u_uoYCqN9T_*voYsYG;>kt=)-U>Mg~cqDg*Z+ z_B{`!_B;#sl7Ce)%0InoO?G-tX-ZZn*M5TCGyLR$Ie8d0<>)6mWw@U$&eC1SdJl5p z14U2pD7uiSQBZzb+{5`;lEo`d*PRLtxtO-SC79V=yVIO<-zxoC#EmPp!%9*|9yArJ zCrcSIpI4xvnEZ-ZYVU%Ld_}QG8nPp?r#F#o3KYntSebocj{%~T3e3x7>`-bV3|MsT z%F>-&!crT!_iR0s9nRj!zQsi?wecleytrqs8j4$L6H1Jh17G3PgN;yVDF1-aN_(5I zz-vNVIIlY|#m_FD6-aHz^-pVM>N|jC-3rm%mW@e~%1`3u?%1ZUmQO0x&tdf&uKlz(PBG>t z8ph%fyL}Op^}|J@qheC4&*P1S(=SqtQY>#woX39dNU`Yyf8bxJ;Nu%j_wh`M;njVM zeZ=z`4ryBDx~@YrGD{4m8a%xSkEH_-^VsmrFZpzV)U~qoTP{0C)+v5;D50=z`wpR4 z%$c>~aX6sSouvd>Qtv0X`Z#{m;veSD+Ord3P8Pao8oqW&zKkELG<+Tu8@Yr=#K}Xt zP3eI#QG0lu+z0$>sAh6M%K{(4^;rCNdSd{7`g5VntX&z8zEY&B*NpjdrHDaBg%8fi zx8A(f2EVwfpJ>R+$J>MyyUICi_;#G z=kiDNc0_jtEup+rQ)ji`Q>>KhaB4ZYQUk-@sJrk0U+kchtA3|hBJbR>MtGpI5f`4p z!mOM$P%uV_rv{NI?Mg}M8rZUvPbFQ^*F?;>g)M!E71~Jlc0AN4f{SKQQKe*dok2oN z?G3WxM-&mVTHk`m$g;Ok-Gz^f8wbZ*7|3WOVU9{+p)gU2C_!cImDWqK6%>=+c_fcF z^6z!Z+@i-)nansw$My-wIBKL~j>Mk^1h1gkt7$hbB$omrtt zzFQ4vM+G>!^O&&o#+MIC5v$}a2n+H&{E*|6wM7PlB(JfLPgBw6w;Cc`%#E;R{te!X zDMJ=Dd=!NFS+38<@M)SIQDrob!CYi~n}MlXtKl5l;=F9BR8Bj9UwFM()hsTI%#VZU z++3MU%6#(avtG&y&b6)xrdN2i8i@7Yr}|%!+hu94`%OFRZ8z1^TzgP@1m4G0y1- zj|@=U=a2jy4Z(V72&e1Pal)YwP;)3;BCLRIj8t- z-4`-kb~PR1c>(h2)Y(t5Z4z#B`krw;lSEP&J&RmlFTI8>$beN>8-fbV?pPOpA zLL%H1qAjT6bvLeRW)Ue?z1GB_TD2VM`}nB+ir`?uX- zHwo>7z8!OhT%(_!oUxwVqTt{lGuB%cFR!!gaq6sx*B-N~7Y0$TfTXW*?~Dw4lQzF` zA582-W@otdr<@!aiHARvjixNj%1NQ1z$9qH5sj^!IZSNX$(O;x*F?xV48xHeeoSCf z`?PpX1{GCGX4e@c#5hvgi+{7%Z)~U2eQspg+bT{>Ok;Zk8I2^&Q3(qLCczYVq^|6l z7rQ{+*~7@c*C}|#QelAfm3zrcE| z=JB9Z7|nhk)8lsT%inM2JT+3ZY2c!~Y4b+dkd-MMVk(0WrH#x3%rNupB4Zo|@al9v z1*?vc*m%NXEo}?j7sOtF4J!zdI!gL>iuq_Xw+zMUuog2Y`NrZTTau>6hrti5r9nmd zU3#&pIUJXI*Qmne=*M%Nsf@cb@+bEFEP@)}rXZ&tO55Y{D2%5jyqf0mWaQf+JsmGE zxN}#YBJFj>f)g^2r&5(YTuDjp#ZI9NquxvQyvYw8{bfFt{Xb6J~B^g zEgSGGiJ{mERGP+Z8e^BaG3GieF!@-=AJtdhT2jj`Pq|t_-ik)t(F1ev>CfS-dm^#T z6n8keMdXc($Y_#gn3qpWJ`j_O%-8C_gYl!wEX#j`JNtq+RF?nbU|9)w{9R|3;rwxJ zirkFD@7{UFG5BuVaXix%&%#xAhg&u2cw7=ciR+~ldhmF1Tz^CP&ZE2EN$BxYXMAw* z=+Prja*1mcuvlZryW*}8t@CKFnE@T8h^geT^G}$&bfA68Zr+i~(K3B75f8LKjVvZd z42T>e=2qA3I?RYxn29Lm+A~9}4Q}WE>gTWq_-q1`eo`6sCf!S6qBni5XoGLgWTTOV zIjX53$W&p88n|MpH(Mq1pqI&)!NS)>=!fLXr*P@>IJP<%yC{>2DkZaPeC1&vVOKf( z+vV!u;NS3EBg@`Ob*z{aQKn3p(%6q_j7~I?Fh?aU6qp1nu^e5%b?bvrzU*e?-|JNM zS|W}(&gH(dxSdKxBO|jaGSILA28#x=bTK5~b<&}ebKhJh4=lvKw)Ue#UgfAyuVKqB ziboo`*KEom`}-~C^6dJTu%;4mRc8HD(u*l62@DKs%3~DE7wG+q?Ik*h*UZOGUgq`kz3Fw(Z;BOz&VzOQv~j#GYG2-qhpyWevV|6uBsZRn}Lvv32K*mkmLTvUT-|)0gXR!A;aggSrRCP66XvDf zvBn2s_awhMKb-srW6Y|-oa6+TFc}nKyc+ zrCRo9ciPSNgYe*w;%2b-=XM8m7|8l#LOlqXPp{x$y(ETWp1f{YQjo=u9Nsr9r4miy zbz^l}t3mj<2VYtBYP5+f)!-AIZLr?+uJ~Bq?xibT63Uw^D4$|heYWppyg2vP%)PJ= zz`u9yWQPAS!|-25cQ%E~BW{#;ompAjnVo*W-21z?2G*n|yLeU~cPbl~qDvk`g`94u zec)uRtj;PQ;N8Znw>Z)g{_iNs-Gam1^3nZ;5hQ|dMl zfv?OeD|y$X^t8G##uN_Cnx(XFPfge7IpHcfAKa~K6?sK3j|rlb|I}36Bq|*3rilpA zFT~Vny4S)ChN(<@mA<7gaT7(ycW~dthkSjpm2SdD0}G90%&NM9K;49RYS5d{&%VUx zbBQfG`7&7enn?H5=6QqnS>Q{MMBnAQoB1=Ts8TY!&LE*LPif_52`cpOFo_2kS@u?{ zlV5sTpXQT#k%5dx66UCcg#we{d)_fT^#BZQX&2`)^6zyD{~<_b$k;hKF(fJ2Ql+Ai zky(|IhC?{4?{(1Gn0(hshl_XRY0?KGEdtFRV0Gm6khKBOb#51+Y7ucW$#HX1%9-PM z6$Yt*?-e=A0|QL*BgDxT5CZ*-yo&r;5{LKZd&mOc4U+Q!tjr;8ziaCB)y zG~$lvmM1KZK0F@nV@dFqSi*&13}#)}K*T#-OS`|>UebvnBP>KSeD7}A)R)|lwC5*# zC`ktn#y`cvtHeMqi5!VFNRP^oh}77P4$3+sV5KjP7iEiqtU5yUwgE?(9=#n)YLy{&q16m0P-EWsxcStN`FbN zSk|Xi&CbOdubv!NTn=6#-d`0vqQWq2Vxm7Kzn#NZPHQ84OdI_D0gz^>T{6o&N&zjCX0lHMBk(tT!Bfc zfa95CQC}1LpAvnn%p70alqO(_v-!t^Ut8v_Gg z&+s+_w(YFggdHJ0i9C+S963{d;WYR&8O(p+M0v%k%Dc-c(=%7E#-Img z?RC>k>47{Yv#gj*v;VU=`@K|u-eto}*_Sf@dc(qyEK{k!{(%1jM;INXY;FAyoQd%i zj5Ji~oQ;+LJLmcSO#VOhKx2`=Xb}AG^kb$u{BcR@0vOPcP{k(QQr5!|9TJn EAKQFY5&!@I 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 zcmeHQ2Y3}l*PiqQ2mwqep-B-@iYQ1CBmty|AVsAKic0TFl_H3M3W|jyf^?DIB!GxO zAdyb!kdR73dI*q0NP+vmXUgv0bB7-Osm*d1I6 zToe2fxEZ(&xHGsHxF2`~cmjAj_#5y-@Cxt-un%}Y_$c@sI0PI6z6s6(+uJZ!99$M$ z6joxB|EaxE8n`_*HOwaCdND@F?&k@GS5T;HBWT;BDZ)z(>KSz!$&~;COHb*v^iz z5@1j8v*4G&&A@HIUBT~xM}Q}TzXZ<%{|sIR{uR6(ya#*`d;)wC90ra9-vZwO+uAc$ z3|shtJ``|&~k>F3k)4|_?7lYS;w}Afu?*ktJUjT=JboxHh;MxC8hda9{9H@Hp^v@VDSa;0@rN-~-@e;Pc>6a3nYmd>xzu zz6UPi%vc$4b?}Se=HNHLJ-`FOqrp?bbHG1=SAjQycY%Gu$H5oCVc*_g9m{>22TWk0sbDm1iS{k6}$)R2Mz#V2GhC!Z?Zfb z7(&Qg!olm6J>?jFp~p`4#MFmkYF`3e)WFm)#e?cdCZ={YK9Wga zT*_*^l#>q?N7aselP%Sej<$t-QBG}Wn}}6AwLO$me@d_7KzfoXK5C4r4cQypk*vl= z{VO}_pW3Rit3LkCoZ?~ZpT3d6t|Gd8j8)T{?tF#6VA8$Fx((oHNmdvIOX`1mdl z-pbm-8Ubqro{$LO1-t)0w0GD4pKY;ji8TWMKm>}Nneh*J{ij{VG=8N}GrWI#9FNa` znaAf!_hiIBZAX_33e6wn;~T#cueY}-8JsGg<}QT+)tT$@>UJ)N_jQ%a-eS?6y3GYAyP)Djzih*jm@HC&t~3OUyf_>?tV}r zq&6m^*`(`oAm#S{b}Wo*Or~(Cn*N@YeRVco)O4H{mK8jRtR9q}Sr(IhmcHq*NZrCY zhI#NlQ~szyM$I(mnDnZP_~ z7qE)LF>NDbtG&4bAvrM~SA>1Ve*=SFEr%HM@j5XIkF9HuT>e&i_pBc_GR5yT}~VIzs?@ z?BIph57aTrnmhQ)H6uO;m1-_j}E05+KFsZO!uyd<6I~#rnwqPn`(b&`c=(i zzrl1b5`fjsWQrMRxAWUP^)8dn-o`0KIBcK`rdw3%awhX)6N8#bJy3HASLZJCZg`C} zAX&vpf*h&tD;h0&n(nlvMj#ts1quttr({M3Ai9k^xBTso!nw+Mc{elB*elUnH zq;yOybb*gjOUiwFB`f{pC&TvRQzZjH7DhHgqlWTu>~-*V$_5U|2K>`@wEf=MSKi{? zHwcey+T}Ef`oS09xbgjFc)C;NQ*$Z&a09C2dgZ%h=$A_p=qpC1_gjD~%}BMUmKI5O z!qk97&)nM3T-D-2s7TeSLkLHvB&%e?Nm|%wh;Mvdy8F9m)@1E*c{JD7XmHxnJ;F<%k#ZO>9ohN1g4~Q_Y>K`vWOU|2@8C zgs|Qe^Xx#Er)V_4^p!ci3gzeA13hsH53NKj;d_$IXRprV%(?43fiGV;Bg(#YJcMYVj zUO#Jtl)dHWOqrl^=LVn}uK%7ZHQmwYdpSSO%YIkcht;Ay=bo z7%<)Q_)l`3MCW8~vUxR=24&MMecr;{by<)j)niI}+3~JyqEXb^e>v7WV zaun*^W$K1%Gy}*oP736pxvyxnV871DR(LSrJGTEd@-N7Mku+XTIxbq2d|~8GiAcHF zicV<-u=&BD873CCK>g^{Hhk>U_T|Q82Vu>@2#|%5jnJr}JSKQCJxaKY`iu9W%i|H5ReG2hh=5N13b`Ib7WN*a*&L?^Yhz6* zYbpm}j_;JkYxMd*9EPo>V!_lil_m&`Cn~!h&?Cs`IWlWsvbm|5HTtk261~j&JPw-iX^JFngBK@3skj1?-N}AL*oKKe+ zn+7Fq3(=?rY$#4U{A|SxO1XaYxf)y+ose=O?Il(=>iY#KEq0CKI6ra$>kw9%D%ffu)9ES$#-iO55ljQjOHpfWH)0$G^=&F zZ~^tz}^tQ{uDpFvQz5jj$=da^vADdWKxF( z(a^>D)nX%cx+*%?6qcHaD8NMSBhC2Xg|RCizOA99TQUzLKW9i?Rvcvp3oDW#I5Iwu zC>f)wYDm~@v`XZUURqH_#_G4uM1xie3n`2~NT+|WLC=qH2=;A3) zMoC2E0Gl5Snqgw$3adQ%usv6a(KigPMnb=n0Z5GiSs2*}jYH`sB;AL+`_Y4p@;ONU zi>TRv+P2<_RUhk!Z0bIoyJ<7?w0k^A@W1Ot1axyZJdI&=a<$ zhRaneQKa$w=Aa z=OM*n8VZxj5y(aNuBps5R;e)%Xty9)z%2^Oqp*h{DfNwNua;ICM0^1&?0F9Wx44? z;NMLjno2Gh_i@1Oq#tQ?wSSGt44_!lT$_|}qRY?N;!ggzug`O0zJ!+nsOfh4PW1e` zF=#esz`>hY$LixJ3Z_`mNuaVkx#l{NUs1q0DyF`OKT^YOoU2t!b3|4cOp7 zjADEh7r$1xDVvt$23$i@G^9k?>y;tYj(Huw9ajMoei|;k7^}w{o32v1r7xvw27}vr zKVCK_CQ(3Ul^!hy#;6Lp^Wn_fzZB>4&-{$T4?*_f#rH6S@4Oqo>zIIBR~h)jY2Q`U zeT?*Bg955kd7C)%&-5)}Gw_gZU`awFc(ZSpq#8-1;}eQa!0E$D_QvJ@e$?YClLxXWqZ=ZshnvRtP zO9r51I|eU`$=qMo2%c69)ZhrAc<^Y5{Sx+!+CP||rHbp3$rMP+@QQ5ALgW|wIn_Ej z;GHWalx666B~e~F<9S}B`IO^8%e}*?ao>rmy|G^s;5i`6U?RiNggC#x`ef^9V zG@b!3V8Vde!&9czdr9-1@nm@8!gFcYX3{m5Xjf}WckHO^RO;O;-YlK#Yx7wRkBkhD zZMzN0t6Gq$ky@-yUf+&R7(tG|e8h$ECyKf*xPEKV(lu;^n8dD|hM%Iv<$(F3I~(^N zA??^F%S9tfX?jECkR>cciEN36ZxT6AebpcidodwL^4X+st6G;P+XzG7*JK0VmY#7C zTLjo0LolFz?lPJ9dugks)T8Xa`=4yoTG>#jy{XiVUyhQy$Qs60_vPZ;u{^U&D~dIQ zQ(RvD8L~gy;w{OGWwyTwbc!y?_3XlXmtPeUv%TD+*0sj6fUIs9-%7g_TtYk1FZzNU zQsJ{8n)yLR`!W+Rz{EHCuSQNa4$s9(aConhF6}pB{&S_Lo68$fm?Dmd?BHDG0C1?s zR_<-Y(S{BczKHuB&5~BU;81J?j@EZj)ykKueOoe{F_-I!aJ-Yc?irVf$%ntKSY%=l zt93hZMh$ycvj6;IOP2~hvzT{UVn4Ss+gIZfAJm~pi2(@*;LEhXp*@x(S5>z!KY9}0 zPROBy_0^-~o%Pwkj?(0pJr`Z(mK)}FVd*H0o`v68hLh@75ihoePaGUBB7~euAMHW; z$RBt~9*S7nlq(WkD-v0U%ql%v3=BaXH;@XSo_qCD%g_0)V~1ZB)qU=)zm^lZKBF5R zQY1dbw$h!92z=AyTkh$A$4+>&Gvd(qI?kKkZdkNyGJ&JRROT>^tMVjh1aEdNW0-6t zEovUm@~H=yRr;G^H?_fN7+ux4iHFO9OhL2Qdj&u zIyTcuRFU3hZ3i$-@oZxx`PV`3!KFz9tJu5s$T011fLD;F8NgD5$N~g{NMb|sL0~7>qNC5^ z4g0*vs6k{w0=&_SVSxkWZ`FzGqQZahoHbQqw+P(OA|3bqLL}f!n9aPA2Q!g zqL!z^0eSIYiL~KQ-QfC!cVjh&${iUblw}SV8nIg*@P}UZwS#rC#{tTS_$$3s;npjr z5%bm5$*4<2o8r9_2{GB=JHHQVVn-6g9XHXY?mVj*gv=$4s+tDTMPdW=-IuY|z47k| zL~wBdxuO#66r} z1Us5MYNphH>X}$9t&XR<8et_V`wR2X#IH%R2N2_du)Nz&EaSd&8{LdhIOgug;q7Sp z97Tt0*7jaoY2W0kJEW&$+0eT*@G93?x$yE-w_*Vr9dRHrxfrFO=9eh#zk*V>@ME^R zCvw}vy;&@Jm^SUMBXgz~9*acPtr$X!kF4)9N++(Q6!i32u}im8JMB!xzjm0&%POI1 z@q?em))tndrlN}ccbc0uy!w=LI)#`h0p~HNb)iii8wan)TL)Mpu_czM() zdvJ+2ZMai-P-$dOHt4i0%({Cl^(#JA!eJ2Hb#O@5xdy1Jy~Dm`5?!o$!dKM)0*%Ih(}A_rbau{=#zI6a?$|D?p{ zh|daLHU2&yYn48>tV6@oKVo^`zovbyb$1V{7L@lr6f;|NXw+)*b8})3^`+7bnoxjd z&ZC#)@Dhos1LMzoZBKPW(FyB={Nagh<=-&yq|#)|N&9%S(Fy&FPF=cDmaM`7Q<1u| zG(M$GBE9&cz$2knJn)5+=8mRsd)u)Ii&zD0I9GSs&A&3e{*<)scmNgfd@pg!iZNjr2xXOu_@9fUBCUNmV{O7g0Y{6h=A1;r9 zHVKKe9|V;!jZ`Am+rnNFh5pNtJ7jm^r6Egn3yV^{HIZ2q4Gg_58DJ7Ne~PsHMhI0C z*y0~ZWuJqw@~QVv!@0@s_RJ%_2#M7q@DI;WwR8Xq9@o9Dj<+F!4vix%k(5hyL0bMA z>$eLzw_nqr1nkuXSS^HbN4V?H@=-qcZ(-F~ z*>KnEW!UiVsp&g|{2{LC7?WfoOO24tQucyl! zXHk%K$gI+%#ej!eBuR7~`)wd=a7L8*M8PzJ3fJi9ZyZJ98;35}rd$L*-5GCSrBa!b zVTPBcbd=W(`=Va>B0Hl>?l`FU49~V-PEA(KtdT`){z3W}7HPU0v!a4#Azswzu^F?C zO}9+XvY?jgzz`-!u`Me!YgRj@f5|I!rR4hF%dsqfD1fT9GNys_J%{MVV8GrD18{(J zkh{NVv|t znYMA)Na9MfqPq#*I{6?m0%T!itobWwMwFkYsS z0uVK@)F83|fkPz0Bo}zHq#rqQ%)~2)d67|r$N~gvuF;|o@t9(J$t);ztSneE0Au*Hf9Za(Ao-YQUsp~Z00_1J2 z@WAjUo;z(D$IYTRFycR5?%+kfGX#ht|Dn;nxKxnSNLVJ}kC z$|A;MUb*OM8m_IstU1$^oqi5-dsAgrF$_X~F=0GkM}5Qj6fObTI=pLNc21wBEYM2( zwlmvaI_|ShSGK^Xw1-GPb}psfzN}#aEjf^^20{jNCR7S9Ngv({wOz!sG$+hbE;j?# z^)J%2o;HsVYTL52LiKSDjD`0XF7i%Tj3>`$^ZCmrpz?@yT!DA*TK$E8T|1ujz#N;X zNkX$RM}>&A5lu)WwF{;=5iJ=+X^@xH625^yI1b6ArsJL4DpY5gxoZ+9toRFIW2@jJ13fDo5sMlQxVS(cil`s&>q#Y z4w+SYv>1q3Q(85eQta`5D@XvnEe>%=cO8&8=8)3OG>habMd0am;7$0bC$hBcDdXZ zRHhfYOgBhck-$d9kpOD?mm@24@NLWv1yPwue9r;FXzZ&!Jz z44N2Emp{g`HeNd) zGD8=D(J5HQ!SUx7FWdB1pJ~#qd2|`&KxtW-Yh^Uq@JVNsm`mXw$dAyghv8G&MdaZ( z>YIN&)=GZXp?xSy2EEs9#WjaMEENxKCeEiqcqMAY*ekR=q}j7b3+l6aEL`rONuS?% zOO`2i#p%XWoccK{6Dfu{X%^e_243r#$xgK9mUZLWup;s8L@T@pxk^dwt-vZuKF1dV zwlC-9z&c88IluG*=@+fU&_#nu-?1EuI1&Gf0ncF;uuO4b!d9N*HJSXVsUD$1L6hSq zsIo~z5D=a=(R|I3;?(3MwHSe@;aT$!Z0ScNkiEF(^k2ympC--7g-pzfOQv~nU!=t1 z-#Z6RBRlpg6+*e!wuc$3e*ohe0M*e(&xJd)BTWV@CWAWREuw0Xz}Sx;vhG`j*x?dj zoydL9Y$2%g!sY5)PSD7+$E*$}(#k6FP&{}vL_8^dYX_y+w>Fk@$%*`A+}n}VBLi8@o4M-Ei}s5mUYr_QM<}Zt_h1ft>U}J+kx`jQe9r;F zXzdFggM!1iW1Q6AC(37AzTn zatFWF9*nW*;Q8QZy|sn$%3-#9WMSDd_S`*v&T^;?bB{q)_dqwszo-pF;Z{X5_DVB# zrd-Xn*s~dWJFb_}=CWB*ElbWeP7xRtIW>{j?&u_pime}Rw4S(3l@|6>(#F16Kj9bL zt#r+A74>o122i$i!9=9en#Z+fo@vSAh-=W5&1<`g92iPBO`|nmk+aw z7t3MkdAg|obPj#aO4NH7W50-bZGA618WvjjBTz{hM%ov#^FHN`aED)CS1!V*w0wI5 z`Q(#-V%s^2k3@4p2DQ@6z;N0M0@H;bWdhd9obb^fAtw;@!avX z;}E~W*+@ih>Cx|=P|CV&VXLK=p$qE;B78&VH9ya*s$C;zV~+3wlJBwnBbA_-Z795^GZWu4xF1t3ncJ^a7qG z(O$OOy+wtBE$z=fnJ*sJa$OJpN*%?W)c9f&QRB;td)ksf_SeWqsjH2ciDqxbLu)Yp zn*lbf6VOO>Lzb*$B9c^B67gHbm__dW7GIu3u>SibsnZ7&{xamcWiUFu};SV4g%qj*mR~MPO4Y{~_w!w*Z4? zm{`cdsE~^2u43pYq*#5t$!8l?t~8{Q5g-dA8=z5xel`p<7ZAQI)j{teCD#ivcH9|$ zpT72(c&%X4?g!=ew=@rNFlcH(T zB>Gk(z_+wFR=1EHXjzNZH`cdR(~(WRr9SX^w)EHH6}OmHc0NKqT;msdJhj~;KBX-; zED}pL6~c#lIMtbxiK_v999FFEglL*e89o$iCH=WynqJM`sZ#szQ|z^`ZkZoGr6q%+ z{38eY5ERt+3Qd+Z7wQvTgun}G;cwL^{$9)@czjvA${)vlNUgL_Z<*&+uD(9Tpni8V zrbQX&TRWBP9p3@^KVPRxymq^_vR#?JQB8QEdT{dqJ1()_zx_5CQlFLEX`kb^?0ri0 zMZA4twuD~`Iq6O{oaFPCkFHax%>kjzH~o9ITgm>(NqsT&>r+b=>2ReBW3%@?XaDqA zOeW_Ys^L)M%iF15Y?j{(_HN#IIccNVGt0J=ac=VG9o!elQ2gDDS9RV&J&|tMg;GKg zEf6Ts-_6;j=Sg6&pSji3sm1xJjHLwBb$IbGCWAluRB&pr{RYM?yV>aF;@oieIM^q9 z(XkOWqqy^nYi_*_G5YD~s>qnn>|h+bEBF3N1^$u6o}nZ%*Kl<^a_lLtpk!rVA(}+S zW=)ZTA(M5;tkR>!Ko9D!P9!h+qspxYUyHd|teSQAzO&(4&edNzlKs|GG?9U~>k)BU z_rb~q?4(~j{{3WGgA$_Sr~C_6bEPWf*GSdhH}^KpF6I1M(A{cex%q20ZbgAjraQ2y z4#L82XGaM5w;U$%*^?+6zUMGE2p{6&(fuG=7(nj6qR}`}pf~HVo1LMvg$<##97(w_ zQfM`J(}B(?5z2}4o2NYSCxERpK5CLy$MV9U873C8uv(11(rOx7owz&WJb|hq=nZ59 z$im22-&fEm#!pVxzK_KA>3Or64tftR`F~fcX#4Z~&a9|vSF+X03OZP75Ltjgu*RWH zPd@v-f^+LDID#!9qXv-$2()S14DbB8^3E;J(nZ67l{#u#k_?!7=BaP_;*!8fc7~dJ#d`+2*Pie{ERex@kC@vr)PZ#IzC=wyMF~6UbO&f;Yz}&a& z;+UMcHDW(#K5;j?Joc_Bf-qj|s%{A6maDI3-}2>iyLfq(bC?mClduuDPDHjT7aIwQ zb+`~u>j$0A$BPA0x%Jv2UV_@)>Jhpr-iD3;mbY$dqDkMBucWK|vTA-=*L}SFyfMal zYi_3ReFcX0pGRbePsVO_`6)Rwe0d%G_MF|s;OrYaF=)AGN^H(Z@q6E|xwdI|lb))k z%%YBX6h1#vgbShtVhl=lXDNs(2=)%&CT0cnguH0X)y(rG6kT@f_Sp&Jv3@7J8Ga%& z)vr6*_lmfeT4&AUVJ!SpG%$;O$;zPDNlle$N5Dxj~bYS;^Z8$K`3x0CM*gjTWjq{NS26te1$b6Q3`eVI);t!ig#j@{Dg}ypVbBZ{3tJ$Miq1CqI3PVcyk@2Orf<`fZauWGW z>3gO-rw7|G9rPYts`PtmM!+z8_n#uOlD4-ofTaeJ1qcLd9NP56Zbn>Iz&kWP3&^NJ zWB~#-)oA4I+qeDN@uH5E1xxrLXa$rz-?^9>@iA|$>8u#2!4X37;L*@_O}@gyQTbLG zD!%xJ3PA%5DsvI9beYi_v(kT}kb4A$>a$#|SE1}4i_!NAA|H!4ax!w6Sg?3(4h!LE z3!f1&cT+PM`(R1#AEO3W>~j0w{8nyFAP{m-hz3mme~gjd+#hMNLfaeI>Ji5aiH z>i#5z_lXIs#bD|{IYX=0byjS%jBSODix$#+g{x2JTV<$d7*DUJ6ab$Q7A|Fm_WuF# z;YQ#UKfQ*7TeEME7yIXj80`AE`>+rE;c>gXuRkmiC>z|Nt7l%Lv3Nj(knl*V4B|Bg=jvV2!{N5CQ!{`~(d4 apBv(T(-h*L+e)ivYXqzjutwlfBJh8pq5jVR 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 zcmaKp%}N4M7>2!v3F-#AhITD#)w-qKLUaRx*O1pRT4td1OH5I1G9vP4<aUOyo8m>hi=J@BmYDTVeRriViIVsb zf7ehAv}M0N)L#@g;#o{v4GAf+I=P45Epa5sKZ;ku3?sq0)NcX}58fHGhw3$PAg;u{ zD2NB~Uq@|{k{Rxd90#ffmlxw!D4 zriX?n-fFe%R4QdTj$^s5OFkm@1amOovA_@1^w97`s@1A(&My=Sp>DU!9#K!n$2nwx z+GL}>Ue7L-N +#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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 T)_wB(Ke7DpZu*$Ne3JhF_IbM; literal 0 HcmV?d00001 diff --git a/demo/src/server/nitlog/target.mk b/demo/src/server/nitlog/target.mk new file mode 100644 index 000000000..17eed8e94 --- /dev/null +++ b/demo/src/server/nitlog/target.mk @@ -0,0 +1,5 @@ +TARGET = nitlog +LIBS = cxx env server blit +SRC_CC = main.cc +SRC_BIN = mono.tff +INC_DIR = $(REP_DIR)/src/server/nitpicker/include diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..4d633d1ba --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,38 @@ +SHELL = bash + +all: directories.txt + +# +# The order of directories in the documentation can be expressed as dependencies. +# +../demo: ../base + +directories.txt: $(shell find .. -name README) Makefile + @echo "creating $@" + @echo "Directory structure of the Genode source tree" > $@ + @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 zcmeHJD^%^u66F1_L?V$$Boc{4B9TZW5{X12kw_#Gi9{liNF?sQs_w3to=HN&IrqKu z*~!{9{j2_@lf7YUYisKdU>n#0c7Z)$A2y{=hb{1MC8Oz&>yQ90EtcF>nH$0%yQEZ~{oci;oq`Um<0+rSR63+w^=zyWXw90A9`32+LW z0q4L4a0y%i*T4;M3)}(szyt6IJOR(Z3-AiO0q?*Eu=P*$2eyG7U>Dc}_JIT75I6#k zffL{qI0Mds3*ZvC0z$5SkJOeMlEAR%q10TTFztJDq26lj5 zU=P>_4uC`82sj2#fK%WMI0r6(OW+E)25x{`;10M49)L&S33vuxfLGuRcn3a!t^c4u zunp`0yTBf>4;%o8z!7i^oB*f58E_6<0GGfOa1Go5x4<264?F;mz!UHcya2Dj8}JT% z0DoYF73&VxcVIK$0e<ju^htQ%N2ux?=8z`B8T1M3FX4XhhjH?VGC-N3qm zbpz`Le)kRh`MclXdVl}R4dC_prZ?zMT+8NKNSi38Y?mWVMbK@;SQHF}fKrHUr{WGp z`LUN0ZtK%sLo{HSqj*D6y@-csXP`BeS0aTl%w0X}24Vw)_iO64newlj{JA$Ucy0s7 z&prS5O=rKR8+FIux39@;{UyaNn{~=Rnb-&7?^|(~zjsl#sa{w^MfA3wEXi(KmQkXY zghdC11;RFmGy2nLxlEI%@{&=HoJh2Kbca0~l+!Oxs+>P~e=>XnZ zdvP~Srsp2-2lqj*q~4xtTUc+EGt0@$#h3T7y)(z_`g4F){5}h9C6FvTxnr}I*eoO{aLx&^u;WYqo`jWy23NVTe; zT5I)PzGBx(U&*Qylzfd<9zz;aq)n4)rQNN=l&9>;9PUjqt6{$rbo%c_s@tlCCpU7s zT4DO{foLI}-6eR;ojjYc9FmM0!Hznw=KfqNxs=fTOv&rhIlN zt>HAz#_+isvKy6EwGr9^*SF>?PqGRF_khypY0l>9TKGkGS-MF8G_Rnci(mct273XO~Mw&jULtg9Ysb<$16y6k>mZMvCm%S(TFBY9a=-z{x<{HkT zr0OH8(Rf5fPa{#97?6!CiqV%S)v$P*5%;4E<@u8bwXf4;D_*j@d6aB@J*HPG>Uag7 zbhtGytsWD+X3DG1s6~f6E4L608(U1Nwx~=7V{uRvl*kD>Zg3D zObJ2+6t{b`IERQ^t+qR8DO1cRdq&v z9^q8j%zQ*SCeYpS+{d2&$(gPjLwT7Lfika%mS%?7^HPky*rZ+8WSw|pF}h1Tao9~$ zGWtcy+$DMBB%Z1Ix-FIHRi2O6s?Te}_WSiL4B%~(G!Gy9MEiUQG*4gc_*ita%(~`Zf1>MB=%_N0OdfNHUEle|1p9bvD%QNy)=|lAOr|h}nQ+acKj?Ifa{$|Ya zXW|x)aw@wClJ^*wR!~iP?1eX%$8qZKwG(TaY5IcHyrA`tc;dpkz`ku z+MvGY7%9>v!e?354O*F-B-%kX+1o+M?_JGst@}IX<-AV4W!k*Fo+Zk*_LgOb&l5z5 z@?ixZHH0RYDcf?KIeJE8OT(e`#+HEsQYCAKYw%mBVahotRo@2nGIrKH*XRr%)#zxd zj4CxxDq4e)Q#hMq+cNyn2HW+~GQ@kT1o^`*zX^JO5hukj6`&6UxwBMW_GR6Ya9FyG zObETyA(uWL!ia5rawBm@lF2G@dY;)x9FvL3FfPxR^dz&|--V#2c#u>g`mvsWK9v?M zsbR5r-1kyxx2b+3^;j~`qrOwJ=P$N7w^~72oHv+{4o}}eX*chOZLrw(TI8ihn4)1; zLk!7MYg7@7(sg+%9iBRe^Nf`E$yS}3PtCcxPIxTh-#K_xr;$omT(+eAN`jvkP5mHR zNK*u#FRu9|BZp4qu}&dn)pN?}IfblFRaOl-)0YLks%3~={PrMfpW-hrWq3pXcIF`B z{+XASrV3uyP99#PoN$y--j@+s$DNImoR+)6Hun;j))*^E!qsqSpD^V?f%>=0tM$>^ zq|Je{+5-Vb$0)x_rB| zc7x&OhX3{BTH{b^ms&2qw_K7?60|w(BIgy-h!S}wT+e5DA1pJt8YKr)z9#6JDOv8u zhJoAS$?%+XiWTblYEFMZ6!J}R4qxB zNwy02eEHR|rX;4Eb5ipuHO&$C>lGM}T7_zgrpU6cM&(w?{pivLV}aVE{itWX3ne9I|~l=U8h7=$w&av^-B`1@|X8 z^jL?&nCaK{QFkVVzBFHR=s7OU>v+*s)#l|KqpLr8zdcWN^+&0Xf6+F2c_=FW`%B!i zF0CMJnC+)n{*07XtS)~Lm&18tn@p||zXoZf9!<)RNtz05Du)kHEo#Lw1;?1A&-}ei zd@;n$vZN(G<1A`YYS{FdzaH7Pu#QSGt<EQ(S9l2EJQ)zjr4^;6>7jWnkD#OX* ze=0SfNKr4E(F>H1)G96Cl9fr199frdQzds1m#qMYe{5Sxt?M(MXZBmEtbsX9^|o??&vy zUKQUdw^eD-+-iM^&<8rY@0||;R|rz%XQgZxzt<@|%N99HPp5}hWEGX=tZ#V2Ed4#| zX5ksv(!5^GM>pp&5r@^&#t#vfS|U z97k<8RK_|ev@~pjRxG3vJYIMu!x^J|rPLxLbyI%g^(yO^huzd|45h)D{H|69H7gh_ z*cP0ZWN?y3(581xGGIeZNYASMRen~MJB`An(1fk$8+rC8k2>>oKMN$denXKqJEvE- zKGu4~wzf7)%wu{Gr}|hi+*FP)nm^Sgf4ycl@SRKg&h(k#ro%s~@AO3Tk^ke&{My7X z)BIdhjFEZi8gWlmXt`fW9IN=f`C~OV&bM%h-~aY}X|vN)O-D)tqontJK}yM;RQ{48 zlhuFzz%O|J7`d6cF@FAy|CkBaCH +#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 { + + + + + + + + + + + + + + + + + +

pMPdQj{BY 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 z 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 GVQ05f zjQJpPweTCbBFp1z7Uoupx&1G#?>Wzq%P;KTFog(lM+UbHc468|z)2ZJ?hYz(8ZsN3 zv${XQb|?A%-shjjl;)idqZI9U+y|ugH|}!Uq+yXUsvY9rN9@Uebq#ckU2nY3MGyZ4 z^6nWLY9FAPmL_A|W$h*VfgE8&gGwA%z-zyy-lOzxY+HV0-+tQ-uBcAh5 z>bNPs^EinvuyGC)_Y=CllEaES(6BZ7v#wxSJ`0)ao!wmmtZh(z6ph~*|0hdo4w5(W zS4yf1AIu#<<|OyUZVw)}H;O2O)zCzwc1Y{F!+4IHZWHM1+)`4L0v*{;)i-!YTCt#h z0yj;zvmbvNq-)y)?N?-B0Sjd?R|08Yb3$sWZ^z$$$c{J zi~h(7kiQC1sr%QwjYzJ=`Jc;ClRPwG1ZCt&Lp=|j^iImbB~is3LM>;0U=;O-@E6T=9_*xr-2SrxeexI|pL$75d_5Ft zhsj&*BSXnCCwJzVSG*h2qY90YPsTOx=KIQZs1jAnIK`PJ>#<}KX?>_iTqUn_gr`FK z=GoC*G{Tp9i%P?kXE&l%!ukVkwO}m0!M2;@{p@Bq_?~{d)e?C>(#&R2sbu>I#+xQR zvtOy&bleUZDk+d}NxA&^T(84d8T`%2Q1%dypq%W%9yak!^!INiKGNAGd|6$0Azwr= zeI1`5q#lGi&~9G`{+lB`6NLv&)^E3|IxQ5|`*?SZp22rIO?)FC3w(e^ya$Jb%adE3NL5&FW<3 zkBXaSHoI2b+U%VSZfL-)w;+e=OL8>AM*SoW6Y_~K3F7!?pco19Fg};HqsL|O1NTu? ztaqq42*d zmNGqcv7E!~c1E}~m2S+gQz{M9ahr`Hku|=l_f^J&2&0)L&%wkdY5c#<`xky27xtce zyI3Kth6d8lrN(ni=fxIUuxz#e7xkVu*;d9k$YVndc5zaa$(Uukm|7*j7HcjIa6NbS z1NW;pA*Y^W*er7C6)nLukf#`X<3zFQben19&I6~v@gE8>dhDFS)50%jS5!YuWlT|9 z6SUo}RHW5aDh`_CAOXV><@+4k<1XJSb+APnP29dinWb&w65*1Q; z5IPZia5-;{G?!L6;*Q=02#Z`deClcMW3L9Vpt>96JG1?^%JdR#+aHpbYvoEexot;= zp=D^b6Y>pD6-+Pi)nmM(;>k#I=Qt=-#bWnXDxMGFDH64tRv7hEESY+d_@>@o=J2A3yrVZ0CPv+%T(LiFis2rTwAAJ*zeZ3CIzr5{eCwAB32XYFv-xKIFD83S)``z#3 zca>)y*RTGXM$2?rj>ko&7mVH0SE@5E#{sGar1#R5$vF#s(WwmVqn;e3x$$>dd-Go= zT3J-MbBIa|xHzlz?8emTGG7pRaqob_vod@|Aj3UW^o0jF<-l)tQAULcUNAjihCX+`|5>tHr0`A$4FK2kB*1`wiaeF z%_bKp zGbY(Jb=XvFqUmu;u1>9Ht{YBDWbmtI=@yl|K%|UcXdz#m5eN;hHfQ4te}NmCVEB=N zL|xbFv3Ry~fx{=Y2FcI)yMvCHC#mF!>z8~#-qGcc65w_Ay`L2Mlz55 za#;W7gLY>9k&Br_d&@JE#1WL3k}!f4qzSWIlH5<8Yy(1WYirshmIG7!uuw^furfjD0IQH_s{*c#yx92r2;;|<0aifPhMS8pFd z6!-{u+xJU*B|oNoZAjJ8)a3u#K$M*$G2TwXdX1AhP0H^kPTRG&nLqYRB1*A(YZq-j zmZz5PCXCPqA!&c3^rbg1-F9Zj!{@UDE#SWBompSB)IW$ANjpqPbgCv}dG^_R z-_^ScaEGZiH*FH+ej4YAV8OZ^%}z(~Q&fX8ocw*k!-kFWiTJhg(wSW@73-#txS@kR zrqR{kD?}NlapaGavVw=dS+*!JJO2+Cz~WxK3G5ib$_Nt7FcHoi=7%z^1aZxl(*@mM zsIHv(&ffQ1V(yK)=Z#*T*1oXL$JBI$L$ZGy5 z>as%VvsBu?q_FBY?9~$wA`K)g?y(O&h-zS+G!bs_yW@{};WzwsqIX6CRtASv$s9Q_ z;HM=pdFTNLwt0Qfepf_ca0!-q-b^=iOed(AXj5~9I!!|++wTW8z<^{W`Vqxo z+=HYu;Qv0}XU>TMRFkMqf=0JFOtmy7#~E0YsaSC*ca2)SgCr#+*UMX6%s_zNybk4_ z58@Jzf?~jZ_@{Cn{#TXE_3BW|H18K@Jc7he&&x~Snt#sjh)neL^Ui0q>yD+v&i2C8 zy&icF&9_AI$Q2YL9_Nw9KlU)nc4B^=OUhH4!X@WkcO6z`et@u;rdapZecP?@@AHI^ zcScHBR7w9avL2p3EgJ{>+I1F0Uc$cY<)L;4)0udN56$W~dB??@>}`stFfzm;@}26v zcGYW6?`zG<)zM421jRxrQXLbeQhHIsk%|n9A|3NFU0PkW7&o1!jINNcw)Vu{t7Br`(_nNRxPE7+)}?VCUeWGe^^p@NnpM$cg#ci% zZOusMNWn)%YnDRwn6S`1N2`B=rxNS72YMA;TfP0nd)XYi{Iov}u}4zaF1U8h%}%ub zk>SRpVwl&Hsc87wHd1xb%aG{!r)|T-NzkkF#JbP;al9U2;3=5XqloW2ScSvehsTXb z5j4ynt6c89a1K=5XC0|f_j+!77r=t#$wsIMVBel>_O^B~f0QkLGZ`@R zTIT~4KRJBsO}UmXfk{Dnb7nIAWAXax0C9}2T1Kei)?H}}2RZzkquxrX@2l^p81a12 z_d&N04pW4S7v^KH|A+hVIDrtLxG-FMKnvznWT3xYnBtMy-nRq79pAL?BYD>08Nu&K zvd6YH#BpeB2e&T>u$p36(?B<@uWqh(qNbSW%=tuw&G)Auv%!}NRR6#c+jyVFfcU;F z{$1z*Rx|Zt;k~5j;Z2j0Q;8jODnOU;@dEO>+@EeQ`CTJpkKjia9 z@lr_ZJ;gAaAg2lKTz&6cp>Nfrl&7TW5ohJU1r#8hrinRVFaI=iyUlWb)d^)yRGWEJ7fl4uOp(2nc+EY%Os*#F>B zy@j`MUaWd7B4k5M6$~6IWzq{vk$R-Zo@l7dB(5R3<3dpU=ee*=#{5*sED>vceTv@s z!3@tY)Y=@{Veqxe-GS_Yc<$Plchj5uYG1-c+|!drhYs{?$NN&8iyBTN2)(7>)5!%* z=Xs!-q100xpQeQj&YALp-ljYq?4;a3Bm35iPptiO;LeVQ1|!y4KG`_GmEa7c3WgS1 zse4hh@9a=6>)9gqpm8=Nv3FATipAj^?wsj@Cs^x*i%WUUwPZ4pF9`%pfOAS}rvJqa zWJB%}5fjN_@6oTWF^OI6Zs&!SuxbmB^Hsje9Zpg2`9-yiMb`H&7mI1>ZQDh9!VUGIPFA5bg8CUwx3TQ= zg1PS9!U4nj?qUDp&tw*Rj5D|_TRd?`-4oJTVOFYHdA=JsKu7&ALhvqqy5x0euu4+M z>DT^)^u7m4k2mW21}?w9NiP3&`&KVhL0&&uMv41T`RzD+hy1YdN zxa%qr@rWHpN5@)ulBK>^eT6K=B!iBLLn+;mg&buCy~#MU*p?G?_P@Y+Zj8=l{eX*; z&nfLSrW~eSh(u|c1Nm_U#&wtOj5IDk;s_Q|fI-kDt>n9J4+r9c%X9F5NoDTMdtGdAehi~~r z+8-{Jl^f-eyVx!jiSCb&s=tl7d&h4xj)MrziKOU$8A3osYE(3M*Q?GtJiZSir>{+4 zn=Hu&`1=p^tmv{)%382hwszhPni5L~?fesY0ZzRLR1(FN?Ueub!{15;Cg6vN_E|6J$FU zo!UXF%Zb{&MH28RaT&F@uKPj{<}gz8gOBk<&K$POJ^R)RvdTeilSj2{7as)-x##DYN)!*db&8F)tROLfEr;>kqElOjnK#45h}Pr^SpN`g*!dYH2!Hq;d@SU5Pvn8}Ed9qp)nZl58NnC2N-Or4*s1;_N} zR=K`ql{r6;Gzq}4VRlr^vo01Qb0{eU8NyFe1`2LLv)O#>Pf`#|L-Z_nc6=A|4aY`Y z{#ru03nS)`Ovz?}9?LV+1z$X0BrSwGa=M?OE+Rf?;`#F1#(!XkBtCpnTEP|F| zI~?FQeb;AdUz-n7;x?w)NXICG>VJ<%)g5A9?H@kL!Edke{$-#eWPa=*QI=`nvQP4u z=Xa@wnu%NFgDx=W=Zkmd21|E=djDp@Dx*j+TuPaQ$li~k;^xMOA!__>EodGbJ*5xDU*n4X2LX;QyLuak{FfE0X z03JAi3OOqY^Y(zv#EA*2YjTR~!oWA{wCgyy8!iC?Iv28_GpUVUws?cunQDGcl?9hx z-q|G5l`^!CzrFxYP3a%@s+xaVRj!piM3@n_y6o<>?|p=zkKY8n>O&Ky43So!|MyWwjmW7Pd3+ z#SexWjYOzWJ>$MGW+4l1I+OXL((c#5x?c8Dt?oPPk8Q$TsFu{RFZ1b7SN#Z{ZsVI4 zAIEY`yX1abdJ&;sZ%1jP z#xIf1+E z)-tGsR`M#Z28{2Sm?z2SVfL3sR%Vas@wAJ^VhJ!fMiuV&t75b*FpH6Mds2+B=S#Y$|B0S0? zqRu7lMEb5v8Efs{6=sDAz^cSH3byoJ_z8JiSIZfls08#35B}|u1v~R+QW%*B>rJh) z&wsOn<&1JLJfBAJC;=j&H-=EmA?j#t$mndHl0C>Lh^Wx9&&Q5ZX{vh_tw<^y&)4YOTi@$%V<3vL zksTH@M>rjSgWOc@f2Q)vweh3>wj462bJnJm7&5N;&vzsPeHGpH)cW<`dt7WvdG5-v z?hW6*eu{*L^$lDm=zjPlZ;JP{Sm9}` z2=^B9pSQQc*Qw`}IpkH05CQ2Ck%Sa;E^TC9VPDm5+;(INf8=)uJA?yC6jK?*vkgNg z2Ve*G{ji})lvt8_@#R@1D%~lR38tgwepRQb^~(Ln%WauW35(ZfjsB?0WQnYV_lMbw zqA%uUOjS0D=?hN_yJ-jKx|G6zsBtGiP;3%RJVyq#1hfFUl82tj{x2Q^yQOrepeO4F z2^6gDF50btWM}A7-lNzS@B^@d0{8~ z3ctd7v&I?xK7DHoi5LIF4Sk45D#L;sy%uW3@B7zXZ{^ea9r4-T#kn+~iC8iR-tE2X z&o&+jpz@-_b>{3Ow;ji*nWnZUQSRS+2~>1QNN&{S=5r;`o(z&08UTcFJ8}t%<(DKh zs51gzrG7Gbse(^-phj0Jl!%MFcfEly0#4rfj%)28C#s(HOH-5+mx;Z^r{0j4mwDBf zfelFTaE+TsS?E~gydFdeqCAnPq^!VK9qSjDdP~N18`szL*HffM*lbST-P66*_E&_n zX6(GYux#lL0s>#6AHJH|CW{Y!9XlQ}O_rn{uq^rsbcV(YlB#=9*W3VG@Vw{>-s#F5 z#1ioAsNa*3eR(a*@kwC^Duoa3qbC{?RaR>B=IVPavl0z(^DX$v85+Hn6uHH$56t$e zf2iIazyXZ@kV+U836IgswBtIK)#rp*%2{yIk>?k%cRY`Tk4R>PE3EdXUD^GARK0~? zlu_3C&LHg~^ zDvPt_;|^g++e*Kr#Rvm}g~FyEtgX>aSKFVDwqW2zys?`IyD<}i2ZH9p-x2LK(?q?3 zW8+)I#ae3&ou?Q5$P`hl$8MzqNqP2!fzPf}iH>17T0BbcV(;Hy{WL;Dx+T!EbiR5h@?+isAJITr7gwlrgH>1j!r zsNF&TNC0Ss6)B6Nt%!}xaT=xY8F?SzgnqwX!YwhQ0KFh8(i~FljO`wa&|M;` z8J}|k)uWsBs#Ma>`_E*Eq7AF-+GqBXlfo8Hr zzRvu8g5KA|S@_Y)%#ABcs9Zs-grAvFIGe|>;|ix}wNRem^S?wxjB~sIT$jSoqfF!O zDcM3eD>C}_c}3#^S>5uU%Ri`6OFuD!RpE>E3^R`4&db7X?zr_pEfvCCPr1PsBib-dS9SXSzB5+nWU#K%P96ye8&PPU1zn6q{#cEcCu{lfc3v%o}!jH*!Djk@dv)?{gX{<^OGJacX z@FMW}a+gBm^FfN)5!HVN;kn!^NE1P#VmftiR)rauJ!?5L58x3DG?K=Y^~qivzv*X; zxemVrmbP!V0}+403h=XWCRFzz|&sc0Pvan`)_m4tPv84zE5aUY3DU{brl-t& zuu5sy{a3iI-{WBr+sgl-!KuCeC`pH>{8MH*b0DWht`XL6f7Dp=W14xvz)I`sk=2-X zmV7K{4)tw}utZ}nRBgos_xbIri=p1x15-|Z_bvS~b%t)YW|&7u9+WXaUlr z!d!3W`$P(49Di_+Zd{2Mjbf`^N2mL8&58)W7!cc#KM!@n4hl*$FB7z_X+Jybe*K~x zhDrsHa1-=DMsU|=Bqz*^M)h%gC6#{p93Tv;YvOvw3s$!QmqDnv z$JM}=ucXBP3OlZ@9#Wog945kX{b0Kt_rX0YGcF0mJ0S5W#2VqtddL6`V0bT+)P<`w zqNH4OH83t!o3?))gq%YQK=-d0CNVh8db&7TO;1fQW!3vrjtKCqdZ{ZwX3Cvqw~Q zY&}IXR=+q_!+Pa0+S}Hb@ry?A-s=Yc#TncaT1~Lx!z8Z7@~KP;AmpisavYC_#g_yv zBsj>;NQU5G&D>hq35nk5AZv9Bhtou$)s3i-5*9il`||BFn1-~T7wI_;a z7m7Px!88!W{7X1@H{&_V4&1C{8 z6a6_hhTdZ6N7T(qdEUBPM#s`+bYTXJJ$A_B;vftb01H*foRrate>v_%IpR2z`La7l zp9+<1@11J^k_{J3Fzca`saT@%9B+Sp5qL8)t3-xEABV@#cth^58U0M8-*p{z70THF z*(mB-j(d!I!V$QgcEGnghPHi(`Biq_MCi7wA{ssV{!xe1{jQYB^S7FiDheTnGNQ3p zam@4)Vk8WINriAE?3w}tGU;3xsVKY8*iEv$)Yzb27+xZ^N1^QhU6tFyY-v4qcI={i z>b}lmz_R@4AnehSxZUp+TQUF7(1GoXpu7zK8NmvnJUpP&gTsx95w(_+jHqt_^Ee5` z4$bg@Ug$m6t&ip~5oQA}^brKK*x4brK;vZl+Vu7AoiaXPBI_`kxHYR}zrKIq$C+#z zf`uU7RI&sgB?zSw+KXYBH2Bvn&Jp3&pGVv-e$4I;H}Ibg*A@3u=~c?4-yz|JBdwPF zb9RL^kuHiUQT5WNkia1H$3MUWSm5@jpPSoxO;4>N=AW2^t7^rgU0q*MCmOG_KMKJ; zT`#r&r-!}I>5&@Crhh&N_ATGNaAOm%ky7z@l=GB~5>}=ZBkBA>P6Ycc=q*&yomUz0 zds(AWv!nWKuWIVXc{DFB40BO@L84WJQj3SMOZJDOl>bc1HBhCkGA!ffs04U)UU7Uv+HYG>ZqC++`}Uilaq52 zq4BYs_V-l%&$CtAW9V~xAx9yc@v-8|uGx3cl+~K)+g`i#{99h4t<+N&Zb5|g-G2pCHoU%n`p=6gbnp1A>eOtciHD4nQw_xA-h3tssBzuRpok2V9VVzZjnb& z0-X()0Z0(+5X%(ZZbFv$BP%>VYToU$j(v-IA$z0`l)Wo-u)W$Y?O^)cx@myzkz;8je6c@bhgHIV&JJQy_#c7{X36h+SmooCBPBexs_5Z^K zs8EReY1%w-#+L0QRR1NQP@LOGftZ{uc@kb2)Bwo zQQr{KN16`>y$OJau%;GnS+n=YIWLwk&nG#J75I#eV&oZrP!Q&%PStbehRcei5^X=_ zu8l+*)IoORseZ%!3#*G6mQ06GP$K@Ezduy@yx;LRUbc?(QBZ%Gl)1kAPX@^?T}&cv z=#N7Km=qIJOj_b7=_%|{7LOF6H2{~5%yu&3o>fQ)g^NV|D-FuUrV+K}%eYJbT1@n* zt2Ck1YVVFhdyAEJlA=*c1PV3pKg3lpPxP=*o`2*9#rj^@2?}Jin@_v}m&yYkYPnNS z(6>K9gyWfH=conW?VAaNP~xhZT1_t|pEs>?IMUap{HB^jVd}q44tWJP3YlK>%}A>V zig+Sy|2UUbdq|Gj=(&kJ)c4@5WSVeUb*>s*Ri{+G)3rCTJHZ?&XPpf-w70Hx{)7&= zY}3#7JBXEnmeK=mF8twrkkI{qeW6?EVHXnrGhycz-Z?mrXo)=i9bRz!ryVN)0(8bi zf7I2Z%BgvAP=Oiju~t|}cI~TFiTT3Ds}=(NIG#`k@)T<9obs`Vv_wHs*7oPsvFsR~ z7Ur3RiP`peg*V8O1jXo{Idda7@(oj9Xy3Bma4G#{dd&~ZAfX?gM!uorkZ+s9(<9ri zkH5~=PZtejZ3113B zTEcchHL?`vlIDkxhU&vEsuTD+%B+j)T)wanr8cJJ($ZCJom&LvF2&cVR5bQo|0=fr zB)4Vq3S^BJ%kfg%0^lU)Cf5a}P8+jE=03P@x>)r(*ZD3RvQ1bRL#v-t`+}yAZA6>7 zb{-l^vVerHz=6SEc1jY@wrA5DhR=l*x0HbU;&(`I$?`ZLt!~V``5NvVL5as_?5b=> zf?RnO6{v*k`(*;%cz6;BzqsS5t9IlP@Om!Ux6@a~3xEb~c=wy?Ij8}kawGq<-PS~+ zVWPL3uj$;|kz+X2pK;M`BBrUM1ERG`ednwIr= z`*-#QK7}ZWJ$wo%U7Qv~I(tK-_4!w|m~4MU>$IK9XB3Sz<^7@{aVosD_n@f5Hng|+ z{u5ZO3d0CNgG3p-?*UuK#^0T6Bl&A zb@1vdep~h}JK#HE)++hT+ghPlSqzW&1+63zxYz@hwgxqKqHw4C7Eb@l0%M5eXcq0X zxJc=#)ydVn6;YNDn=SRG5`%k5CWdo`W^M%A&3JauHAuKgATA}cJMxuYl_Of8P-@(@=uVxB43%-G9`BuNqtXV{-MPwP{y2NE*+X*FXPbNvdrtc_@CA>|0uxw`%CI zA^pEiO>)&FT&#Zt`qp|P&%J~ckHI+5yHM}Tqr;zeO&V^|2C@mp4LmoxJs%Wq8wDo4xvw0;vI_pL8m$=W2v{(0GvKi>t1)^Dl76p7OS655<7^PFE2$2gU?+@7|muW-R~3 z8zE#nsTThGOT}&C2varH@hu9o)Xp9e%O5Sa-XCF!A91c`6e8#qPd0J7L9DmLfxb@Z z)uTc7a~?~f4)L0IAlW0FnC7duZ8|bzFLc|t-7@LtP zy4$@*35*~TR!DbBmy{zeqB+4w&0k4(JdIxm`#CJ6^;BRWC}N*W!ocIFtyqk)>_@YB zOUMx-<1Ji-E%-S;joA=in*ByVOI3T+AgINpX}f&#*;`n~tZ$VesCbXv%+L2fL)pW& zSpQ0nzvqoyS;lZkmc+j_7fc$iP+J|i(Jf_ln)}il`-09l2Zef&7~jcZw(S8f;Z=(G z#oHpslZ8jH{&;5hjMk=Kr-Vz$a@Bj>+X=@GII*&b;h(J!qRg?AVh zGlxGFq%nQ0dl$wCoE7>|uPuPQ&^svQXQ@P6flT#1tmdZ<^|Yp?`Ntm&pg4lw{hk>4 zuL%^@Fe>Pei6wD zh3jDq>(kdi68ommqaQcqW?1147mJHWa%+}FMNbh?EuOAXOX#~M{C8WrF=)6 zy3Xej39(dhZWa@VgCR!nwhv6BSegvG^kjr_zi)~SerDwUEru6U;;QRId#kYWz`1Pg zGef{H$`BILWp{Y8ric;F7?RqqC_J8KCDq*gFI{@$Y9uE1xdmYn+WX=rQ8*V{NPb*I z%p*`*j@fG-zg9kYq|jmfo_d@r;{RRUT{ZmlIU%#C4xMj7Ki=(zT;KtEW91__j7g*} zDe-IzfovorFE0H@KlUM^hg<;$9u%gpj!rVfK!)Zl(a`w@X%+G}EsZq9E1#y$o}V1+ za%rqeIw8(_)t%uAj3uNL6{5US7ZlB0{@{CixFvVUO~?*C2Z|JEOToQ1$ne1u@u#&%@e+)@E=8 zo%wJb6-4_x+5NzgtK&-C7|p~BqDbgR1oB!tz9TMY!dNv-IUCDJ;Xi%e^mPwCa~j3 zGFz!IGk~Q{!PJdNcIKP)5MVT5XOu$j12bY+NE<7h3t4c*mi9Q=t>V1ZQ;U9M+YwM% z%k?=83&P>hckU!-R|wor;)IwLD!hgAe)PqZkPS!EAi?8x9jccs{W0q}t9Bn8@TXXZ ziB6t@b!Qq(-IJqkfZEu3qT;G%tYoWvZ#Ovz#brtK*a zUZv1`^aEcQ-q@p&;Tb%zBC11EPs81w;Ct;X;;~y}^*gFzQ3ibm=N}GUvt*yF z$7)&yO4|6EGxnU<54Y)f?G~0WV{JkNT__qw{9s|(T-#F3^0-GB`sk(~)$3D?~2-JeUezjWN>Xkb(LsR4IiJ*qfL@S`Or%3cju{^U4&k{n)c)Iye_`TgzuEKvGL zeinXE8Y8bKuk8X6T5TMuuB|?=KsC3PuCh}Z=Eu_JKK~Ms)T{8ZgM>oXBjk|3yRuaC z<9tHN-p18GprVq2Q<3~vg&!YW1qBwz$Z)BRJ`%Dae=}+6h=yb$`m4>P>oq@U1xgg3 z3CT4XqaKeZVJIa2#OKwKH3ECwGm7Wiq1VUj*3@gtC&m-BZdbo+wBQTiaMpf*st3gUc3jtcA)=p zTDeN|UwH(kvt%f${He5kC4uVofp8hP?-ite9MnItMcsq=w(7$$9p~xMWdyGP z4?A~J;hFJRTN0|7MMjv|tz4N4M^sjN1;!t2v*4VwSYdCZpC59PbD8405B0ye#CW@z zS6l|u^{B? z#JD|ORxJ`ueqAmzn;{dvSE!JU%14U##~QbTM!F;{*VW2l2B3>ig zAR?)S{CZAYO3q5bz{(5mgA1Fg!i*B7|CZskt55>k)3U=C&6j9YfFJC4E4L@fTkEh` zz=SA#$-}zcO&3*bIc?On0eSKx9|u8>g1K2@(wW2hmjKKo{b5uB#er`b?kf%ri996N znAli>sZRsuU5FUIW*d*pD8A<(8!X=kN1&pxW2ipMg;1=9!5`sqVG(mykdKzujwor` zRNCA-t9T=_<|;5ZUC7FhLBpyi9PXl(rt;?woWIlM z4jIkuj~=ry8ao;GQ&cl$DRHd*mdaV4$>okL>a=Htb{b31m%UG{hVxwj9VOoOhhv7+ z-y;b6Zx)dclMR${IWDUZz70L=-kVwPE{G5ez2}ec@@87=%Prk6;vrjRX^(KyDc`?X zf{bg>F?>E5)hcY}ko7Tm>631k=4Mj8AyGl<-&Q zj~v&A=f*RiCrnPqYMkr;(vT@H#=Q2FC)T*kyNOr^9RnH z!{Q|9-iq}HCunw$gcS?mC%{p!UL8vFM2%^0&pkCnZ#C#C158f$(PGw zrmAwWv zoj@Z+m1JI|HbaMB+zON*_J+cexcHVVP(HLUoq9=O!Npm^=6u_Mou)`_9r?IcTEfGv zo+$cTqc+Y+#aNCP9hJ29Gb3u+Rz39=)%z6C^|rKH>d2LJh~M0Y-oN|5em!Bm30~Vm zA(|vYTqJIU{E(hd0m1Md8^};kA7@C#VS>Ay22rxX&Ov z3p$r1!8{86tGn=PKU)}oVl;kOBX|-k&0->*UH-=E35;86dZ$!rv zg%4{}A42s)v>qNurkRODy&E!PwaEYxbW(7w>r=b??-stWV%pKei|VRlv#!75|DgM- zk<8D18VLWxGDEs*d;%V+hgLweM7-2=Z{D^_liUI=#hZz>|ISuvGUWqy{*ADTNlAT+2;=7E5cdzifM#fHt&i><2ymT^ zCLHNR6#2p9%FS72j%R+GHbA7@vC4bIMd9R)%VD>57C{+p$rOHp?R76HbaC?lL!^$i z=%nJeRa;(BPRgF3pQFQx#Ji6{EwN*aWmTiy+OfuXBCa-y144a&CoA#V7PrS0BC{$2 zIqxuyg1){IVs~dzm|Gid?h{*HtRR{Kr{|IQ$TuX)=uje`)Q7=+YR2eaaqilh#pp$I z7v8T@+Q&80`v)S8~CNsMrmRpXuSDCP7j{rQj)3>gx%s zie@EFUm|Vi+9JhzQ+#Z)A7awq51}qpw7>eY1+<^<`k(vYH|)dL?8_!#%p-AX>kbAs zI&Ccz?aU5@xi+veGiqI4qHL!{k;><<=yM2k1co({07ZJe+P^q+>@MK8&dq)IJ5>Qot6$!J&>=&V9wGJuC`Nqe zUY9^Oa^(q<4^3Hv)?|!W?Vr!{8&>BP7i#JRK3bLe>qq`$4T!#OZasNqNra9zk@{vS>`jJ{{NaQ^lSyS^kOG~EyY;>I{t&g{Bu6y$|HEJbP7->#;39emD2ti^XZLHk^l; zW)~U>PKdAbS>745nx#>VPGLD@@dcek3&z&L&B`^l_Mu)2Qt}-1-#6ze4xMmjdx0dp ztxDWWFmPLq;tRq%B@BUxNB`;tnD%JNJcc6gU{Gku%8%lj*zpaxLDF`5=l|2%00^G$Als{l^*%}Yg+=huc+kAX!xMw^Nfg_KXIVV5L+IF(Q!Ym4 zuI?j=4|&ko9llgseotNp5Q=%Tv)zONNlIOv<0Ddp4#HYU0Pt^y?2u5{?xRt zE|Yl}mT8-NNn(Nurdj=w)M0J%u&jN`8Ybvvn?QIsM$R539fjTSMbn{Kx-^L6Nu}x|kBQ6+`fyvaC{GTCnS0)?L>m%{mwuNIsZ#IBf^E6!cj{ z*s1s0ZmZt-uPPjkk*w|sZU=@(pr$$j%zWOD-T7Y;eCq3a0v12lcsf?42*Ke?<^Hz<&gru94DxfKRdksXlT2!!G z00g(h>a#pZ5|ZQH-j?1XASsnvQO2{BOj5-AUg)#IMxkmGj5Iy=uB;u&FB@O?p+CJ~ zg+A7Er?D8!Ci$<*5t)ErkAAU8CTX-S*?VK&;z$*Amzn6B(h49+R&kVJBv7x#A<+|vlwz&!kky5mc*xvr{reAC;?q1}HS`QAkeKK*d+1xS!TUL%z zcRal14a@5#M3fFr|BNLFdubNVG{71nS&xh7@0HH=+28FvX@qbaVd_qn>8`d9#MM5( zDCiHXYZIRew$#Sc0{rW3p4Bp@Kkx3eeMJ- zQA;Z%uhkgj$ouw+&C{y3+F&T)(P_IhBlWUij-0m*0JpejU-bm*p+OFOfb zcWxL9VJUTy{LdowufKIfB?y_l1-yT=b#$nXhQ1S2g;COo?x?_C|HCu0?{5LzfI)|X zQCb2_4cp3c|H3$rX+0~o=pLW>t(j4vD&H%*m{do6K??Nc?t=-k_}kFgy+Bb{%0d__ z^R)U`vhU%lzu8{iZWk(`oI}}(k)^$;*ug+d$rYba5Y7`GtZgVcqEd0Zwm*!P0kN7K zSqKTuNhU0=ZI&qg#yFl&*->&I_^p!x{`icKB|FQjo$Yk7t@gh`k4E9A1kvm5QyYqS zG@JRKC-4ApF@XRhewV@vSzGyQ(SkfMKqZ@NzbmZ)zu*nlqLkEDlmG588^kjmd(GNS z{fg_@tcGmX-*6CX!L!j?*wWi>yj0P9O>q#TQHHnQQLPqPH~!SZ)}Tg{yul` z6yr|aCtmv7pgszyJHP(nlN5VMqyPCpy0U5hL~@y&KyJ#Wxn{D*E}!&0VcpL2^K~u6 z@(gqIl=tPh&P0QNIdpI|Azw*?beiM^c}5iOdzIxwYY2ZY%F&Kb`DioU4t>L%ANEj| z_4G0M0%}9z%vLI3I14`kq3o-T8zOBSzsyPW9*-RJ*as5J0d)vzo$6gq1{**T#!qKKVL;Md=%rG4U_WQj1I#QSzP_pOfZZ#qo}dcy4n}*9_ngIV0=->HujBaM=5I zQu&;8K@P1c2>lUtJ-#rtUymSd0!~czLSXgFJ;=(<@F_Uz;d%bmV5m@wmt80Kg+8fJ z^g!X6LnoJZc!jo+=C9)Hx1mPoVm?|P& zs?``90%Ty?@=^p3gsKMPG8j3op6 zKU{!ni@ON>|Ea@_Z?W#-!?pD_w^GN@eM>hz;pH~j zRL0|@1J>&MvRDU;sv6Y6ux7*|z5LB4r7DSQ!-Vwi06CmNpOFx-3uaIphOg5h!YgBI zz#UeDb>aj#;XrnHUG^2_rAYYPt4B4HN`Aa2w@8r*6zVQCy(&Dh1D>>Fyu$N3uE}DB z%CoQ9+233_WtkS!_-z{re?G(`lpN#|-P-8d>=S>-{>aNk!f6J!G)m@YS!E8FstYID zuT0_U$QQ0uL(alPasqEB?BuNv3>d00r;F_ipmtYOdQh#oHaWLhK2v!02Hz&X`K9_0 zJizv<&7L8q)j$pzegy9sQ-bofgS+>j+^a^BUnA?~deDtDd;04A%ND7&=EcIQ0?Ezv z=ha?W+~kU2OS{+R`)frlHT=QE(ZVI_?)29q*r9QTHDU^+#rRLg`doGRmbBR10kNgj zo%TFncy*%RB;H>rx~c$pakuN_5v74?nB)=kzuzN@0!u?>5qLpg-S1Vh&oi*1N`Rkm zZ#?h}ij{lXoUH$g=EZa;pG&3rv3&GY`Y_Bm!;XJQ826)w=_@$ ztUb2fDL*^WBIs1szf$2Oo|dQ(7)XxOaI>NG#DL6d+H~!v%0I}qtYs{Te3xnW)_J~- z=ocbxgzTUVr2mBnT8!9A>cK&xa1*pb(s7V*ayjH8^Ez70c<3m18Sc?CP=dfC245ik zo%cnCgQ~hl#C2ISh~?N+(*#9&90TJBFX*wqU{PHi zc%c>h#4zBoAzy+7wLa^POp1*PtJyOs1PFKcWFRR?1IWPJN0(HEgK#g5%X(}Bxfm7? zSb5N>c++}H<&&(!PG6fUa*0L;V2kl59vk}^(7XSSkz&=`KDDbVWLl_lpH-FIq8-sEUOKF4}%7WckEiW@b=wdYEy^{>^RQ=4VZWS4?Q0-(~(=P`n5KsGXqq7vQ3##$2LQ z1OKG?=MkLShhqP3jejTwwW1Q6CT_G|O?a%dL9)uC*dwl9T`AD!)~|bM#8e1_ky{Ao z&gg(+g?2s)swE@z*J2PYX0S8_?F#E;m`%ehyF?5uKP2dB#sC9nt><6u6#yI~f4Cy* zPxY(*Sun~h=Xo*%BI+R*!tjjKrcbWR$TAMc_PX(jr&0+BlN19S$=@c>p5LdqEfFX; z5IEqsqP7Ez#j7Tz1}wRbNbA~u1d;#ORroA^3Uv?& zYchZr7h<#$y%TXX_-AfjiR8b(|NAi~;lp~ zF%%dDkxHO{rJs0k)%9n*3*G^o_)|xjM1UeqKW(X_LNl9VwBEVw6NbE%g%H|5 zJz^mph-ZVt-X9#Ge(QsmYq@Fm&-_Fr2eX%-8Us2c499DFd(~b5TwTV=e5cXK41b|7 zgy$a7I|c)?!%kUhBib*y*RM7nwa#(v+Tqopq5KTJ%jSeo%u`ba$YW$7+~c1?-tSgt z7DpDuW^KFSv#aml_8Q(PmFi#`viG?+bxfH}+tJ?Re5X_02A4|~dBYeTcl`fkyooz( z_xTmE^ZP1f9PaTNNdn)d`tpCb)O%M`4w*Sxq&(`D4F2oZ=t)DaL9?oO7JvTme7FBi zl7<@iZA%^N!4AllGk)PZ!aXZggzG%2CG`^JV-kY>?l3vI+ko%NpT1DmWG(TFVnJFF zEzTaIfHn!@Qp72{4Pf)3hwCkA;1c-FFtg&j0?8xWp$>GvI8n_Tvje7Qul^Idi$tMB z{$h&{^*>UQe+Iok9N+$!#*Mze3hmej-Ny1rO=7<3^aNjer3O{2@ud<`m!96X;(PF@-vlH zA+=E)S-CaSgmsASLls?-&~gS?yI?dW_iN+0zg`u7Efp^Djm!*mImiufyuI zQFhB9;mP!(L-^sUe7tnsUVH!hta!TFWUnrg+Noz7B07C`i*xQFq;x8pD3;eGXiMOe z(Z+?;mvImOiDrU&i#|(!isYnaOV~f)-mAsx{obKivJLLg{~L|)u!7GCQ)R6-)ms~Q z=4c&cZt53WPJX*=324AZ&~BIUZ}&q4dn`S{t{56o%0HNW)3iHB8PZnv)3iIjaVR$M z2~=-ug)Q?Yo`nqO3)<(nVk@cyR^i4nNbpB%RVWVE;qD)(-Rzz_A~pPLYx{zoh4*i1 zn%1f%Z=WJ%8Lrc#8-ks>E#CEZb(|J4;^RX>28-VKE+21}*<*+Xg?kNs3)FOH3WS_Y zfbFOLk+PpI!nA3(D;-_E@rQ@}*EpV&kLY>f-)k5i)~p^Yku7U>%CtA8`q)>W)AGI>Vzpe)n||n9M@>4egq^ym%&@6jB6WrWAntXLuGF0*<%QBQXvF%q3m*XIX<_}3tM54fIWRwS zhw^-&k!}2(unRS;Ls~T*df?BrB+iApTnBe(KEqJEI6`OuZDIk8$74%wW%oR@o86f$ zn7Oyc=DgrlXkEs1pD#aINq==f97VT0MnN6_`NgD~_nz`i@fR>Gg+rt4_8mcKPgdE+ zSa0|2Kr8Aa9(hL8yJD6xt&p(67fS**zxq1tl9wKA3Vr}tM4x|qT@{Kuc(r?KwE%9- zC@lF1TB~6)%n;?BX+G6$KH{}~HV`_oKXu%=ldNnPg?pc$YZ7(lj+P=?a9Sj?OA4T} zc|c9D2jWhn|FvB7{!%}WIr}CZgzzQZ?>^h|+B*O&vS2T^|_leQ?us{FBpT_N!?UPnXmGYk8J+&iT0ZRKr;|$b8 z4!w1Lt6PsLpOFeEF#fEtGF`mnJq5H+uFoNydm*Nzb?bn53(jTc#19UB;^%aZm^5-^ z%-bueGg7;Q{8c`?N0toWCwSX^BTqKt@9Z7t_VCw9rm_k^%HW2Cb) zhgYN0N)RQDs{Oa>ujio`k82l2eXHv(Gfy}jV_+H?_cHJ$M|*MQ0JR{lF08zg?j%(^ z@8r*8a43iz3j&3b5#QIJ3x_iC@r;GMQn9ROpMFY>wq`q>$z7kQQ=A8Wf~`#bO|LUJ zix4r}O7r)b_F_ zY*ec-SSe0U7klN-wH%`GG{})v&{1;79s2JC3NcDZ&J&VD{Dij)dh@4zXWcH} zp2jSIR$nCi>RDBU=ufp)ne}U{Cf_Ui@=ShEA&U4^og=34oeKPGLkI}nr}pUTkz%rO z3js<(LUJ2WZRjZd;h|1|;PX$s0K*TT+LsNV5B{mYz~4Mdp)X6EsueUOnay#`!CY9- z9GTA%CzE!jL#zVHMCD-g8tYVL_U&$@<>jS@KxvCyT%SE}rpT;cfF1o4)jd7QlVW_9 zUk>vq)ByjkS-fU;S;+$;=D&VX1mW7J)V)|6V_Y>~K)AD+)Wd|`{}{>S;za3wMD~lP zS}a)2A&iFU{<)I&{_0f9g>FN{_K{m%>v^By%gAzGK=@5+NC^{YZwrIBV!PrF_y(%A zG*AD8SwIhhY-=qa#F$EAUqhum_g@IWT_;<&pJ-0vCE^p;Q@Kvi_sN<7=b*Ya?bjnHZ96n|x=t4xN(H(@(pJn-VhPghG6i|a)>5r&z) z5{7%f{sI7SJVfkHW%k^GL!vkGQxRn^)QCPIhR?-6{q2{zMdV;0@RdKM&>D^>kVvrz zCj_|WGJA9}!ALZ@l3vVeB$mmqqfCe98_VC%!`mv1O`D6`Re6w4u)?Y2knv#xNW{?_ z4z(%z`8^e$1u*_UXYzV51LcLwuwsn!LP_<_dV@s4m;J66!x`W=FY?>ARo=K2-aN7` z{7ElS@#J=RBLbep-(o^XdI~CNMyz4%{J-BES>p1hZs^Dd)_lkc!6UbC9c1tV_^bc{ zXM(F&BKi@xBTsk82!}wSh^^cQTAX$`@7jFH*Oy%Z<0KPZ#Bho*9V4~+CeR8lu0UR7 zyXBI2Y9uQ8{m_jVvK6F{4+j#g=ma+1CEHmwE%pJ#%0SV(-=<+u{d@9sC(A>;br=7* zm>L`IG4+I+F^j0gE{*MoBl;R*-^}P4%PbLr@16qjuv!5qUiq$zdX(*h)hf)Gv2g~b zFp$glf;^L|7QSZig$bL_y1-J(JYiic`u8OP0MO7Lzv-JwY5pTz3N;iS#Ggz94ITsU zT50nOcwSNtjQrXtSWTxrIFxoKW^2-0D&E~?Wd76I?^`w@MQO2R1A;fq(4+SXM8^*`2~>l%KnZxAdb|6zNey3vfkOam8ZeB)R< z@9-;XqvR(KOd8GmPZQ%pu$;KtxYk|**@P5rU>xr4wf_5=@|EInhg2YiSgx)j-Ez3g zd}HzS2$`Oub1hjcMoZpqy6fek>F&D;cmc9w!{f7XJVwzB^GNP_R8X?9OCg~sJ>kLc zDCuw1;C4_RO-X~$Dk6HupEYJ87dQw}b5@2*hnR1Gq^>8nGH$X3Rvz|gOne{X8WgS( zsU4&d6IvB%u!X*m_Ey#ZOF(!F_xCY0v~Ui45nkh?>EXsgQ9vp)I$diq3O25CuXD^C zgC-gH*E|6q6H@Xw3s6f~nPWA|JTFU=@Vqv9o`9svu>94O+zgPY_p7SLN4imX(36Gg zGk;8ljB>Z&@^G_}VMorGkyezT2HV?=eOaHgN$-t&P=rTOJJIJ*;9-f>YzXG9M1Kqt zbw`-lzcwc|B&+}L{%EZ?N&yG|o*Q#N%;!I+EOX2b!QW_)bYEtGK(`q&r*SCz@-czN z9o0P^FQ8>82wlCK>hr_TVwijVRRQ3-%P=di_l=c!Cl0ZL1oC2BI0ElYq z?YA-M8QSqYH%I;@7g8U68HZ^|i&pLKI0zuey5%f^w3?3nWgf-X*EXHcuW!{0FX#DO z3sTWL1aZS6Z?^|J1m&QoSGjK_4LG%iIH}P@k5Sy^6CW!ofp6As10@~?ExDEzUS!xfK^e^k!ZTGv%2ZGb~}2BYw1y+tI;#Rv$9dUVovt|1|W zvsOFsVc1`Z!%wa`*x`~->}>?`Y~{Y$#Qdul)`OyFu1VkaQOoG0J_01^z0H)wjt(Jr zW#R=U7yaH#j&Yeq2Z&@nTTX!#y}(u;fY{?NLcss|HtebbZ{+xo9)avP6j-v_5OKYC zDnr0asI3MhVFZW_*35zGyFO;4hRC&)(Qv&!QbSsK5Jx`0gci1QkuVql^@}Gl5j=k| zrw=c|S4s?g-c&`44>8mNt!$}R*046~J^Rl+93G)2+O}dN@ThZLS=R9haaL7e+@;_c zFocrStR(6j(vT3)?vq`wcp&=w%=9rf5fsX1#pQH6nPhI}xp#{l)^}9GI zN?fJVfG9{&lhgO~Vn>T^%Me7fVJLht&9KM2sX|H>R0YCvn*jTrw~KKCBUU zS`SFd=7EkV6~D%MvPEY6r4LJ^mI(nO$D+kNc>ey>XKMcxJFJkMjIcO(7X8-YNQO3& zC*6A@g!-OcY33ld?7FTOk70(hfl%Nkui@=2RsA%47CNODD$8@ z?GAIVO_3y2U_~S5LG}~i^CRWg;yL8Nnubfxp$Iz|?fJaA!V<|2fh7ExuvY*Pn6HiBpP9;Qx)hc`q_BH{_ymZV9=MV z?*5SUlU%jc>Gr3Tkw=53{Cv2^dzC%dHyUa^fZGf&3Kjb-sgi>27u8!L1*SkA zxoKu?w0o}@;EPR}POw4`{r5Kx8ZQlykx%>wEou0m6aDI0;H-_<^H8K1c?lR{*Nqg8 z7W>i{R%>F{E>bsp?kcjyY97wE1I}ZX_;{(&PZ-W1Am0ebupm-3yHq84rOwx6QV36N zTh|yV+OttvMkN?ENt*Ur_=YgAf2-^ppCV+Q>e_Pz>)4fMBj>Nt>Qs9*m8GFTtf5qk zGb+37vC0~Mk+S3;b5`!m3mZq$oEBUZ-sQF1HvO2_fJ$B)brHQe{2H~awY|cB&&b48 z-w9QA_{=&#TNK}5gl*uUPH2AU)DW(>oU%uJ~QB> z>6}UiH-%%fHaR)cM%NEnlc1>jtV~V(^QY$?Qds`kDYF6|DU#W1UUvWo6=?|let94A znBvnX1~7lT@BS~;+4YwEiNuuO6kJdSs@#jioyWYL4mj*m2BnWdk1RsaT<;JT4ikZ5 z#6t9Y6r@u{7{jfJKvm?0d^1QjJX8eJ9paT>YPP%Oa|A-5Y>X6;*Qs1Y5Sqg!=Xj9( z?wc9zX@Yu0S|U;K8)-GdaIjGRg=Q*Z4!OlpXCd*z?4 zm5xFkHMP$c&;fcnMf3|A5}ZS!8021Ftd1vroQKm4hrLA4>W_ZOG8u^wJ(%3Lv$Enz zecZmJAqTuw(u--j@Sux}3(#w)Fwz6h)6JIE_ppFVo+0bhhS*Wbc~3etDA-a2o6xa> zP$EYm*EV|&dRWpAy3ZNLWgD+yt7V1~bD|KN|J$tqH;g8xQPU+Gkr*awAC8_E0`@N{ zY*GX3nOnrtsrNPK7hwBm6=n8v0E+jdlA$%f%{p_R-R|UK`$)3lS`*fY9i?V~LoRWX z&?)Xvq67uqgkGG{VJ3ni|3>|1t3d z8PRnoH(Y*J?bo5R!ayz6+s}MR&T!zfNloe{;VaNR7^K z9sd_Rs=#a)@jgaYL&XyVrWZKKgOU9<51)z-^8ykNZ74Dww{1{3VlYgGZ?`(cni9{6t*`FZ-h+Fn^uxg`l|RH{DT`b$APgu_$ezD%3CaCp)pFpEiZ5QpwQi?F zt~JFExW(T11M2Fk1 z5TCC5kYyz+-!)sjdTz3H_a*=e9TZACE`c#u^sPA)(5a&z)!F{Gm;ZzYMH&3E66uxK zimRlWu8MwG4ccWGFdAPf=$RFM+t*barZ_`T6pFN15f(kh#R3Wi0AqW|b|2Y+Zv+dS zo%8Q|W(~s8m81`kHhga-P*=~bfBr-B&r@2ncpq=2M_J+fioxo=lJC7a-%!^p+owl> z=wNQ%XTmr4yQfl2ms`ZHysBsMs@ZHJfV8oQ7|WM}>bdf<77P6=UE7(yI1`aq1w}yz z9~GBzBTAYQ5?iZU)(P8EE>1ds88Y4AS@P+>-G`eKlW&T0FOI^4>w_+$_}s8Prk(Ti~xh-x(vJaI|k)Y<|61$bvBnQ}?VW7YQjvKxO8CMpHn* zda`Ck5+yR+0ksmW;(aFRIPtlCi*ra@)3=hL{Jkko!^Y2*qdkc_lw4Go1?rO+VwvhX z>@eRr%TfqAo8Xmi23*j>qg`QX(*nQ#WL{jx>YC1aP(}IeS(vxn=VC`i%fI&*Cv250 z#51t;hKcaA7$qd?>__PfjBeeAw<-Z7s9bvdw}-Nn;A+ViNib(JPPHdo0GYc2Ei_29 z5{bYKj(N9FkFlR&1ghJEIARE-E4nb~lhtObeN2SC%d%!=0KKLRn_KfNZg)glZD4y1 zfoQIM{m*OQLGkYy^i?nJb8iJ0h^r+m4OXuUWqj4hGcuI{PZu?1PqbBl0}CIf&SS*i zjxqEVvl0&P5v-A;2;;wiB7*_Hf3wnYN^yPq!A{Xq@O<~>c}CSkNtuqkjnL@a|BK*s zIV%RDi;TFtuR>hZ&B%m|Tm&~@?$4LtHBA2RG3)rKv*qvPoTt&5;8oc8cUOg2)u_{^ z*wn)mC|B|p{g51W%sxXLul``pq8%O|P9op^EV~<5HW5*i9H>Mg+yN>o*7PMn^YVXr z0fds}vfJN$*~$d*?|7zWT+9k5Z4&%5SE+Dk*YkreU5!TM;2m4cjiU>Q^Vq_tQ}kTx zYz1l<%12(2PcIGHB7PzzzHf4$9yr7m^9CHmn_W;$M7~y*ic0a)_a?qtTb_gBLlc_L zu@-^p+D#vo>4(s_)doq}W_t8TJEQr|pYytWA0xElrmcU+2d6mWIiIrTPO&6qb z)t>Ojej6XPKWlZ#F0R0{DAaboze*&Bo6h&Fdu|X7k6umkN9GGnXtUw=k?JUN6UM~k z2zyyod9U9FWr>vYFP(aPvLBPAq9^kLup%P$i_Td<4Bb94Pbmiv1l*AkjzAEd1llQQ z6frsCAYoU-W^~Tzl-AXJi|C-XvwPXy{SUbv-%u4`)nywhEz{(HQ zi?C4PoQIfUWOocYBR47-I89~#8S0a(41^&Kl}p-f+o@v(eOmvvH@BBlLXC7MY-B3~ z)y;Iigqc)4-R92VK0B|^3CRh_86n`m{e7V=xxvAJ!n|jM(#Gk589k}1HzR*R4};w{ z%l)jAZ|xU|3^v}zuxpd(TOcDKg>Vn{g~Uf8~G40PDfVurS*md5d?q*)R)lEf?~Z zT~{`qqG6ue_#EybtyOxOa{43hdk`sM-SyLhd%@1(__F+scQ+fyt~X@gJ4j&8J*{xM zSr$0Ezn4Qq=Ju9D6ppMBJoo)k_t=fY|NI|jci8E&|M3P$`&s%JuEfJb(6@unKY_HP zsHs=>rHXPf5(ja^YUNdc-;iMJsQ@7K=7lKgloUDFc>KB-7d&Hq+4c zXiCS5zGHfnedsKF6p!f4&qvVmSk7yuc5f^y@|I#eyS!ikn_Ob_N(;_y-V;vbU-{`# z6#UtuKafazX4;q!4Dur5f`0ZqPV%_3{JO`uDeAy-#9(}(_w5=kNBEbP$7cNXDePT8 z*KCYB@+B7T_VUuzj_jg2J()Q47%5$I`vktEA*H+0o5d@ofp9+XHt=v@o$& zRbK#3R;bXPaswqQ#z=Vl`uh}#A?2R~?NyHoPgPJVr>R3%Mu@8E_v*)5SNr2X%Vf9c zYjwx|kd1c_GNi=_@YaMw;&nr$fo<{K} z2y~s(e2abpi<{pbBYjZ3mu!i6+yBxKj#muJcy z*A>XEQO-tGzq-6__ZirhcOG_plR0QvKKgr_Z;O$|{FcEy8~b~f5cnrxF5ECNt{wD zM6;zMFMY+mm9YnJYPX36^Y zNr^^nw1m0ZOonbQPZiN~?dO(Hm0U0!gisfQfEFZ8A<Y5%e_q`ZkJ{MLVJvep6Uu#Z0(?X3h0I^d8}Znq z`&4k|J>qEpqET4_6~O1lBLz2cB1z4zcCg^Z=hDkFXuQdbx|Z(`>!1WMF}M^~iyOV|WeNINOq6@$joHf}bs8W)%CwK^nU6rrNLKc{EEC?!rso@{F|~ zz%hqJytg)zeF}xDKVo}j(IB)mX?E}Go8Eq1TXX6u5dc6}&@I#8dFB)1?TFoK($VyR zEOE8UbgfNCB$mf!Pp^(Ry8Gp>HbGi;>KG1iNn-RgIW>2BV27>xS;s3hg?E}>h%)7S zpZo5fz0=zJ$gQfKZ_T7!Xe+ZMfINm9i+^kx~If)`*kPi4IPygYp@l#_hxweIcQ zYf22r^JT6=`OofR;XK$!7x{|@3vcQ_bK4+b&A{3O>r^4U1n<3{uR@1T;K=Q?`iL-7 zMi#Z(jh_VgxqJyYe(;wZdEWjd;Ij-ciNxxb;p*p#J6ba7JD8AkVOrPNxC z;+~kF<9c$?aM(FA7S@RD{fWq23wwLD(nV$QbfH&KMokgjGrEOnnvXGN5a-o}AkECd zbl=W5~SD?*~o|a{)l}Rxkt*=+s2ZhjuD&W7h+IcpF);t_=6*>=v{}1 z^TP>tcm7^9$Y=2N5yR*9C9O-=eu=pfVYnzFNBvBEs$l*f`Zs&?HC+z&v0IL6APgZ#VC* z`(Eo8r}fyD+Fs3;&=L5myL=B_l$9fN#by&Eke-xFI1RYn7-Bul(f2=C5M&v;?4aRz zO5@?YQ!}Y-6;opUS{7K6p35mh`>H^&qwW6?>>^Iy3L@6QKam9-pk8|q0@W_tAi$_Ju z8%<*2p51EjMR)OA?-$-~6DAsZF*m}1Gudebv*nlb?xOVSB*5UyGujcH!{@3mI?j){ zV^^JUWMoN124G*;DyfYOmp{HV^zVKQ9Yx^Su=5@`z zL+Tc=RuQZ7TFhe9AJ?Il@}NpEZ>aMpNc`NJv5^z-`^!SMuo1yt@S z0pSeDt>)f3r_SKEcb3lBt>{2JykCEOT`x2VXZ7d~>BbeAnpap@{Cu6Tf7L9CSpGvAk8zO#+6)tMRdcgS-T zILI!KNA$z32)6;6zh6jSO3=?v9ff>E)W!>YZWwi3aCUrc?nUmfmtHaOoJ8TtPbAGk z_FI)jfFd(0W-o>f8c;(^;|~@>c4i=c`)(qUr|J1;j!uYCM}v5# z71YChf}aTVSe5_K2$3^7&pKs23HyUgqW0db=C5%lF_ryNH#Kcx9*w>5ppgl5!7j#l z#{S_kwOES_D~g^x9=Te>OS0}z6Ve0He~^_{47VUxnbO_^ z=_Wv!+@G^o-zJLiVQrL^hN8QR;{mR}WOyQ>(1kE@*N-YHa%Hs35G;I}8Av!#Df`9M zgkaCU*=Fxd;r`>ZYjM1A&sg%KY~{RbB=O=AyrA)5K_)n0_ERVKOCfP(nGlX~1zr@y-&5+# zaL3sqOJH3dyQwck#Hbo{5?OjoXKK***hZoY)YGys24<;0;e%_V4Qi$t{k<@spWCAU z0(MXQ;Du!Q&Ex(>csjj-?~`F< ztcl3Rtep&_YJXw>XNyteh2J#+^66u&0b4YGizolfLoT&C4jrE-jI${npmz}brj4lu zlVguPp$J7e`-xLDwrn{XIC>js&(#-pl3^psbKTjxC40(0cp5n;b56wh)Jow5a=%i> z`I7@OGwN`S|MAtmeC)VPWvX`=fR12P~x+eWxVVAG+Iv^I((7Ly5Vpr+RzKqAvE_1$U4e~~8FGd5?N0E6h$@hLwbp3GXcr3Pkz+ z0N{cxiA)KPoFBMLVF%^XFi7-Fk#=ysbri|+ z#7~}Uao)UWO1c1sY4lr)JT_Ut6l_<_qTE+|s zp}$;WJ!7E5h^2pnO&u0ndPSaln~jDZafmB0<{yfcN|v|#WwF6aQ}#mu$&&h$tH7t* zLX9j=uOrKB2cu_L&gsjlT_FoD209#zMzzic*)QlcE26Qk3U&}E-v~Mk!FJTW$;p(o zA2h`oXS+D9vDIY#`8l^Fl3BA5rQU4^7LIe4D z1#X#!J?eP|9-II9^oJLpA)0EbuaDzj|4BCS zLopn^BUCJB^B$ayX>xOtFvwYP4yR-t>3Mpg?%2O=DccBLv9<)N{=YxW2k%c%Z}<2& z6h{$?@8U?f$GccLRVfrT;G>V!`LK4o!r3h;@MI_pya|HPKC8X>6e>WzxNt@bH)7We zFWuQF$!P3l>wb`xD!5)+oN1}-Ho7NJA~ORnfY>kB>kaw544Z!pvhwq_!sj z8N6f}g*%+IFU#n0x3D^OFrc_e+PON1xgy*MPZl5s1d@sK7|Wxt1|=q%{`tC^{7e{IKh-Z=(Tf!t!CUJ65+R3oy}`QajbS}?pkc%!f$ zU0H;{BqxKInzpJIM(6&X8Wk{-$xkBHbZNi-CN@l*3CFegGCQ=@j7SU$W)}W?Jo9ff zH$2o(g@qmwIfQ=HZYK6E4+zQS{4ls61^-`{%ZL3)^}LlDa!3q-qH+yGhawVMCKRB9 zImy4ZW_)?;^P*EC|J-_77c6rKaEz2IJ2! zfuhw9?XCOJ|5*>xJHBq!mb3S0d~$m)8JqAeC=+G|E*cKHYCd`)>p!faEv(&#cPhDlqS zApF~j1U)B}S$k~_=ED>v^2vRP+3BCpud4fb$Q2#J_3n@UzmR@Utc*o;pB2K^9wuGW zyIg+F)CgA1Q&7wy7xEGnJN0ZbX;o_Q*0e_$SsWn}CBjkwmWxXexqoG7&ARC(O%N`+ z^y+e4uT#YuSx@n26U?|6FyC%JxNSw<+cJWUiBoq|pP|sP?d1qTDY^q`f&BbuP0aEM zelqWPY0yS7RPs!(rHeuKHp>$$Z%>7exnPhAh1ioHZ|a%^FmS&dCaC4BUHs`AEH@UT zKe;GWFrAo`*dTTwRMxe6MAtVIeG;sroMweyN)4eD(so+x+uB~C3BII5YXaFzI!$4)3NF|34(JNvp^R;a36Bhy zKaGB+mNXuUB@E|3K4ojFOz|2+H*1;#3A40abUkzr{&*2vy`J$tA2sId(Ta$9Q=18_ ziTo`5vZQlhOK*mw+>2hW3_9yQkfB7r<? z0F392U(0b`=-4oGI)b*=PU{hl$kwL)jMRYe-i*$##{;J6D1A1Y`2TGh+WK1agw!ua zLvSziF3tt8e(%-`&DCjjPJ>e6y~TQ-UX~~NfN(AO@&U2jGWZuu5=70sS5@dtOHlaJ~$G%iBCz8^@_`JguA0II%a6dY)i z`PX*JN(kJFZpnbJT|cgMh7lk4#2!+TLYFrwPC<;e0+xGuA0j$8g&lZw#<-|A@>bR{ zLQ@ITZu*=;7H>>4oMH{7*z-Iz=v?JaL5Du($KxhJ)b0^1Xj{(g@E=k;DhttR-jVMm z@&(f>{Cr+M;?nIbC$&$njjOm4?}tmqA^Yg>Y?4LPm93(@4pebLJce~X7FQa=d{5dv zo7QZAKq<;S=PR#{u_0d53>n4&G>nlaJ7O%R%}dxEA~VeIy$sCbvr;6p-ATnaD+k0L z(_cof38k3`(X_NGQ1jaP8t~MJKAxqBp{|-}KarVXX0#}pL&lmqc$zhw211CI2o+w{ zJ=78H_0oSfp!fOJuxhA5S4Coj>e4MU@o{Cb7-zt{k84jsBvw)q*fX@G8E``?2Isd< zkmH;EaY%cI`F{*zi~q+Uw(-tP$}(r|xzipI807E6AUMf6^Y;AY;{GEgV-uL%j5ww( zumG!qQ89cv&6`2dDPAYH%^U=Y+@e5y!DTrFk^eL$=U3wkzj=+3+zzJYc{)sCGYCc zLdng{1)IqdIXE(U?R3!PG+no4AUFP7F1FcBaQSt5cwz?+e?_;DiiKdGG*{f0!EL(o zt%;0mnAk3JoO34*!N6DHp_t1ij@_nD?7?uo+QC=yxJ#wTesLw%V8C;V# zm)azU@Wn<*w*`;`&%`Y*rFy9n&7NUJ?xDvJ#mfCcMMc;(NFrwR>k{rdXl6#EpcY!C z=v8qAb=JDEBUQo08VNs>^P5^N-HZHbq!(9ZZXKs_NQOeqkMBY!p(|a_x4mGZbU%s* z?lE_+^V@l_fFiue!r){&AwmTbL`*$-nAYZO7suU4{@JBpq_{0BH#4mwiX}h z|8<`kszyfsgt-wHe{;bMM6nycI47^}pGt%jKd8#XE)z9*xnOiE>!zP5e|dFAt65T^UTR-nQP$ zqlTi1s@IoDKW&c;g62qEk9s>KmE~C88JBRjU08G@d9Z*XLLU(ylJ;9d>)VQao2~_D zPxcnow95!EKhw}WD9*Sw&M2UU+EU9bKAe%s>znou3ruazAPgeC^LvFO4ygXHq>{k8 ze@bGh_wDz<)|DSpnV~g{HE{lxNRaZ0)2Y@&(JR;%)-gx@y2X?tk`^rwQJC(Za!hRU4d*+)VYaC@QafNVH2``AdO z19S-f`t6Z-aUGYslt0(nQhIc8hV}O;ZDfD*t}P*yE<5XB%6*iUFFKV2yi}xRmGUg} zW{4|K8WWk@QE&8L$KKu@E@(c)6N*w+wEyrxG26coFWDM`8s7xoZ2A_6!R=<}lEov@ zGkt|k#1D~3gTTV4_8E@?tA~bx&V+xuSRt^-$`5q6j&1qi3(k93WjxYT5 zDtl>ck;P{5*`vBmImher>@jYx!5zx;cm))%CX`U```$!>K@tN_P^`m1y)e)<)G3xM zDk)s1=e7v_>}3e~^Zd!8@D}=uZ6L|J_j$}!yUuGJaGsHY_t@aondTIya-_vQ-OsX6 z6*`#f&W`?5dvpo2*;$ZT)j9c*gJuViEx1bsF8%I0X3Qg-B3)!V! zVpJ1aMz24!$hEeD2$~D-$-?>1GT&B|<@eEg@rS+y|6<;8_NO9{xTk6}%;`mqvKMxc zhLOAD=Q|d$t~h1y+jKM;M{Bc=t(9JD4(dJiTWj&6Abj1I+ct)N?>;*^^6`NHbZ+(} z4r}wcyKxsWq6~N#YX6y$crPmky>zRXwu?of( zoNxmbc2CH#NIy}iS<8{S8;LAd^h{c!LXjrhpym<7>kLXEG9w(42+$jO(yw= zoe->D(-o;crl(f^rjOU3fx~;usiK4@?cPtMkiCvKq*b;D8DY_(aUw33PXkJPbVE74 z+n~(6yjK|Wegw;Ez$N{_(wKLH-qupI2TQMpKJ?ZQD66=m*vAU$RWjWwJnoMq5TaK0 z4Qd*S+1O-QnLg?4SF*wPV^`>eb7I@Q=Zx-cY3rldOwfzz-Il&Pd|e8h=(cQ`F8ZIJ zx)}*(8|C>opgyP{(5Eqa8CVV`U;=5S<$k0u?2aBfaRWX&sJdq9zUi=kC=z1X7~asI zA^dHh|LICg?LGaOCRv2gcotNRJ*vV>U??bGwgI2_HMqBMlg^PnGeH3bl4=@zGbWR1 z`JHyH94HJc{>;K8^IU3xQlJ$R|n&N%Xt~L5=f=Lw5U#a;AW>2j?3@@gp={ zzDNINL6qodr@v$YTdQ(mz0(LJ_!jq>y}Hd(lChATJ>zc#i0tTI;*2A;(c`lOKWQ?8 zpVLThxtBNU+im%#IJYwCZyUJ{h0l-#dzucYC}fBP_?)UMU2Mkma~;7qoFnM;*O8HT z6m4lt_x>r=wO^7&j4|MJ>T=8UM|bukD+~wBpU!UO7q_4o4n|8=1;A>M<4SwMB~ON6 zj!{DwAxwT%VK*nI64X638`%ny&i)>sJ#7-^#qbKfF(c^^e8^OEd7{&NyLYOSz3!&9 z`O&8L+s#uI2f#1MSJlngn3pQwot7FBJ_MdEN)FJ|R#FPi`iASyhxr2)Z_bknv72)9 z1alUT@c?~IMt1WNrT520Ja#N7CO#gSjf{j(5{#zhy^H-*H=+Xv*70J!!LcyLv~8sN zYkw`h<@~#@rLhr_-{$oFL8m_v;*I)&n;8-9IX%(MMdfYf00p5oTu$WQ=rvQslnR1{ zM#Y=Os4qf)8W?vE!(+=Z)r6Ri4Jh-^g%RlEDe(c_Ymy31*fX9_ymRJ1ISJ5!(R+=F|#h0+H5 z6-wyfo~|P)j7MgS5IcklCoY6`U&{n`D2M>huWhG~4FNzlg5c@i|C!Jp)ukm5X&AF| zNL8Jej`M*c3rqfvd%bv2EtIbV!sT%}7|0P zRe+`N4iAVJ6Gh;ObANZep@~K+E#J&~ur-D+Nb^utCS~gBNUt5oom@cs9fP6vQH|veeGRREE@(-qI`wqJ zxU->dT&I&4J=@je(DrUY&@NCVSo+zD&jwMV}x znDGwIBELjZZ~jmO0RhgAa1-vs6ikdJ6WM(Mc+*gMomKFktx+=~2S7Dxzu;oATsmYA9#nvE&9aMn*|N zg6*J0`_iyL`U#89n%}9|2Rd$0SGqU6=Cz+{evb80LL8#R^-K%4#0w56aNHT&ejWY7 zyAzxUu{sKuaYTJKKtxW6P8t{sMiL3J=3(0ir_TC{ol{*2qg^31@JH^G-C1@ZOFl_s`I@8T=OY<}EuethCzY zB}xtXU`t+sLH)sRSwODvHQ>iJpOzG^J?ksfbDljbzo?p|8Q_d8%Y~|RvU~>*DCwWX`*&_yVm0)qjGT(!2n~m%KcN z{v~)B+yt|AgSpwce3UMh2ni%;u4hJdxFKj*o#t{zu;{ag6xd7h5+l+^KYYE_pt8PbKcE0<5j=)&T|arq9>$Nq!dz& zNUMi*?CM_lXquwR}Zc1+0J)n_odDNtF@WCj`9f$7r0hpElBm(C$fvsZAx1OkV zcVD~WY6Ge4vwvQXPxtPOzoK;bZDyGYd4Sgha1oP9hRaULE&bJLBW044f8;PXNrFuk zP-(M?4Y|9SpO_Vdy|1e&Sm1LS=t`h1Kn$l5{P|v~-2lU4Duw}=SXKMJ);Nuh3k%_T zN&6k|_92ryV#$83qpQGBRkC~-S;!LW%wYUlc!C*f){Y9?>ad}t#5;{!hl3H+S|G zk4g6}%^0q9RL#S9r&nqEnbKH11~Moi1XcB~HjC>=ZY($!E?z&is59VkY*U`?t4RIj z3nqUu$qMV}eVR)V?}1<$26FZQb;SE0h>jjlCJlgp4L??OQ!m}6{%>Ca0pS1fH{G2R z?Y(MPd{e-+u`i7B%Hu)`o?inV;3zW*X+p$>^p25|5`ozZI-62TM+=Xyq`uL-KMKLq zmEbLouD?yLgafHHcnD=ie?>B2=VXj?y6}I%6#f`utQ?{Ff{MWFCk?i}bGl>oKJ}Gl zWvo=E=o%R!i(7MY%WAZQi=1FkAp5Q#zV64|e*`Eb@XpK*8%V>XaJba!g{Ac+m(K!D zhgf^%{)FN>V56&viH)G!+X8XOsv~ISCfS6Y-o0iVK*3c6Ap1erG$O z3tN0b>|bsBcYKUQL7sc{^c1xO>O|nqUBl^%jf}(y%^B#6jFcA<6Zv<1eG+p%0Ge6? z?xDcKOPH}~5DGjh>RLT;s->pB6@le&Pz{@4CI#q)*Oz|1V%UdO&m5_Zgq@B-P) zfxMBIM=p?cHoM3SN2s)j5cQ zz*Dy!^I41zHQ2RVEVQeXJcLtvw^K=&axh-_HorNFYtf;Be}!(g$3B`0T(XvsvrmfGlMAu(o?L9kn1() z50(D}q7Yn61quKB4%vILh%4rX!Q_QuSpTjI)^@E8X!6Q0aZGLJHhA=nbaOgDmv8uo`M9 zSx?)K{lypRM`tBC>&ZkUH$Pbl!Y~EE+y^Ho0kysijRK|+s~eznlwYIXIy-;g@zcq0 zzSKq$>FD>|>R5bldiJJy$}@s~VB_6iM;H&qjq@x{7%!h~%>>GV`AY=67ugwNpoL)i z60d7xaU|BE6Hk|mZUJ8J&Jm|HZ!h*RMcXK74ocZs;a7!U%=6FC62#w;;s0HX69D*) z>QEgFI%^Q$?$dwaWOZ|hBeuW-?94MvM4lle>t?{IiL7BQ=v_SEXT-2w@YCrYI< zL#HbIJ@XjfgB4|UfuA!_&Ghhvda8QE?weo}XpDv-4I5~jB~x?&4CNAJQnI=0G5=fq ztH&jM_Y~Y}I8*lC5q=eA}5j{@G%zTy*DtSv(vw;9B-XSyz8C+ z?QidUFy!XM`|0!YZ}eHfits>(Dxs;?>sCROI5!`)mBHwAca<(26!7_>ceiXKs^{`WGD6XXM-x#;cCrpq`fQX!_5 zE>6DM#kg!(Bp41n4D&ru`H|cUM15|}<6qnJ7d*51{{K70m~SN+0e)GTI)RW90U8dl z4;6-uiUerz+#T;fA}M0urd2|Zs@@C}fo|TxIBEOkVD*1K2IDNAT`@a$8~)Acbuo9z zp}XKSw$iHpWPIpa*+^9>4w zWfb;AlF&$YzNz~e)8kL(^XUX{+jOH*4qo?`e#0p`Qs$Y1?Vl=tmbnPabm9yK;`eDVdA8G!MIteSFmD!4|#62heJs(OV{C%70 z(d)jHRSRLqWH(nPq@ zO|YGX(yr31{CJqxI-Z$MQj?v4gWJG9sA2hZQQRi*D;sp=ekm%li?%nB`z@vEyRB$M zJKiEV5b}(8>jW12xUQeKH$$y?cy4X%_^+GcO~O&=1a;WU*bWEDtNac3po}6La+A`n zVR!BNOJ&K6k162X_jpIBk`%eEAEc}1GlZzVyVQRp#;};h8P7w|A8aW{my{rg(EdFj zNxu{%D_dkvqbsTH3%l;dq|fQ*qnBVj7QnG`+qaXbj#OzX5{6~rtsHCvg%VPzM-zcZ z7}+$9j%5DaA*EZIpN8Nu>0Oz z3^>ZO$X@+6r{SA-V*535d(Be&W?YFq8Dy-2Evu`Nu|gG*kwXsgb0o%%gH9gbAS0*9 z3s~Rju;k%ToEcPEt!--1s3`NaI1LL4?faLXVyGeGXHXDEzxtbjArtnBB^sZuS3N}1 zlR^+1{!axj;dwbFk8$tPw1rj!LKUa4B@}zqURxT%d9J4_LXw)&Qh+b8$r8XXP(#ua zsG_*akwxMvOlry9%6)K;rd>hF7D;=l+HFf~}E(oSgjX-vEu>r!K!+;`DL2 zbH6_W0|@KJY(93nUB0*0sXmFYV67i!pe7o|fQrT5-Ob*>L!^xKI@8_4+ZVsU{tuj1 zAa@JEaI+8*72Wv&beXW_I+IW~Ug6*qBb6_0Kz72c3{SbWVQ~4=b$g?BOXg74-5WdN$hxok~FfSX- z;W{={`QW51chhxsqkn#h2?>+k+k!qZ;D(OdPMI>9pg1wa8=#&7?+*M8^yBd6;iP_; zeH7Zn@ztWDf)I;pvYZE516CJXEWj0H`iK`l%*yS~4pVl+$9Z$Y+rKP@j@r1J&v(U% z-B67X^Y%E#s%GS$ogAko1B#|r?4m-iMfQoG3yu=n+8Z%{*_s+*=-trJ`IBHK+C+=mbs#R_t;g4wT$n^9%fosqSAZNDB1b+k4EvKg z=!kryOFc6cseDs9w1F1B5d7}Fwfo}nXL&0iRH?7C)ftMe3;0N$kdWYWdv9;V=I!J2 zNM6J1+Tqi&bA63PkRv|C_SFi1ePe4h@c30S%nw+$+voH8;Lj1fTxS}}^lzZ^ag5ju z4UFXN&F<m|za)5n%Bv4U&9ZBsfv0&sQ)Ql9PD0(MeFZ!fn8re;H7)lBCiYyqehQehROBoU4 z`kC>aHOduI#TW@DE&f3X=c?ANt1G^0Mh?P{M+Hpc;BpzFLV6p6$B^!@q*(@U?>n3KsZY%QxwsrO^D zJ=LN2D71>_Vu2sFnK=@m-@j^aujUQUzhIzQ#cp|(mQ$Csf(+HrW5mSfO|WjthOL37 zKbZ6urSOry7mCuXpBEu`v{8u~OyxB~p8xvFaDY!|FLyf3q2e0nYW7#dYoVB|8abVfR-eR#mDB6ImdCOve$}_KcMEYy^inH}$Tia8#LtxRygvrs2UML}XxK zXU2`g933ec4Rbl#zM zz9`#JwU$l%$BY!1Fq!m%iB1qlfW z36YkRMnD=Qlx}H|ZiXTuDIwi4nn~xFh)U<^6zSUNF;?Hb^?Tp*`)B8D=RDilb3dOu zuj{@>vk^sOt_{h)5857qt&9KEKB25H^4rFleh1jLj&FEN<07p*pZ@or9g7oWi8Eya zmBb}6QsetoW2Wn|^AOIAmY~43HAP+H3XoFyb8m-0Dix%RZpa(XIgW2y@ea8WV~UV$ zJIQ1JBTwewX#9vc2&8Pm5u3ubBo+=)+hO9^&l%xvRo@^&@4uYDCQ$P?mBj4nj79c)lUIa3~*hh96|E5eYo^N%g>V{hcE&e;{U zmxVDEq8ZZuN-F$RRcGw%H7R7ON3&ZB{L6QAbP;Wm>9&>{#jXZ6tVodSvKZGi$D#z? zay3$a>1}Yx=kQm61wQT=M$r1|)~(;bK+j;4tVgfTMUxll;_UzmH0tmO{Z-hZ1h!>UpB#!}MS9{9nc@LdBXJgI6KjijCQG zrbPdpb<8QSp;uM$j2Ka7pB#X-c0a zFfekcoReO?vn_CYsVc%kxIwO^tU;k1BSKgfZ$tJ*=E+rk-h)5HPk6DZgYU#J*s~(G;?tYp-&Rk&8_lz_(j`Q2V;uF}6q)}N@tWp?!@b{P-1*L{jKAoULB6cBj>Xl2Bl4WhPji@Q z!!}xF8NUqwHU0dtCM~Pog0oqiApO+sY52_%ekQ&rRlP$A?*mLVd^Jv0^(TsdtxZkl zPjfx70`Jpse=8S$6Loa;te%cw1AvleX58pSGG}LJXXem&Cfof3cmg^_w002`Hr^xZw&YecHxMtQv%;12(7`BjL5#-#RnIv z`7%w7?Yrl=COluh?PN3T$^i}hKTn6ssW={V9C28F3vlGe?^ozNa7SCr;(Sywidjr! ztvEWJ7X^wen@ot0kij6CEDA33e-|7a#d zBybfhXV=Vg=kBfZM>%wVfMreSKp4gMYwZUP$+5bIMoPa)9d3S2$E|+b2QH&52b3Q@ zH!L}}ASH!+GAEdSj6}= zg;sFhRuc>9kV+t#-u-&`&U^CRW6lq4rmJf-mBlwO%-NsiU!*$)v- zq{F|TbNT$7UpS4P?BZezd4{J;7P$`RzXo;W;UwL*2av4I3m(OVJ%3P}O94OV4}N&o zyF1_NjPYvTZEeywz4!=miL(ASZZMEyRPOfC{lOx$eHC{FMF6)zuAi^xGVkzjZf6RD zzvFP5trMH|f18_x04xY__c{TNw>%l*HH#_RmmClzl9+n)B*^;=iyuADW6jrb?l_Dr~!d$p>b!~HVlbCHCLe<@rw1EGCczj@EWm^Xit`%BN z+8uS3UIYFZquz}J=4UtMa!pakD}e5BD#*p8=RbDj>Mt7iR6P6*s9_>f*U9dw~rT# z&rjFiP=5&Qdizk%=&+Uq_=n)N54k#ZrZG+?5q9>=rG#I??+F}gK&a75cAuE(-{$I? zzoZJyT4ZDt=Q}n)?%vU$yFHa@CJS}&gHS(5lI_tvCRWeT_0(JHv+JP@f(7@1oj3A& zH5m>+R`-OOce6*#n4<^K@=YRg+P_|fiq&YLa_K8MR-2MOzK67G_WU*F{6(Dg^>qOi zA^w;mC^yj9G=oK7%Pn_>?9=-Afy(d~%_F2Y4lTIvD^s5s97*YZ?tn78mAN^OM67jmh9iCd z|J2dU>(qMeWQ$%dDZ<}q2iH}sQ9=K?_1}P~9wPy79)bS_S`d0#9*PT>bL>7T4JXLe z6nRzH%=c;9ea}&q_EmZL_Z(=_mAE)9*&|)iR8JMAC|80TE-pN(u)>#;{{DmIJ56$S z+{Ewme@GbGo%|0Lpr2qU@*p$kjzSR&39X;Kvz!R~(Fu(qLi1fJ@Vm$eX|vKsTuHU7 z$ojI$(aMVJ_u{Vr@&%=WukXl}Ib)6==qG#=f2=%pSU1K_d9_oDEBpxkjNDxCmiW`J zG9x!x=@FH|T0AaX^-^4~ek*+0wxoPp=hH4m{?4IgNpaZ?2YdR1=x)Vt@E^~S_K>0; z)|&#)<7E=nQe4H@-x9(fBd>FgA4RjH;-_7nvDerE$dV|@hd)y<3Xr@K4qmjp4I_4s zEqP9%{w$y9F7yd$srd!F<-2r(>dE$gsq|FSSEfH1*~mX-ocb05KLL{Uw^1Q?FzOen z3D~U<4%B^EjJut6TWkj)B+(mp)Ip2Jc6j$gXxAeV6Tae_lb>j=O)@Ya%){p3<;;Dl zH;iaf!rzb5``-U{3*-F+Ouz|~b4@Ud8}iQaW;O-^WfEWRiusW5J8$D2OlM;tSRNjW zZBZEvK$HN>u?$$p9-Ir;J^pWwqp>G$h2Damurh`68CUQL>!J&>AG>l$+F+D|!+H$8cLBasoR4yVZ1x3PBM?$4a@=)kK-rQ;7wTRcQb}$^Ny{JRzqH z75#?=qoa+UqVEVHDrc*d1+$cmE^S4B^#fL$hyV}+RM~jvf{!m|YBVK6c|k&3WylLeuys<7p)`^J4Y{>82my1NWS^mny14p=LX z8iHilLp2si|9twiUg;3#T#^x}aqo46^1=D~%*T{ZdJhgi>-iR~_XIkoH9eD+GOz{6 z$Yn3HC=KT_!uTpn7}x*~C&Y9ib$!QSc;XiOEh@NLNS4aryoTU%g3oCzV|^$5x{Kyj zkYa3s1w48uDiA<61X$=f2^HL8?!IIv3xh8vT;CU;dKnBaF*hLpp~grMk$v=2Xz=gE zev;^(;gpn&82Km~y7g%%NbDn{nB0SBzhSQ@*kemjD$|a}b~4#IiA|604>KC>+kB=J zlBa6o>5!ErXF!H^oUz`|Z`Xw@&#UwMt~S%lVJ|-mLvD#y+LMQkJ;J*ON2*yP;LSp} zLKj^k)uZ7s?)%ji)Crdo*K=d5p218@_pH>Cearq9?iH+p^K8hpOjg->gQogul|H*JPcggy9_Vf1boAk zB)x7Mm8nTW!WbZ$v?boG;DCs4N9lcO@{L9sOZQ#4&{u=SAHdd4@E-1O*!dqg_W$r% zH+#6P;xj1j;xh!jfr|z8buU2arskUhh_Wf|%>-71GMnw-c`Kb}>F5g+M)Clq~oV3V}bX)=b4ARrMh3m0W zQBf_nGc-@q&A>ZOF|_GKYN+{0b{uTOI}paHS+%|+tBAe=En*Lv*}N3C{?8V&aP!2B{Nb85O*jsynnM0Y5K2*S6}Sn8vsk4taEScJ*F#)-5$ z>a*+B9G496)v;8Q;1BmJJYz#-Yv?R|c{1gz;qGLe$PYa!y*^LE3m1@$N(kY0&%!7?3c78cv>|u*{ zd4I6xrSFGsEXOHBBZlW3z8jx`my~|`S|y+5D{h;X#^5c=>G2V@a^kc@U1LXx#R4_+|6Myc7u77ZC;Yu3UEe``;8 z4C#T&r`P2}6FgeSlpCNIxewC-z$a*?-*p2BW7<1?Q=??ai08PeG6&(BbV)kk;>NlwXw_td#zP^5^ zH7NMzj1ayK9)Ln0uJ?v;B}EmUt_8vW^r^74a{TGNJbU%+Y(BF3Tdbs31{*cv^LJ2g zIeA${JS^YqDLDhHQ%Wtx2cD1j^lxRGu7-F~%=!}?n?W>7kf+k_08kqbzBnFNtd!_v zB*2mV@364qKV00(ju`b6=Mdz{!>xcuGX|wDhn|nUyxR~)dm2Upj-iJ?>h@K=e6}G1 z%lo{u&L8a9l1d<%VqY!GqR^ungD64+Wq1P1QpATKM|~LJ2`)yIBgh;rR=m#YD$Jq7*0vU#xBhwOa$$nQE ztT0AXiEsb;>$apXoJ)bxvRLp?M3BS%OBf&bMdNEJ^znfP57)>p)1e3rA9QfU^)>2;wYwwjIaalp=J&o z>jxl}8Lmm}!@GHJsrT`v^@Uf`Y72x1k;WAO`m2CzgC?*hGZt{lToD#N~9;5K#?HU&}2*rK<{G%8LQrw!uMaE^@M0{CKEU{&@17*c z)D}VZ3`%rZ?A&R{bKWr$fNkdmnlL{Qv;eK))X&MPb(fp0-|H5TEMF?R zo$4dbN zLjnIW>2x!#SFS1PGgx~baa)5#N&oYUR=X!N0~Tvju>EL-9DKb1Owo_VJY49Si{JYD zp0-oQ2wbktN!8sX^j1825I-zb!2NIdnaN@#SaZYCubuD?0q%i}C?Z5VTUWwH@P#Pb zm+rE}wNFjKSj}pxJI7%fyitf0>${I0w$HbPOvnJlOkV_jJ3<2OIm1K>UU#0?U?Ak&d{C`dyw~c^D zBaWYAY`o(JA zJFTQ9%QxkVy9InrGU4Cv4I8he5__DeVmOCqEHhng8sDK1qz+UU7SroU6VstOGaAS1 zmyr^k+x*?PT2LThfD0=#lb+kqQB)`}5|C7o)$PP2T-zvjEIT+6WF|+|Zk0&jGIbbV zj_|x76MAq=5|bIwPXd1N8_v{L4I~Z_2?{B0;Y-LMsR%T4t5@4ocF(Wb;v*J*ILt`! z$d+;zBCYBKjTGPB*wqqzld)l_q&6??ndkV%xTs57#cP>RU(v<~HN?&}uLYpX{6+uGEKFHReO72L4OvKj$fb(Nn+VLA`(jQ4%~tqeAZj5K)q z)&TGydNX@r3={$a4+57zH$}s^crWJHM}F|= zklLN^-!>t=RIPtTb`gRD#`S6+5#GB{-)*|&QqDfQYlnyU;l~97`Zqh-Hm64*h^)n4 zHty_x-_1X^O9S^*yh&P-n8%~qYWczfwb2>}%AH8CKKwkA^U+-)o4)V!OcUi0n zs5p=24Wh^gNrR9K?aF6x&~Jhp^#2`6_20rv-T+=<<$(#c=ph{f5DvWp%;}5vji=g-XD7g_3tEI9h8!*@gG(*3}$N+=tIay^A?K@>rd@zc>iE zg(Pchf9A5MiG;HAiG#gTGzmkvl7WH2l))696!JeWwjcY6qDk%&-?>ZnfZF4GQ(8$s zA$`;RbGW1F75JQ?{L9y^kiMxP!8O=nZT;A>EC6n(uH1%xBp&_<^^Aada&2{9@B7^wKp9EGLcH)AA34AH@*t_^w5~BtJvs=sG1P05&r0^ZTcMSF(fHpnm($O42ZM z8E@zBW^vd8mPg>+8J^)(4OUt(4J^I*{RwkVB1!@Rf+!>CPy}otq8izO+&iDzN8CE- zM*-d z^kxI3!~sBm`sQW`jWlt^qXc;z-sNicE~q`=u)IOD0rdMEP-D9MPN4=|Z@*5yO~;+8 z!^o|JHAPD2XBtu1Kl{T2+|!tK24d8y|2iOVX7gisA?RdN2HSgdZ2tRJMGNUd$VgFP zE3rh)$U<4vRq-U}_)Lsv3m}yD{UGv{bEdg_Yp8g7>YEs8%B8sIr+tyLPjb}TVy>>; z29%!t^OKpUza(T*`Q*nGHK`IazBE5V749m!lyqj7z!1Hx1CzYx9ms1gHADYq!=gK! z&*6)cYJehBtidy)fJR?RpnXZ+xE>hB%Y-OR1z?~SDuEzt&)x+K!ax3(=ZM5g`_^lp z)E%P%fqgK&Gx^UllN_PdNscm?k!g`gM{EA^u#Gx+ z=w|7kdstOZj$Quyorqn*n}7Bybgl}3t4PHKo}fOE>(w7OCJJ?j3Ze2SctKs6b$?wO z2eJo3@{p~Ku^YbiBo-FV*N@Vi3jC}KzxW9(?He%a!OVzl1LYexc)L*%Pz!cFRIXrH z@Q3AC_+%%9@IS5YoLq+QG8>N~Xu6nnTlKk^L_g(b@e~-=i5mcH zy;t{6%#;$bH=%lS!^D>5X?h&U%7w#JS%_>jJdS27yY&M^)5H{9QPEivS(mLwz7T7@ zOyn3m2(!m@NmxWc$(OMxkD zyH$7RKXm_63LYOi(Ce#y_-}5}SY)ClrIKM#hZ3|Wx6p>_&zbXO9nWn}@VkrSCV0TcQxd*lGYrJt= zbtkeTYlPG6cotX$ooqxQ! zDE2F2RnGXrK7aDBHx@#QJ2_9y#--Zf4>sucm7>Do*-qUQOgqsKBDc!oIcoL+&Mmf* zoF{Q|xqUV8hb>ku>_#C1vw4xNvESCac`OT$=woNEmD_t}OJCM8-R;ejAjP$z{`naj zxxVI+DAktCz|L?V(dl zcs0rYM6dy`%zVhU^nn7WUk&#Xg|xyY2B{ zkg`=MGIr?vp}_U&r+^5SU3B`=LyBXL0?E7A>mh3|OXosz+@I-)L64+B-|E%iabHX7 z;w7$MU&-!0S3k^dxpvwm(b%IlHh!(sgq-3CtFYMQCHyNi#K4*?`jpQ-leJZ5CfVEK z%}|8k^1;!3bg4q`hrusjGcyH)NdFytZd>AC#r0GjQF)KH23>_cv^e;Nz@jh0z!8Xn z!>nmkG2k0F=H?pZECk<9#s!>?MJ9U#OM) zl*XO+&xl`Nv^avg6s7O0J8}tRlTO_FCBJKYc5Kw}fmt2$I~-ARkDBZ9X@vCbEN|U{ zcx0n%4ipKjD7d)1sITvL96`23;zz#<$UuFHmGXVdQ)VL+3Ni>toJcT)%Co^P zN=cSE%D29>E*R}i%5Tk2)N?2Xys76gV^MsWX_9o*_M33JQMg#zuI!_fKeLu|ESoN6 zr6}p!w{=Y&w4(C0#qN%WFQR!^THH-ueC*eJ$W;v5vdtWfKkx6KhJnm)gXn)6vzgKN zM|lNcGMGHs#u_y) zZ^zPbw?X<61Et4)PZ!zt0)|C8a}M$_`vQp*YQ^Az-I;>qvaDumvGLq4=2W@aA07E+AmBsy%I58|Y|_@FR0I|yEKTs?KkO-YLJ)j*esxyrWQc<} zr>-Y&gCGbQ^V@>6d25vMHh|V3lt-*LaveN0dUkNoLC(8A}5>8C~_Gtpiv z@)^D<=t;r4a8Iju?7%P+_3(L$*c)T@%_FW}pvEE!7Hz1_THLTD!McGo7VHy#$R{tO zXu*7Jq>j#}b;nHZ!jMKdG&P(TC;9j|konZ>VAtrc1o>7&ADtj8bq{XOdU&9qfksz+ z&FZHDlV9D9hNb(!Ur+Z%{iMb&Zk5E6JM&Q_mNe5E=Cu^R>L@&x009HTLub7i1rj zJ~wsd&v+e}{?tKhSo;LGDZ4@#SJm$z`6@tG!r8M2RP+8*>+vTc*R+AqgsMXyeRI~t z7Oqik_k z>l*#|o)1u9YqXkvV$K!F)y;6U{dQRP=#yg?vtp#w`rPiQjcMmUznn4M?h=sk{VP_gD((wA0SgEA1qceosf(&iYuqFp7w0&?Db+yFv%7x>rcJ8C^d3WFnzx`nHEv zjG%cESV!^=tr=-Mp75?){DmR7^b;G4mDQ@~SoCt+fKDB)4ddI#KckrsaJ!=g=3>v# ze?M1WaJW@>PWC;0DLPc%43KVE&7|@e1IUH5!Yb>_)4_2@6^5E*QwW$r2*}Tnljoi+`|w<)kW6_OcH+E z%uL$f!T{7`JTkl$w=~&&=5W%pI}`-EPqmI;Zt2Y#Q)&r(jNpG^Arh12%<&9RW;Nyf z*KBu&`>za^|2+`2U>9`V6TFsJpy+oQBuyRd^Nx5#G~~y7L+mDX)2{#HVyT&b*?vua zXK^NeCC+wpp&5%}67Tz2cC}K`aUo3=8e>mYBzMYn9OS<^A5Q7=TtPKIu@D}>~qhC za7HfRQCL9P8A!z|U?=YJk@s;hbzZ|ZOpeCQHQ|EupWIe&48 zb56StZMuV|X7Pw9>}dQU_p41#J&D{Jm4V?QI&@E^b3VM*2Z?PtLePF zrKcnIuHy%@38cKe4oBQgj{AM8P%KW6l7@5kx(S6)CJ-UG%=$tjCTNMWvcnHBW`%@x z8CuR;i;4^I;7XqR;-9k@u&YO%+g~|mNU&u7e9z^U-i!-oexb+eih5=%xta0luE%)waLe#>@%m z84Mo2cYjz@W3!^*@HAwcgMnfeUp`sO>i?1m8|%vjbui&H_Nw3Wp5)~j6!Q9nX^zwE z^HiFL+M4aU!meo+aQ=OwY&9*nQs`#0s167#+I zvgt#9i|unqBIGKy-w4{xgqFWB1)_6%+UH0xmf;@`lA}ig8a)J6?*8bYV#43|(>eY$ z91B};!~{F^7FP9J@xm&&e~dXqaQ_&{$vzwNDLmZr(aOes1Cr9*Km@F@YX=1&7tk>*A;k`>b2M zHuq?ep?HZ;Ji3vDzU^RDvW`&aBHwaQ)Jn5N4a8X*NOk}w{aIgQ?K6!^U4!=$|Z$=;G znghcWg}!5&BTPqi*Q!G(8e-c2tonUXD$Z2HkiNXA{}rs&#MS3qgPHV?Ps66z7?a|UEj_tV$Z6rdIDFRdWNN3>p&}x zz8h`cGguKJ=Z(q;fxVCmk-q$GsfE4;TqFGS7MCxHG_{7*S$9;B%DVs-QSXp22ipOM zLs-6Lk0aGG!qa!WHLTe+1Kxp+!e661xU!P5(~f>#Y-|m2?XVQ`msMxod$_f6X|UVR zvvge|sk@zjEtu)*F&fdOioyUJAd{$S7-m)%2wty})Z5`P$8=>QO$Tj{?oR72>#X>i zY-ve(c58-yz4pcSgs`TASN9?w*z{D@ZFxX}o*RGQhvDm<3nzilzLPg{d!csibDMC# z4jmnYRlBFtgLaWe*3P+V;~!zV@|g2N%R$iy;CLO#m~Q ztlV!tt5!hW4sgS%?B_T%W{ z_PhIJ*hVy>p;4mFeZE6%<+_{n#oDLw5zFPH$i7^!KKGTtwdPjWx6P!UJqwdySEF=` z`M^4ab6uUC-%03K@5Tss)P*XT%CynMdNIUVZ!~E8qk>*m`J`AvmBXTDjRka&cn_&n2Y$FiGOyT|UcIbk4`vCxV z7RQF&`8hBy_29qUUoNL`=}y6=(;-g&+*>=jN?7y_wBjGTMF`TY?@aUMCz*5*b&ab8 zdNjmQ(X!esFC_?A#xjQsYc&orHAy`vlRUKR5j7g!@}$Yh>mj+h^!BN?_$<0MS{;wz zqkQcG1)8V2vsE^P*EYx}NdI{syVV((_Xw|b2dbRG%7GP9-afvdQPcBNk4wpAih~^G z5E{%*2UHKcOdm+VzND~ zag#$oQpm^!A&#JYXk@0r2r@38&F18?k$99>+oykFUFxnRmlQye{tWEukfN7ICvnbh zbSOcA3$tO>FP5jY0JYo%D?=%BCER< zX6wTK{Hdee2GhxXcMC(R7#?kuX`(r|E5CrdyS)jo|4UEJBa^L42%>%$fKjmG!X3Or z0p4;&2@wG9PvN6l6li3MK-zG!sP~TSJi560DD@J7ibyOzWESDyj|rV|wh@+Z(6mh) zrcdzG<(1fNYEzP!(;nG3@$SP3z3$YA@dea|h6@?V({+&we?ewnffdPcNMq8qnd_d@ z*$}6z&7xKIzg4}j6_SUWQX&n1owq}oh#*4TZ* zFYApQ(x3~D8+xiAsy7v7u;v@=+KMR>I9V6gG0MhDh;lEQFP3RloP13QDC_l@@(a*5 z2#=cuA#4j+CW4q0Z639c=RImqfpQWsv%c~hR?lha&)tJcv*{nyU*FclBDJn43-5Jr z7SxpwmGiyM>!H7Ns5)IP<4@}uXpo`g9@Q**WEHoM(wXn^$eF`=HNXZU-QQQ`5wtP= zsA;+R`vH2&_-!W2zG4^ng=Qt+pI3>Iym&H*Ofd8mTDjSIAn_)!ELAkTY+*9Q4gMA8 z-+i{fn!Ljv(gnZTSZztS0;XrRx&S&YA(&eWABB8OWgf0)0}^D7gX+Vl2yXsx5aZ3# zC(HZsS~vmCe&-9F+g7=ZbZQ4~%AZo;pqJiN)&A;|PFs4Q~!Ahktbjm5kiqGaS zwl1+lr~NgjNrY@=dl+z2$E|;XB+#$@cW%^nCoH&rXDJejdJxw#$z40-t=+5q>sQT@~J(Fb4PTB3|vRmsj<>>I|*Gv zG;jCEXH6d7o)`IKssLY0VWZn%wSNG~9~Ov0uY~yB%;ZNA1Kolv)Y1o><${lY#YvZN zJ3&l&wO|)>-Oy6DTBe0ZZm|3ezB%NZ$rTlCy2l4jYM>HphuW>JfN86AT~?;|tRI&Q z_jVoDYa{2Kq*KaX1KUy`q|AN=p3u_p-!T`tFx;q^3p;Ml&fgrcN!R(>u+QNeSe^MM7X8-Nci{QHu>LRSns<&!tV3-HV~bLd**fe4^=9_x)+ zwjYPwehX=UZ=ei3PA07<91yxIff;b1Cn9vnS9lqjZI1RGYj{TVV#|J?dvQu~aj%PW zC0nA&b)z!w9WM(D*$&0VDj_d-?5YGUrwcGHnISiCrzBMmYVEInFCd_FJ zo0=_qz0x-tsM(FrQu@|1GyRRSy8Kt7(>guRr*njr3GRpg3fRpe6WM4!32LZl_qM)#9(aGW9lHcBnDhi0?YFy! zeQl3eh}M#aoTzVsDjr|cs43XJ$+YJ z{P}yJ?|tc66M=T$ui!=9_{GL`gX5t$Z=vlmVxWklJi=w;0DG_7OYh?DN9rJ?H#58T zIAEcyNjI#J7vY8t+K5yC@&0xz6&G{&hZN<1Wdky^Sp8aDpfN^2F^w~ylReJ-Dx>+M z$$i_}4EbTXQ%2DOPNcTK*eh)LEpF=0kO^^#T7z#quvXwRRrxhz&4KH-?nkRqb2}Qz z$#d0D(d^8O>!qYY+K&ZT4;SfYp_9h95n4F!vzdcKxyxS)l;}-jhqY^H&C0y$m?<*)R%&rKdtm>v&!pTdrG?(u(m=L#gGh%UbIRE09!nW~lY^4{ zs6huP(5A16w0iB}e_{Y*QjAzeRll5Er@I(E0%hID@t*wqg4c9Rll(}_Eac-Yf+i4hySI$npH zA!}rL>12b0hmM)uYh0hUo--;!r{tKhj+Wx( zJKBmGm6Kxo-5$2@i9((SJwRJJ79`^}pBJI{NLY&I*Zcjv-K@8|#_s4Ke=Rwm-MFNamOR|kLJP$GdlT>t zo5vExZlB=2YXb)I4YMM-*TWNS0x5a->KwQxNM081!L$YZSOaLf|E^!Iw_UAxpeKH& z&qUAAyJR1cu<5qL#Wjn{+ z+(*mIo@ieI;#1QB>zSVy+m8I|4NvduP375J4~jTbY}t^m{n(GQh=s;&sah9O%!#y~ z$()3OB2SZaC(^s*Trxjg=Al{Um!0;l$BcG6H$uLKzU%nft{)Val$xH)cOI%Q?wASP zHj6iC0g2&LD_LdF`GbPJr7sDuT?Y2nrl0H_&sBq*tRw^#C91v-vNhH`>uL{^IO+&) zRLMxZsCa1|O;)3$MqDCejeY2jeT&ZxuKznXn7%El-X@(yd;3V_L6+i$6{Vz^n0i$#J$nGqe(s8)UZ$HTv2UzXU{KS z#-O0b=obPJ@Wy>$Z#h(#mBjq>i?|e$wVcMEF@j?3J&~42WV<=Jv#92(G*N?|tfc43 z#tQ-94U98#^?2Y$|NYa$l~+e@vr&$b2Uc2n`~Tw69T;s> zT4wY2pM0ULB9-$g|K4z3YbisYtWS2nGAX|q?l}a{(#g7Au2{}Eb(*?W_l&Zb4|ST) z3Pt2TEO-AL`?7g$5Ju9n&yY7U7k(i21|DrOX5XxkorjGLStB+wIRzOa=1a?8f{f&; z1SXj3tSEr6=Yko}m!?9=+@Dy;tDjmf)?R~9Y3NP4+vXua82Irt4sze|KXZnsfrOoK z)+x+uR63CT+fO*#p7rJFmq|r+_1*ZRwFgm}n<8?=7P1i$FVq|+VDY;*mwlsKYQi>I zE~A5rxaTy$zxACJiTF7uhvqF_vS|m?Z|0+^DQum!0l2-s(0aO!V$Z&U1-+i*ipkc1Pj_{nlBxJ97{lh{+(R z)XW}bWLGrY0faxl>MT~Y3G0s34r+G{J-gw{K7v+F$~Uhy3RtCE&msV5>5UCZ0Go~6 zh~w?enM3M_KrXIaYj8Nf6m(Eg;n^O`pN0Zd-CJR9aST!;2l8$7>A~EQ7zB5N(YNv| zLKCK@GJqp7TIrW4IAA|?9E$0*Oz#W3Fz11SAQ)hH;T~c~^k5vZG0YutbJbBctH7#= zf9uVy$*zC~_Ojr9Ck~!RW+?RH8d7~;Vz;)`6wx>8LI(pVn*dC~-Ug&|KaL%e%?$?N z>J$Lof4)=>GNgv0+OgPdo2vupUJd%-?00!&I3gM^&}XBNDZwcA*t$1-wkJq&WaoBg zkLvdF?BYeoe|(hc4fr~Ic`*suhYn6cF=5E_%5{u!3cf1XLL_Gk+w4~ruWRvuQD@E( zx9E^%5dJ3A1Svu?R^URRODN@tyIO!GYDHGuA03Mz$JCCKn1x%bgbdd zeyeRjLZEy41?-*xEEXk|u8rRkli!GxEyO*wy1X41$kl;huETKCuJw!U{V#vc9MQ!!&Vw&L6#ADM z+|LEBt9S@TA5d-htUur3RejqZIv(UFN%u6oxLaDs6ZsRamDnlQ2*N2y4w4PJE-=iG zHwnbWc!Vp~UuecD`B9wb=#yV~c)_#Xvoups{TwQ;97GUH8==9;uJ`-fUC;OPG=OT& z4v4e@?VzaL?D(T{XRFpRq8MiEcaK$frYLoa&QF1_8{vq?8k%Gg(B?U^Z|sCvLtUvC zv~_jx9niXE=_j-YdYQI)c*T*xEDVU79^&28N58RZ7|f4Q#zOdZbWj>^@f+Qbz> zR@Ts&;>P}kCYS4)2T>9iwn8rRA;QR2lD&Vb7SFv!py#J^ZenqK1)m5-d4`) zZk3+6qhHM?)Xj4YuS69E^(bw`LhVOFJF!=be*9XDHs9(K_R(kUz;2qXKN_v+#ZPXe zzUW>DxP#-smG9BxDLJ#ZZM^x-tm<506ozqfFS!07Xe_;@S=fBTq zMJ-=`vg9_)n9hB15>AxeU$ND8p94~eeIea#djIEkz8-3C&O)#7 zcJt0A9@WjdkgF?53wGQUh*M5wCZ7(IZJg#2OxVvUVgV^kNEG2dRI6zy^L;cxFi{Rd z3o)&jBqoB%lOT8CZ1yYcN|E~iH^i_}fge48`6`qynO**h+Iespz?%HssE)^6(m4&d zvuRjYQsSHZd=DE``0ByiEkFu7mF*17vs%wy_5Yf?_GqZLFRrW0?Is~T43UWB{TS~@ zWRx-zx$-XWCQ=w847!oQ6eSE9Zy9EkX1p?u5EC(;Bd;bsxJ(QNW5#2Cll!awxvSOh zu6x(|oxk??ti9I$?7h$Vp6@w(pZ)bUK76E3(cDoPr)vTq0z(!%PG`3)e0n)pk*dfP z3GX1-#iZOC8jqzM2V=<|q5LWFp;V)NIh0%sM*Q+R*%3@+^}P1OkLO#gx$!lQ)<$91 zQEy6x!gm-8p+W%4`?BAUUhjNle+<2CJe<7qaIqks9C;FN{2Hk)w6W;tGi^ix)9NoG zkdAxvEN{uX`fOL0z1Uwx$BK311(9V_XkQnye5oznzl;Jbg(M-6Q(k4!A=$(-33>V5 zDa%^4Bbi?HXPev5)$vP@F9Del^h#835d`NtT|2;7ydRtDbsye?nP2tF7`{aCS?l*; z+WNX;F(^-?-z*zz`#p;`SP(wKgvo5#!xiW0tX&t`QNYgNeyhYW0a=EuH;tabI+&=g zUF&8L*eT|*PB)SPl`TK2fvju0CS9Y_XhCzA>%n~CA)_DkO{#9Mej2buI$QjnuJ=cN z=vc!vZ*8aRd9%G~_Z52YCKt5f!-j0=O$Sl{5rJd6UQ3bw_bv@6iE^*~E~4Mi>rzt8 z5^k2bcT-Mfd9F(i=#8|>|9rJM-yu&L0pMT=?NPl(y2=4?>j!K-{>f<(7JYSb7hns4 z(I&#G@nY_un**Ev8Av?I*BGXMICDfZbq$ZFW(jQUs^s*NIL{9wdiG;dX`q)j!S$6| zrNYdFg-qzQy$!nD^>%-3P)h~3dIz&Au34{gMt}|?pllv;L@bifAc2jy=d$`>^dmJj z@8t~kx*2-$D4|D}Kx22@4z6+{Kfsm*F9z{RXo!Yrkex#6Pz(Ueq^DfBqhOAeAC186 zRDbNa5}>^{=9ADR%BYr3)|nXBZ*2?rI$fNum1>sM2edfSMo5l})_y6Qi@|>k@yCf9 zp1su)o)H%dktvX>Y(9;&%Dq6_s13f0$MhFGFVA;=9CTgRT;IZ1? zE;t;cw$z$tgM2EYG6{Mg=s8u5%gF(XO)R8=2%K)M zgKNinm9WCvckCmVuZ(C7r=z{Y9na|)X;fb9bSCV!lxcov>#Wd)IIG~0;e-~%O1}v- zDsR8xc0#F3f6DYxWMDckcm1TFoLX3nuJIaOPba~GDclN7_XT1CB{p*_Xr8BzGc|$l zR@B}|Za~~;R}KKnj-t%jg9%j4B@Xvcd4Np@ZnI{~B@jyiH^IKZdwNU~G5pFQ9EsSU zvaaGKFAja&TNL=#PG#Do0XtK**C1FUt(o`y^+$@r`64nZ!kq5*F6fCH0m@k9j%omj zP;D|weJHJ_ukY9XCR2Zgbz)F*XF;J<==DdrWL0j7-pLU=D>O%adXHU^kv{+Q8p3*8 zjl%ZE8~WEZMH@B_S@01qIVF=m{!x;5hUo8B*l#>!Dq|oE7=E5J*L|=;FUQIb9OSR# zFwt6PMx5+P%p=pEby*n4t_oFudQTbX3xLZp2hEjZi$`uQC8u7U0RyA`l$5%fDEBps zg0m;AP~+z9$5@0g%R5+mMuAv-a6d(`L)l@wDpMf0+PWf)cZxZ1{1m~ha13FGKMn7Z zqVZZ--jUzQfP}`^)=Ao-g{c=v5A-e!Dv8rTGglj;J}Bd^57zI~+Of4VOEfor+EOS% zt&=9RWRa#_T3>T{>x|a*AhQSlWiy4-!~5zkTje%zUR{`Re62W~54R(=Hw0X&gz%G` z2b10j?C*4Ls9q8Wh*0oyi1#B{J^r>pblnAvr^U%V1{+{kpnvBAyjV18*K!%Rz|rkO z#$p`_W{cEVemfGs`K5z?1>o`3ruCfQ<_s4|Lu)3S*;Po`)Z+S?!`1`8NCG)w#&p$w zo7P{a4dlCb6w}>^xepF54X-6WoR6&i=s{2JaIq^Nno=9I&yn=0_nnwTF+#-Y^LddD zyaxarcbNrZ#PL18jSni|J!UFrADZl9p+dGsiWPDF4T1#BEQIbG#!JAq-aOswZN>*5 zu`DMY;%>WWJF|W*M8I&D84A6|kU8LVp}o^bXpcdX!Sf*fyLsahbwFlcG-IsH=alwy zA@R)N);iX`lCk|(dM?LCWpL(qO10n;%eN<#tA^BdKvepG;)|e?`&+8aSF$~2s^whX zNNHMj6_Bb0ujz*Z!$XT-H<;*bd9{ayY9}@!tGMy-YClj5!Bqd2aeI(*8|hTtIppZI zF?C!^&!B9{ySb@_3Sjw*40^KqONv}c)V)>yq9H{wjhYA_IO~H|vXO^VC@46lvUSz5 z#Hxem+QKTifB!QWaDnP}`r8WFl*Q&a?s%t$jcnhli(wfjEm9LBovmoMFJkiDL)3rj zA80v_;n;XwKGGw%zyH(D)V=Oca~&A@B^}oJgWcexWv%i7=83^dA}ROX)warYc!hT& zmxrWepJa$SyBAo^o8ka^_$a9ARh5We#>>JI)0nU)SFq#Uo7Q=XdFA3KWz2MwlSF4Z zFt-oX66$I%dtR;7%n6#bRzu@;p64FC@RwSeo1T@5_*p?$wce|#HsP6}UyYTJvhXlK z`Zuk-PC<{TC+B>urlg=N>o9E%8|xi)#_n4D=g6%6vJ3{v3nI}ib z&M|-sM3qU3cLC-pB*xL9XKr~XItwB z&*qJ4$tf{`QzyZ41pr291*?)M{uAamrd`HhvRGwYTC;R#Ay z+jT$;zY6AWVV|pJ;-H_dQg>2S%3EK4FmxxKc7AI-?3md2yq*U`&kOH#~CQhT37caxfrnNXWmy&9Xk&csZmbcE9t5D81v- zjjO4g@S|POU)QW>I4`f&0yCQO5gerj6PofX`;QpI$deh}h9UD#3%i27;cIP*IN?l+ zY4Cv@M!^ae((OeCX+D|~mN3?(j2|zwXx1BNAYG2kCiKvo8v*fWoD*bv$L_r)Jk_y< zvN0Gyh&&=X&@KLHpF|XV=xE!|l!MKM*^QZ6l$K9&M)oEZ^iY&7YS(;;g83g%cRbNM z%-kJLc9mpfW)SIn9@OA}j}~b#7(tBah?_c=% zmg8}-7bKXH@(F2~*4rH}&+97G1LfOlJXTFnqiyeh>6pPzu1`wA7O|RPMEdMKn3bzQ z{ek!4a=XQNHq)(hp!zRMU7L;n3-R^~!6Wg7iuua5 zneDsT{rPsqW^3H?h1!($`QgX7cz#Uek8%B&$SXW*TmOspE7%`|@jU_mt9`yJ^8Xgt zH!c5JmwXvM-y2ciwb;J{?3>7c0NB^j^Y)9je}8uUJHU9pYWcUP{WoA=wfu{h!(VXy gpe?=vn`2IJc$Lz|obJ|BKYli-CCJ(wYj!^FKS*9{i2wiq 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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πxwN z`9X%I14#&^$;g@(=1=3=Ld3Y$o+QZG7*3OWF;i{NV-*TNIpPX)Jx}`<_FI;34gBnO zzzu1dnhPq*WmWYC4SA@P!gZAYv8IHVRs{A|qf?-G$m#&pu zb2yMV7(SK&81uizhyB?yI&_Dv+NR`~xNvl?c8bOR0F|%el*4L;6U(p}Mt{7?M_e~! zuI?}5+DU?6T8Ye{X1Vqd5=!$O{?5ltk)^}cpKOD1Cg==j^vZ-rGS5WeQil9Vo=et? z)|c-=2Tu;Z?PUNqb*aJCJ3b>M8@5-VUhbS4i9*>G{-I!gU!e`Sx7`hr-BdkMgBp3C zyUgVo*~aHP2^>EAA}6Rmb!e90Z<}fe(Enc5A4_XEN=WlVuN&;nP6ijnG7+KeHo8UH z7*d4b8$aHkz-}6tDiq*4rsI?oVNZ5!$P?BcOKv;~h?s|}nMscw_CC`7H*xf>zZD;k zbtxSR3P=diAOSNiyEB3Dx!LKBz6Z(SrL|GK1MzXN^Pm<}+otXXTil9VXYB05qQ8;- ze1E0b|8YDs3CZ9PD(P3isK;n9@!Xp->08a;`sxtGaPK@JAg8bJQ3fpe$Iy$MB-#aa zuU{9>3m%-c2kg9%2)vkraxeFK;|nZ6UW9a!1Vo5wy8>B-o?c7|Pb{s$EHSqTmyB4& zy(5XeYA4cB!=`P4`fY*6?K!BZG%=SOKDgKMd&0!x%1(ZV=1^n}!SFw?TA*{{dxuM# z%HKK}_kRliaj0qOpya!d#L?y#Gy|{Kg4`K8PnUi8BVJm^QP8$Mm+td}jb~gbHsRf^ zNaqYV1H8=Qg#t;vKEY|j2yj9DjQ*NLlYmLDH%C!d8ITut2q35!;}CA~O=$#ML@u20 zIREd5AC+P)n$Z9?WtMq+U2P CO~uTGjKAP|D!Pe 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̜r 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 z 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¬hn{p-L26}cdoLgM+_m?S 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