diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..d8cf7d463e --- /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 042bbace85..a0d913f213 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 0000000000..f04ea5e7df --- /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 0000000000..c2d8da0873 --- /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 0000000000..2629c72d01 --- /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 0000000000..30bd482a67 --- /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 0000000000..500de11493 --- /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 0000000000..8a73cb8a9e --- /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 0000000000..cb5beae442 --- /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 0000000000..d1fcbf6033 --- /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 0000000000..abe7190b54 --- /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 0000000000..e69de29bb2 diff --git a/base-codezero/include/codezero/dummies/string.h b/base-codezero/include/codezero/dummies/string.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/base-codezero/include/codezero/syscalls.h b/base-codezero/include/codezero/syscalls.h new file mode 100644 index 0000000000..e289825df9 --- /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 0000000000..66f13e018e --- /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 0000000000..8779d0b68e --- /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 0000000000..4ba8249571 --- /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 0000000000..93cb2386ff --- /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 0000000000..b77ed0d06e --- /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 0000000000..9e662a4191 --- /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 0000000000..521519fe55 --- /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 0000000000..be1457625c --- /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 0000000000..24f25bcf87 --- /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 0000000000..7b10977e15 --- /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 0000000000..a68192448a --- /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 0000000000..a0741ed3b8 --- /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 0000000000..3ad6ea6164 --- /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 0000000000..adca798ffe --- /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 0000000000..7fd40ca725 --- /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 0000000000..5145a5743f --- /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 0000000000..4dc4111065 --- /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 0000000000..dd34a10373 --- /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 0000000000..9404db70f2 --- /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 0000000000..a5a8be439c --- /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 0000000000..90931cc19c --- /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 0000000000..cbab044648 --- /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 0000000000..dc08185845 --- /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 0000000000..bfdaec3ab1 --- /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 0000000000..d0e6f4ff74 --- /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 0000000000..612ddd24fd --- /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 0000000000..330b631657 --- /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 0000000000..48a2429723 --- /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 0000000000..a3e8c34704 --- /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 0000000000..6bda6e2ce6 --- /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 0000000000..8ebe5b7d42 --- /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 0000000000..fb8dbc6a6b --- /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 0000000000..e0c9d2be3d --- /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 0000000000..2204d85d0a --- /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 0000000000..7458fc3542 --- /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 0000000000..064115d70f --- /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 0000000000..7d51690bce --- /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 0000000000..a9822721e7 --- /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 0000000000..dcb456e012 --- /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 0000000000..eb6a7e42de --- /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 0000000000..1895715e4f --- /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 0000000000..3524d02c24 --- /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 0000000000..6b1f5715e3 --- /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 0000000000..c90a87b7e8 --- /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 0000000000..4975c1a3c8 --- /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 0000000000..b7c2551a9b --- /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 0000000000..2dbd758a3e --- /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 0000000000..bead355a10 --- /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 0000000000..6e57322f17 --- /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 0000000000..cae0eed706 --- /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 0000000000..3557657a29 --- /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 0000000000..b1aacf1a0f --- /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 0000000000..b642a276c2 --- /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 0000000000..a96dbcbef6 --- /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 0000000000..80a04f02f9 --- /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 0000000000..064ccf6c0c --- /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 0000000000..eca46b04d4 --- /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 0000000000..633ec31561 --- /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 0000000000..8129cac57a --- /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 0000000000..19d0f40131 --- /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 0000000000..826fc5bc4e --- /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 0000000000..cfd604a2a3 --- /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 0000000000..589873dca7 --- /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 0000000000..91f1b03352 --- /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 0000000000..a1cada69b3 --- /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 0000000000..60e6e0ea8b --- /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 0000000000..7ca90e6fd6 --- /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 0000000000..1cb3909b88 --- /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 0000000000..aef9569ff4 --- /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 0000000000..7e0485667c --- /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 0000000000..411c44c021 --- /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 0000000000..9dda379a2a --- /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 0000000000..663cf64b90 --- /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 0000000000..6e6443bb97 --- /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 0000000000..8bda913f9a --- /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 0000000000..75eddeef14 --- /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 0000000000..c22e66d224 --- /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 0000000000..cf5ab378e7 --- /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 0000000000..e53cdd8d18 --- /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 0000000000..a3b2d14bde --- /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 0000000000..d01a257949 --- /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 0000000000..3b9253af32 --- /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 0000000000..8c34daad25 --- /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 0000000000..41b1d98f35 --- /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 0000000000..61e45889db --- /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 0000000000..a6bb3dba08 --- /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 0000000000..0cba20187e --- /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 0000000000..d5fe1ad29b --- /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 0000000000..e5e50de5af --- /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 0000000000..e23fcb4430 --- /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 0000000000..4216d85a7c --- /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 0000000000..773d7d8551 --- /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 0000000000..91fd42bd75 --- /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 0000000000..e2a41b44f2 --- /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 0000000000..81c895a682 --- /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 0000000000..1be0ccfdbb --- /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 0000000000..f4fd7c1481 --- /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 0000000000..7052fb68a7 --- /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 0000000000..55c486041a --- /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 0000000000..41d9215d1f --- /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 0000000000..cda2f56411 --- /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 0000000000..14ea9b13ba --- /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 0000000000..89e726f53c --- /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 0000000000..f9782720c9 --- /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 0000000000..329ad3d22f --- /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 0000000000..fbd632197b --- /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 0000000000..9fe8d2c550 --- /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 0000000000..c2aa7f6036 --- /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 0000000000..16505825c5 --- /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 0000000000..1041e5ecc1 --- /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 0000000000..4b79db17b3 --- /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 0000000000..719b8306ee --- /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 0000000000..b736792ff6 --- /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 0000000000..8bf4175e6e --- /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 0000000000..6cfa9d2321 --- /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 0000000000..adf9758416 --- /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 0000000000..8a9babe9e5 --- /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 0000000000..14b23c62aa --- /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 0000000000..b9c6271d9f --- /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 0000000000..24bdd778cb --- /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 0000000000..53d39fd575 --- /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 0000000000..7568ff534a --- /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 0000000000..73704f9b2f --- /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 0000000000..630a9ab8bc --- /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 0000000000..36f9c530ed --- /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 0000000000..76a2390fcf --- /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 0000000000..3771f3cef2 --- /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 0000000000..46c90d33e8 --- /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 0000000000..81779dc151 --- /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 0000000000..c87c795750 --- /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 0000000000..a39ea21575 --- /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 0000000000..938eb00c5f --- /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 0000000000..4a2d00055e --- /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 0000000000..a5084d85d0 --- /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 0000000000..fcfe1d33d2 --- /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 0000000000..b88ffd5c70 --- /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 0000000000..92bb431496 --- /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 0000000000..118db4ad71 --- /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 0000000000..fd7b66b7b8 --- /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 0000000000..fcc3390a2b --- /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 0000000000..f7d2bc145e --- /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 0000000000..56ce644f2b --- /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 0000000000..b27b4da75c --- /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 0000000000..540e6431cd --- /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 0000000000..96d1cd776a --- /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 0000000000..33f3e6cfd2 --- /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 0000000000..d0bb4139fd --- /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 0000000000..14b52c669d --- /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 0000000000..ff94d707d9 --- /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 0000000000..33f7d67d9e --- /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 0000000000..663cf64b90 --- /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 0000000000..fc71769709 --- /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 0000000000..9a9abfc516 --- /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 0000000000..8058093868 --- /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 0000000000..83ab108db4 --- /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 0000000000..eb4f42e1ec --- /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 0000000000..c2309db088 --- /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 0000000000..067f346946 --- /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 0000000000..067f346946 --- /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 0000000000..3487331b90 --- /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 0000000000..de155386ef --- /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 0000000000..74ce01970c --- /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 0000000000..80154535d5 --- /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 0000000000..010cede4f6 --- /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 0000000000..fedc7acf3e --- /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 0000000000..8c2ef8fe07 --- /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 0000000000..9586dd77be --- /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 0000000000..5a12904c71 --- /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 0000000000..cc3c005055 --- /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 0000000000..07e4d55da1 --- /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 0000000000..4a383fec8e --- /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 0000000000..312e8aa019 --- /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 0000000000..eec6e570e7 --- /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 0000000000..031fa605c2 --- /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 0000000000..341693d41b --- /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 0000000000..fe126d3447 --- /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 0000000000..543e94196a --- /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 0000000000..a32d341c73 --- /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 0000000000..2b866bda87 --- /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 0000000000..5bc8f16f9e --- /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 0000000000..d922d48b72 --- /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 0000000000..02c3af74db --- /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 0000000000..f8bc4dbb7b --- /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 0000000000..e5e50de5af --- /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 0000000000..c4ccbae9a4 --- /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 0000000000..71dc997f03 --- /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 0000000000..248adfc71b --- /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 0000000000..731e29da23 --- /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 0000000000..65e4b69a84 --- /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 0000000000..2473e656f4 --- /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 0000000000..cfed3982b2 --- /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 0000000000..6e03797547 --- /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 0000000000..06bbd166d2 --- /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 0000000000..fc4f7a12cb --- /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 0000000000..020bd4c6a0 --- /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 0000000000..a9ab1a7d40 --- /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 0000000000..a664e3b37d --- /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 0000000000..d5c578cdea --- /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 0000000000..de8e348be0 --- /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 0000000000..bb5f8dc087 --- /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 0000000000..5959c5b940 --- /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 0000000000..89dee1de3f --- /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 0000000000..2991411a78 --- /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 0000000000..466aa13107 --- /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 0000000000..995cf9d371 --- /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 0000000000..7ed3cfff4c --- /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 0000000000..728d6eab94 --- /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 0000000000..73cee97fde --- /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 0000000000..2f0490b1fd --- /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 0000000000..7052fb68a7 --- /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 0000000000..5ae2713b84 --- /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 0000000000..45a01a1791 --- /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 0000000000..3a83660123 --- /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 0000000000..f75670556e --- /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 0000000000..a75e3942f8 --- /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 0000000000..fa08844a3d --- /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 0000000000..c202db06af --- /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 0000000000..12ca769c9a --- /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 0000000000..2288447b62 --- /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 0000000000..9fe8d2c550 --- /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 0000000000..2406690e63 --- /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 0000000000..e1f5fc8e07 --- /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 0000000000..27530aff63 --- /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 0000000000..ec4e7632a2 --- /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 0000000000..da360874af --- /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 0000000000..a5829af402 --- /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 0000000000..4a5ca470bf --- /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 0000000000..7efed81808 --- /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 0000000000..62dd563b32 --- /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 0000000000..b4b1ffa60d --- /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 0000000000..7324e497e3 --- /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 0000000000..f01766c42d --- /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 0000000000..05d69d2054 --- /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 0000000000..570449a9d8 --- /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 0000000000..81726b75ad --- /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 0000000000..3e7487c664 --- /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 0000000000..834b0f8f4b --- /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 0000000000..b051c01e27 --- /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 0000000000..ac3bc03c25 --- /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 0000000000..9c8a9760a9 --- /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 0000000000..57ecd778aa --- /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 0000000000..a0fa6a56bf --- /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 0000000000..915a0ccbe0 --- /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 0000000000..524cb1f031 --- /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 0000000000..a79c1d9a1a --- /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 0000000000..c22e66d224 --- /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 0000000000..8f910d8e9a --- /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 0000000000..efc9378a0c --- /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 0000000000..3055926c53 --- /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 0000000000..c3197875aa --- /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 0000000000..34c91e9f3d --- /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 0000000000..0cf455b06e --- /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 0000000000..0c00829f28 --- /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 0000000000..ac46b2640e --- /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 0000000000..0b467602e1 --- /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 0000000000..7c051aba3b --- /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 0000000000..e62719001d --- /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 0000000000..4f50f05f92 --- /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 0000000000..6b1f5715e3 --- /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 0000000000..c90a87b7e8 --- /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 0000000000..4e52269589 --- /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 0000000000..96beed5700 --- /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 0000000000..4d4487073e --- /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 0000000000..6d9dfff821 --- /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 0000000000..ae7dabee85 --- /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 0000000000..4e8e309a39 --- /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 0000000000..94d6093857 --- /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 0000000000..310689bf0c --- /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 0000000000..da171551c6 --- /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 0000000000..a1981ee00b --- /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 0000000000..5322ef84fe --- /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 0000000000..7574aad198 --- /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 0000000000..c0f5548301 --- /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 0000000000..e7f08096c6 --- /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 0000000000..033eb53b9b --- /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 0000000000..bd4b61f00b --- /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 0000000000..d98332fc51 --- /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 0000000000..284bb27d02 --- /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 0000000000..432cadd0f4 --- /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 0000000000..c06312ff3a --- /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 0000000000..4c61c59042 --- /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 0000000000..6e66dc920f --- /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 0000000000..32ba726754 --- /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 0000000000..d07cccbae0 --- /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 0000000000..f8b81bade9 --- /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 0000000000..51b073080e --- /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 0000000000..8597540413 --- /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 0000000000..1e3d7795a2 --- /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 0000000000..fc42a899a4 --- /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 0000000000..db22b1f6d2 --- /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 0000000000..765f02b6d4 --- /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 0000000000..28371b0da0 --- /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 0000000000..248c5902a1 --- /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 0000000000..f2d6144978 --- /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 0000000000..a09531af71 --- /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 0000000000..6bcd64715a --- /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 0000000000..9e14b2fec1 --- /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 0000000000..ab4ca95af8 --- /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 0000000000..7b52ad4d4e --- /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 0000000000..32befb51c4 --- /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 0000000000..2fecced9a4 --- /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 0000000000..d62b76976c --- /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 0000000000..454e2cc92d --- /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 0000000000..4e1c2439da --- /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 0000000000..eb8c5aa3d1 --- /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 0000000000..b6cca57f9c --- /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 0000000000..768c48f8cc --- /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 0000000000..e6dec11523 --- /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 0000000000..3c7a83d353 --- /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 0000000000..f63d1d145a --- /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 0000000000..fa1b05ce31 --- /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 0000000000..61344c6d66 --- /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 0000000000..907b88d984 --- /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 0000000000..bc4866b5ef --- /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 0000000000..1d3379b1db --- /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 0000000000..cd9fa461a2 --- /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 0000000000..9fadda64dd --- /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 0000000000..aa2aa535cd --- /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 0000000000..cead86767d --- /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 0000000000..d5ef72473a --- /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 0000000000..5f809b68b8 --- /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 0000000000..2946a870fb --- /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 0000000000..7f77619031 --- /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 0000000000..dc5815dbae --- /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 0000000000..9a5f39d256 --- /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 0000000000..c2b11d79a1 --- /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 0000000000..14f7c60a51 --- /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 0000000000..8aa049a03f --- /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 0000000000..fa3e2f54a3 --- /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 0000000000..1ef45a1c12 --- /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 0000000000..3432798c04 --- /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 0000000000..34f52fe4f6 --- /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 0000000000..5433178fba --- /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 0000000000..db8bf950c8 --- /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 0000000000..384207b8bb --- /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 0000000000..cc290f6563 --- /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 0000000000..fccc9c05a8 --- /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 0000000000..a51dd556e2 --- /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 0000000000..8edeea9318 --- /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 0000000000..9297c94356 --- /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 0000000000..47b008a887 --- /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 0000000000..a7f7b83ec8 --- /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 0000000000..3125dfea42 --- /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 0000000000..827a457a55 --- /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 0000000000..a79f7e0b3c --- /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 0000000000..7a7242111c --- /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 0000000000..ec66d4fdb8 --- /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 0000000000..730ff1f637 --- /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 0000000000..efdac76433 --- /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 0000000000..9f38e7c2e4 --- /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 0000000000..a3b84ce6d1 --- /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 0000000000..dc0d02af9e --- /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 0000000000..ce17cd31a6 --- /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 0000000000..ebb4c6b42a --- /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 0000000000..76c5a61808 --- /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 0000000000..a13806546d --- /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 0000000000..13c8cb7bd8 --- /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 0000000000..25f04b2432 --- /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 0000000000..1dfaca10a1 --- /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 0000000000..57e531ee00 --- /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 0000000000..5d176c384d --- /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 0000000000..8c5d5d8450 --- /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 0000000000..5d7fbe218d --- /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 0000000000..090e521367 --- /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 0000000000..a35e000977 --- /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 0000000000..b34dacabd5 --- /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 0000000000..24f25bcf87 --- /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 0000000000..8ace07986f --- /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 0000000000..5c703ad10c --- /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 0000000000..00c6c75886 --- /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 0000000000..9aec768c8f --- /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 0000000000..99adcd34f0 --- /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 0000000000..265adc0534 --- /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 0000000000..3693bce60c --- /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 0000000000..e6199c897a --- /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 0000000000..e6199c897a --- /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 0000000000..5d215f2c9f --- /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 0000000000..13ca4c7f44 Binary files /dev/null and b/base-mb/platform/mb_s3a_starter_kit/system.bit differ diff --git a/base-mb/platform/mk/microblaze.mk b/base-mb/platform/mk/microblaze.mk new file mode 100644 index 0000000000..a0b8be9a6e --- /dev/null +++ b/base-mb/platform/mk/microblaze.mk @@ -0,0 +1,19 @@ +# +# \brief Upload an image to the supported Microblaze SoC's +# \author Martin Stein +# \date 2011-05-23 +# + +UPLOAD_XMD = $(WORK_DIR)/upload.xmd + +upload: $(UPLOAD_XMD) + $(VERBOSE) xmd -opt $(UPLOAD_XMD) + +$(UPLOAD_XMD): + $(VERBOSE)echo \ + "connect mb mdm"\ + "\ndow $(IMAGE)"\ + > $(UPLOAD_XMD) + +.INTERMEDIATE: $(UPLOAD_XMD) +.PHONY: upload diff --git a/base-mb/platform/mk/ml507.mk b/base-mb/platform/mk/ml507.mk new file mode 100644 index 0000000000..e26bf6d7b7 --- /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 0000000000..5ea33e93e9 --- /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 0000000000..a607f7bcea --- /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 0000000000..fb595ba1f4 --- /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 0000000000..eaec3f3fea --- /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 0000000000..fef6631f36 --- /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 0000000000..c88c975089 --- /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 0000000000..68453711ac --- /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 0000000000..b95392bf2e --- /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 0000000000..91a4430271 --- /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 0000000000..dfa1bf20cb --- /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 0000000000..52c5e722b8 --- /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 0000000000..dbc9260093 --- /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 0000000000..2f81344246 --- /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 0000000000..93f819e668 --- /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 0000000000..01158ffe2f --- /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 0000000000..0d1bdec473 --- /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 0000000000..dd5cb249a8 --- /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 0000000000..5ddd3af331 --- /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 0000000000..ec68637ebb --- /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 0000000000..7e87d5cc96 --- /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 0000000000..28795f46f3 --- /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 0000000000..2171059906 --- /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 0000000000..52d65bc1bb --- /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 0000000000..0eae044232 --- /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 0000000000..63412d9956 --- /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 0000000000..81b4220197 --- /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 0000000000..ef78306312 --- /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 0000000000..d9c562b8ec --- /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 0000000000..b40290e4b7 --- /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 0000000000..9dd852969b --- /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 0000000000..d2b70e336f --- /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 0000000000..8b9d50ad42 --- /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 0000000000..6157040a3f --- /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 0000000000..7ded5f2de9 --- /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 0000000000..88ef862e43 --- /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 0000000000..b4b817a7b4 --- /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 0000000000..8ec8111093 --- /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 0000000000..ce03b6fbfe --- /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 0000000000..9ba4d22541 --- /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 0000000000..407603b9a9 --- /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 0000000000..88de7e1097 --- /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 0000000000..978fb0edb8 --- /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 0000000000..a5216c9e8d --- /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 0000000000..d79f5f3c84 --- /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 0000000000..bc2b2c1c6c --- /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 0000000000..2f100bcfe5 --- /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 0000000000..1672c56158 --- /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 0000000000..f434dea6fb --- /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 0000000000..fcb702c02d --- /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 0000000000..660f307b72 --- /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 0000000000..490cc00986 --- /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 0000000000..201bb1fb55 --- /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 0000000000..ca94a34928 --- /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 0000000000..1fbcf1d6be --- /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 0000000000..0723710239 --- /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 0000000000..931f81cd46 --- /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 0000000000..942fd23252 --- /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 0000000000..bff850f767 --- /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 0000000000..3d69f9ccd1 --- /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 0000000000..136aa63879 --- /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 0000000000..76a6ac3c58 --- /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 0000000000..696b2a5335 --- /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 0000000000..488afdd47b --- /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 0000000000..359cc70313 --- /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 0000000000..7e10866ac8 --- /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 0000000000..b7aaff2bdf --- /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 0000000000..baf1b04d9b --- /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 0000000000..764a47770e --- /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 0000000000..5954b95f98 --- /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 0000000000..b22d58f543 --- /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 0000000000..a9f32e42b9 --- /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 0000000000..d0fc0df2cb --- /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 0000000000..45a979bdd8 --- /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 0000000000..f2f4d395f9 --- /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 0000000000..ce1e067cdf --- /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 0000000000..2e6ca23bec --- /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 0000000000..5222292c52 --- /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 0000000000..6ebad393c8 --- /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 0000000000..d22297dd00 --- /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 0000000000..b5c8191a19 --- /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 0000000000..bd95d1b7a3 --- /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 0000000000..5da4e3f3bb --- /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 0000000000..6bdac702c0 --- /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 0000000000..9ccb8cb01a --- /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 0000000000..9764b21363 --- /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 0000000000..3b155ea47d --- /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 0000000000..a8d107ad19 --- /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 0000000000..a73f244b04 --- /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 0000000000..9f1dbcca91 --- /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 0000000000..e9994c91c9 --- /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 0000000000..3272bd67ac --- /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 0000000000..663cf64b90 --- /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 0000000000..439e6e2d77 --- /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 0000000000..7116e3dd9c --- /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 0000000000..3b968d2b6b --- /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 0000000000..2c4f26b18a --- /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 0000000000..8f910d8e9a --- /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 0000000000..3bbdca3d3c --- /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 0000000000..de155386ef --- /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 0000000000..bad92f0daf --- /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 0000000000..b599c112ee --- /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 0000000000..55444d8bb0 --- /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 0000000000..e8d8592665 --- /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 0000000000..e6c5d276c6 --- /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 0000000000..fb2dcfa4f3 --- /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 0000000000..b2efe4605e --- /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 0000000000..c0720c3096 --- /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 0000000000..9bce170f52 --- /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 0000000000..51449881f1 --- /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 0000000000..f33c688d49 --- /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 0000000000..ef6ca0677f --- /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 0000000000..30abfa8b70 --- /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 0000000000..e460d98efa --- /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 0000000000..7f3f2039e5 --- /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 0000000000..7f6222877f --- /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 0000000000..27542de52b --- /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 0000000000..53a07d187c --- /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 0000000000..673645dd4b --- /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 0000000000..9841b39651 --- /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 0000000000..23a7fb4e23 --- /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 0000000000..5ddd3af331 --- /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 0000000000..27bfd7c6e0 --- /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 0000000000..ea4874d78c --- /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 0000000000..aa2a2477d4 --- /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 0000000000..6b3722877f --- /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 0000000000..49cda6d7a0 --- /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 0000000000..1ad065bfca --- /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 0000000000..5fbbab9d3b --- /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 0000000000..6c5248dbeb --- /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 0000000000..ba8c42b2fd --- /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 0000000000..7e929d2b7e --- /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 0000000000..bb60e58db9 --- /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 0000000000..ba543ff843 --- /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 0000000000..dd89c088d5 --- /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 0000000000..1e32690632 --- /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 0000000000..d95a63e326 --- /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 0000000000..2b1dda4741 --- /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 0000000000..fc026e7b87 --- /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 0000000000..36a57d0f3b --- /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 0000000000..a7731e75aa --- /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 0000000000..78a90fdf29 --- /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 0000000000..a1981ee00b --- /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 0000000000..5eb89fc51b --- /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 0000000000..a730c18cb5 --- /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 0000000000..936a1798d4 --- /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 0000000000..2747f074a0 --- /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 0000000000..1dd127896b --- /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 0000000000..9094cae310 --- /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 0000000000..79b3447a24 --- /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 0000000000..7a9c33853f --- /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 0000000000..ba35477491 --- /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 0000000000..d1ad6fdc4e --- /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 0000000000..42acd87995 --- /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 0000000000..7c2c50474f --- /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 0000000000..842f5a4cdc --- /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 0000000000..c78a071033 --- /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 0000000000..5fa30f2ff6 --- /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 0000000000..8ac0d647ac --- /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 0000000000..23e4dd0103 --- /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 0000000000..627d98e9f9 --- /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 0000000000..8490de99a8 --- /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 0000000000..29dfe11462 --- /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 0000000000..c567a8e806 --- /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 0000000000..7d4660617f --- /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 0000000000..13cebd1417 --- /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 0000000000..663cf64b90 --- /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 0000000000..6e6443bb97 --- /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 0000000000..21495f2e2a --- /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 0000000000..a79c1d9a1a --- /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 0000000000..c22e66d224 --- /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 0000000000..e33e29caa7 --- /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 0000000000..08888f1bc7 --- /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 0000000000..88f5f35a37 --- /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 0000000000..577b721b49 --- /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 0000000000..78d4d7cea0 --- /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 0000000000..067c7d14bb --- /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 0000000000..e1c1a92c10 --- /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 0000000000..10540b368b --- /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 0000000000..6553e42e94 --- /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 0000000000..e57e3a3b38 --- /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 0000000000..600f8a8c9f --- /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 0000000000..96b3060db4 --- /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 0000000000..3b2e71b66e --- /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 0000000000..fa4b6d3480 --- /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 0000000000..345b20c236 --- /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 0000000000..c540815f77 --- /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 0000000000..34c0ac8ba2 --- /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 0000000000..3b255d7b3b --- /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 0000000000..f544ce9f71 --- /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 0000000000..f62768eaef --- /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 0000000000..6635209b0f --- /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 0000000000..40054e3c3a --- /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 0000000000..8c1ac8deb5 --- /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 0000000000..f32d59ef1b --- /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 0000000000..8b29f5bfdb --- /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 0000000000..ad66b78b42 --- /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 0000000000..9fd4d21931 --- /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 0000000000..47f2cf72a5 --- /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 0000000000..36b0763e6b --- /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 0000000000..165c5d13a2 --- /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 0000000000..8323d73ff6 --- /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 0000000000..8235b8a952 --- /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 0000000000..16da21ac69 --- /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 0000000000..51dc984dbd --- /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 0000000000..fd48715fb5 --- /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 0000000000..0421e00ca3 --- /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 0000000000..e6cb6f494e --- /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 0000000000..3e3091e26f --- /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 0000000000..c9b8e2ff9e --- /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 0000000000..48bcaacfa9 --- /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 0000000000..7836e1a3a5 --- /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 0000000000..0adcacc5e8 --- /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 0000000000..7eee4cb5ca --- /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 0000000000..be396e362a --- /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 0000000000..e2c98403e7 --- /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 0000000000..9bcb0bd215 --- /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 0000000000..51a3f85fb7 --- /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 0000000000..dcb111dd99 --- /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 0000000000..4bf5abc5c2 --- /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 0000000000..7696ddfaf6 --- /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 0000000000..eb597953fc --- /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 0000000000..a5011484f9 --- /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 0000000000..59745d0ee6 --- /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 0000000000..3bee2c359a --- /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 0000000000..ae2b350d5c --- /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 0000000000..0adbcf0bf7 --- /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 0000000000..30e7e367e7 --- /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 0000000000..3d8b06a68f --- /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 0000000000..5fd0451d55 --- /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 0000000000..9b32d53b26 --- /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 0000000000..85775e1b99 --- /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 0000000000..f0564a019e --- /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 0000000000..579f5e2b22 --- /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 0000000000..00d9354404 --- /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 0000000000..9c3e2971ae --- /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 0000000000..5ee72ceaec --- /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 0000000000..70e8525586 --- /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 0000000000..fa124f5e71 --- /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 0000000000..60851581b2 --- /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 0000000000..074c411aaf --- /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 0000000000..2bb20a62e5 --- /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 0000000000..0421e00ca3 --- /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 0000000000..ce8ba8088f --- /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 0000000000..c993b0c143 --- /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 0000000000..cf27568c1f --- /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 0000000000..2287bccb39 --- /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 0000000000..0c9f535a08 --- /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 0000000000..5a184958dd --- /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 0000000000..8d8d81f71a --- /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 0000000000..2f05ae2d4b --- /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 0000000000..a9ea9c7b70 --- /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 0000000000..f426787dab --- /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 0000000000..3180ae5892 --- /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 0000000000..f910dc8ea6 --- /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 0000000000..88af210caa --- /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 0000000000..68937713d9 --- /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 0000000000..3372b5f8ec --- /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 0000000000..5afe4c2f3a --- /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 0000000000..dd345036bb --- /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 0000000000..12e9db1025 --- /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 0000000000..662611d0b8 --- /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 0000000000..663cf64b90 --- /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 0000000000..c1725471ef --- /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 0000000000..6e6443bb97 --- /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 0000000000..53fbf85b9e --- /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 0000000000..564a78ced2 --- /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 0000000000..a79c1d9a1a --- /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 0000000000..1c3f6ed1a3 --- /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 0000000000..efb3cc3f51 --- /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 0000000000..8cf5e4f086 --- /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 0000000000..6e5603ea19 --- /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 0000000000..1a77e27c56 --- /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 0000000000..953f3a8a5a --- /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 0000000000..13fae36619 --- /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 0000000000..0d7862eead --- /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 0000000000..74a852c40e --- /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 0000000000..fd3ea49634 --- /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 0000000000..15064525e3 --- /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 0000000000..e6c8dc0354 --- /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 0000000000..0d51c201f8 --- /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 0000000000..439ae32c20 --- /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 0000000000..190017d3a9 --- /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 0000000000..9476d457bd --- /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 0000000000..4af93ecdb0 --- /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 0000000000..9c8c9fc978 --- /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 0000000000..0e9c44236f --- /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 0000000000..d5bdde2ab2 --- /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 0000000000..f44e7ae276 --- /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 0000000000..a0c24d6116 --- /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 0000000000..039cdf07de --- /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 0000000000..eda4f4640d --- /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 0000000000..ad866c08a1 --- /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 0000000000..9fe8d2c550 --- /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 0000000000..a78197e587 --- /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 0000000000..eed6e1e14a --- /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 0000000000..1041e5ecc1 --- /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 0000000000..1159630401 --- /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 0000000000..719b8306ee --- /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 0000000000..29f5234f51 --- /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 0000000000..b45b96b9d9 --- /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 0000000000..f91d21ff06 --- /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 0000000000..c5f5e82cb1 --- /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 0000000000..662464378e --- /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 0000000000..181b2c161b --- /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 0000000000..19cbbbc7bc --- /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 0000000000..a5d3616aa4 --- /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 0000000000..b17d4a69b5 --- /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 0000000000..26af486ac5 --- /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 0000000000..cf73045805 --- /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 0000000000..50c8a5cbe9 --- /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 0000000000..14df9d886c --- /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 0000000000..cb07d68fe7 --- /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 0000000000..6253a3b745 --- /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 0000000000..c8f360d2ab --- /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 0000000000..fe832ef0c1 --- /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 0000000000..54366e4b24 --- /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 0000000000..6fbe34ca1b --- /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 0000000000..3487ca9ca5 --- /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 0000000000..90a7780c83 --- /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 0000000000..57f9af478d --- /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 0000000000..022d5c125a --- /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 0000000000..4e3136d157 --- /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 0000000000..18937ffc6f --- /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 0000000000..b4b08f53c5 --- /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 0000000000..fb649ba3b7 --- /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 0000000000..9dadca9151 --- /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 0000000000..ce674e20a3 --- /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 0000000000..126e2eea45 --- /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 0000000000..dcb490c0b5 --- /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 0000000000..e889c5f614 --- /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 0000000000..6fb1b6107b --- /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 0000000000..c9deaadca5 --- /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 0000000000..c665aa6c97 --- /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 0000000000..eef2c56270 --- /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 0000000000..be2a5cb3a8 --- /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 0000000000..5083e1dfae --- /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 0000000000..2cd165e7dc --- /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 0000000000..353635e609 --- /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 0000000000..a574fa13bf --- /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 0000000000..f142e03b15 --- /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 0000000000..d3eeec0e84 --- /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 0000000000..737f379b21 --- /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 0000000000..08e3bc9a55 --- /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 0000000000..a06109d8f8 --- /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 0000000000..042e9b0016 --- /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 0000000000..d82368da51 --- /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 0000000000..c19bd943c5 --- /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 0000000000..b709b27d5b --- /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 0000000000..fd8a3657e3 --- /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 0000000000..83aea89ef6 --- /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 0000000000..3a6d006a87 --- /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 0000000000..a08d49b28b --- /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 0000000000..edfe178149 --- /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 0000000000..3db87deb1e --- /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 0000000000..4fcf6cfedd --- /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 0000000000..d0e6f50528 --- /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 0000000000..4878038c35 --- /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 0000000000..eb32c78d5a --- /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 0000000000..6a538b1347 --- /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 0000000000..2f6ca07636 --- /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 0000000000..96ee318412 --- /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 0000000000..b240bb0bdd --- /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 0000000000..9861c7baa4 --- /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 0000000000..98345512a6 --- /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 0000000000..7c69e34bed --- /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 0000000000..688874364b --- /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 0000000000..7a4ec96fd2 --- /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 0000000000..e8a137991b --- /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 0000000000..b05c73e434 --- /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 0000000000..e2afb4f5b2 --- /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 0000000000..283ebf7ff3 --- /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 0000000000..d404b35882 --- /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 0000000000..711ee0bf68 --- /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 0000000000..8dbf4703dc --- /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 0000000000..da1762920a --- /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 0000000000..c3b9781ed3 --- /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 0000000000..91dd7a2201 --- /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 0000000000..1ec7f96537 --- /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 0000000000..a74803ee00 --- /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 0000000000..55dac2388c --- /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 0000000000..3eb7030632 --- /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 0000000000..999f37a02d --- /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 0000000000..7c1c46fff5 --- /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 0000000000..902b68599d --- /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 0000000000..55eaae1df0 --- /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 0000000000..ad1c50c269 --- /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 0000000000..12c092c4df --- /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 0000000000..3b9bddf5f4 --- /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 0000000000..730e5dd427 --- /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 0000000000..eee5ebc6c8 --- /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 0000000000..39ca44e244 --- /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 0000000000..34ab8a1130 --- /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 0000000000..e80604ff78 --- /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 0000000000..d872597f68 --- /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 0000000000..505631e249 --- /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 0000000000..da0677a7b9 --- /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 0000000000..eb9d12fe53 --- /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 0000000000..0cd6beb15d --- /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 0000000000..51a059fd23 --- /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 0000000000..0af51e6b97 --- /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 0000000000..e8fb2db791 --- /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 0000000000..7372c8257b --- /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 0000000000..ea6fb7c82d --- /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 0000000000..fe3d9fa6ec --- /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 0000000000..0179bd7931 --- /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 0000000000..e3965c0f3d --- /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 0000000000..4527a9385c --- /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 0000000000..db2dc35372 --- /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 0000000000..ed169718cf --- /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 0000000000..384b8c78a0 --- /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 0000000000..753766b201 --- /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 0000000000..eba36da283 --- /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 0000000000..5ebf6c77c5 --- /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 0000000000..a57160a7ce --- /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 0000000000..cfbae0cea9 --- /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 0000000000..09da7b6058 --- /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 0000000000..cdb8315791 --- /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 0000000000..a4d4bb905e --- /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 0000000000..a76d58c976 --- /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 0000000000..79000e0904 --- /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 0000000000..251acafafb --- /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 0000000000..b872c71ea2 --- /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 0000000000..1ebdc801fb --- /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 0000000000..8b5b8affc6 --- /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 0000000000..0e343f69f6 --- /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 0000000000..6e3381812a --- /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 0000000000..2d339fc668 --- /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 0000000000..bc0f3037eb --- /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 0000000000..3df277d4b4 --- /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 0000000000..ca1f9d1716 --- /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 0000000000..0d6f828b85 --- /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 0000000000..651e430cef --- /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 0000000000..bfb490471a --- /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 0000000000..4a410956a9 --- /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 0000000000..9cd2ea2424 --- /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 0000000000..e69de29bb2 diff --git a/base/lib/mk/process.mk b/base/lib/mk/process.mk new file mode 100644 index 0000000000..e51576aa4f --- /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 0000000000..744098113b --- /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 0000000000..6e08dee0fc --- /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 0000000000..de155386ef --- /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 0000000000..b778fcbf32 --- /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 0000000000..c49922a2d5 --- /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 0000000000..67dbe553b7 --- /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 0000000000..0dfb937d60 --- /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 0000000000..d4220b8d09 --- /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 0000000000..33f2e1449a --- /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 0000000000..e72e8ac088 --- /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 0000000000..a6184ef9c8 --- /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 0000000000..93a7f0b5c1 --- /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 0000000000..b6b0d5e952 --- /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 0000000000..bbcd34b42d --- /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 0000000000..570e210a23 --- /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 0000000000..605b433d52 --- /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 0000000000..7878d908f1 --- /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 0000000000..d2490942fd --- /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 0000000000..4ab575d268 --- /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 0000000000..9ffb451ca0 --- /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 0000000000..a050bc07be --- /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 0000000000..cbc51d4e99 --- /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 0000000000..87d23c16f6 --- /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 0000000000..6551b7cb47 --- /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 0000000000..09d23092c2 --- /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 0000000000..63b1a66bba --- /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 0000000000..f93ba98b87 --- /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 0000000000..824e173cc7 --- /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 0000000000..476297ea16 --- /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 0000000000..90791a2daf --- /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 0000000000..3dbaa0f09a --- /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 0000000000..e7f1aba529 --- /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 0000000000..d516375658 --- /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 0000000000..61ce8722b4 --- /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 0000000000..2d28ef184a --- /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 0000000000..5733443f7b --- /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 0000000000..c224184089 --- /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 0000000000..eef14e3be7 --- /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 0000000000..936a587700 --- /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 0000000000..e1ce58927d --- /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 0000000000..dc253d7660 --- /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 0000000000..92de4d43a4 --- /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 0000000000..140ca8fd97 --- /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 0000000000..2615c0b05f --- /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 0000000000..c90c7b4865 --- /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 0000000000..ea05010c24 --- /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 0000000000..6f5f29df1d --- /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 0000000000..e233c186c0 --- /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 0000000000..500b5bf317 --- /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 0000000000..3f5c99adbf --- /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 0000000000..4a1957ab87 --- /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 0000000000..9fd26b443c --- /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 0000000000..fa09a23ae8 --- /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 0000000000..5e0b07f2db --- /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 0000000000..2d38da381c --- /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 0000000000..51c6192d59 --- /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 0000000000..cb1e4da1be --- /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 0000000000..8ae56ba318 --- /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 0000000000..5c2f4b5ac9 --- /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 0000000000..15465b30af --- /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 0000000000..512e3b86ed --- /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 0000000000..4617d65062 --- /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 0000000000..5c761be69c --- /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 0000000000..a0ac0169d4 --- /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 0000000000..a9b216c223 --- /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 0000000000..d27d3a7930 --- /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 0000000000..e217fd3349 --- /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 0000000000..c5d7209766 --- /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 0000000000..e584eb9354 --- /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 0000000000..91d69e15b1 --- /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 0000000000..c2dd8a2bd5 --- /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 0000000000..4271d4725f --- /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 0000000000..e2c7e4605d --- /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 0000000000..f0a873563b --- /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 0000000000..0c3c2d8e27 --- /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 0000000000..54525cab98 --- /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 0000000000..0f1e95f58c --- /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 0000000000..2ad82b35cf --- /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 0000000000..7e06cc4b35 --- /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 0000000000..1d626608fa --- /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 0000000000..f9944f5472 --- /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 0000000000..b81d4da747 --- /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 0000000000..6e4abe972a --- /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 0000000000..4db48e1d1a --- /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 0000000000..1d8078b13d --- /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 0000000000..b6a5fa097a --- /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 0000000000..e8a9da1ded --- /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 0000000000..c60e19a873 --- /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 0000000000..5aefc0b034 --- /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 0000000000..d460c36be1 --- /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 0000000000..0f4b00a6c7 --- /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 0000000000..d04b282681 --- /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 0000000000..00e574f6bb --- /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 0000000000..2b59e97981 --- /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 0000000000..499a3b077e --- /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 0000000000..f88664118a --- /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 0000000000..23c8143adf --- /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 0000000000..7aff03a6b1 --- /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 0000000000..024e9c7d95 --- /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 0000000000..881b56a70e --- /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 0000000000..e5b0402cea --- /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 0000000000..ec87a5a9af --- /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 0000000000..13e17885d5 --- /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 0000000000..55c78e7621 --- /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 0000000000..29c6b700ef --- /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 0000000000..4b879e19ba --- /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 0000000000..364eeea6b8 --- /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 0000000000..e986b36140 --- /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 0000000000..cb01f8a5ef --- /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 0000000000..82cd582222 --- /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 0000000000..577a82d153 --- /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 0000000000..c19b325bbb --- /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 0000000000..9d34ca0f30 --- /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 0000000000..394c004bb6 --- /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 0000000000..5ebd845e42 --- /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 0000000000..68f4614f86 --- /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 0000000000..12f4c44188 --- /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 0000000000..7681373bec --- /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 0000000000..c76642aa66 --- /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 0000000000..07e412eb72 --- /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 0000000000..d9225e28d6 --- /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 0000000000..e990e4585e --- /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 0000000000..303215670c --- /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 0000000000..51b3ed9bf3 --- /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 0000000000..c3880929dd --- /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 0000000000..8c786aca3d --- /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 0000000000..30a700d968 --- /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 0000000000..3a331ae42c --- /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 0000000000..a401e03993 --- /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 0000000000..ec496966e2 --- /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 0000000000..f9e0623009 --- /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 0000000000..b6dc36ea11 --- /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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..6c660f7e28 --- /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 0000000000..7cdaa750e9 --- /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 0000000000..cd3b0c1d07 --- /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 0000000000..e69de29bb2 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 0000000000..e0858af382 --- /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 0000000000..e69de29bb2 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 0000000000..ebdd8f3c21 --- /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 0000000000..64a5db17c7 --- /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 0000000000..401ef0f083 --- /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 0000000000..cd3b0c1d07 --- /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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..e69de29bb2 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 0000000000..5d7c9dc97e --- /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 0000000000..71b144352b --- /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 0000000000..6d7213db01 --- /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 0000000000..53f41a0b21 --- /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 0000000000..b1290f4d53 Binary files /dev/null and b/demo/doc/img/genode_logo.png differ diff --git a/demo/doc/img/launchpad.png b/demo/doc/img/launchpad.png new file mode 100644 index 0000000000..ad874d193c Binary files /dev/null and b/demo/doc/img/launchpad.png differ diff --git a/demo/doc/img/liquid_fb.png b/demo/doc/img/liquid_fb.png new file mode 100644 index 0000000000..f7634ef845 Binary files /dev/null and b/demo/doc/img/liquid_fb.png differ diff --git a/demo/doc/img/liquid_fb_small.png b/demo/doc/img/liquid_fb_small.png new file mode 100644 index 0000000000..3b1bc4bda0 Binary files /dev/null and b/demo/doc/img/liquid_fb_small.png differ diff --git a/demo/doc/img/setup.png b/demo/doc/img/setup.png new file mode 100644 index 0000000000..2372223ae0 Binary files /dev/null and b/demo/doc/img/setup.png differ diff --git a/demo/doc/img/x-ray.png b/demo/doc/img/x-ray.png new file mode 100644 index 0000000000..1311abff7f Binary files /dev/null and b/demo/doc/img/x-ray.png differ diff --git a/demo/doc/img/x-ray_small.png b/demo/doc/img/x-ray_small.png new file mode 100644 index 0000000000..71d1ed573b Binary files /dev/null and b/demo/doc/img/x-ray_small.png differ diff --git a/demo/include/launchpad/launchpad.h b/demo/include/launchpad/launchpad.h new file mode 100644 index 0000000000..0982fe6b0f --- /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 0000000000..fd7ea88f0f --- /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 0000000000..389470ec62 --- /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 0000000000..21cecd6e06 --- /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 0000000000..03a9431c8b --- /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 0000000000..022817927c --- /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 0000000000..140a324787 --- /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 0000000000..747096f1ba --- /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 0000000000..68e0b4cb7f --- /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 0000000000..cabca3b0dc --- /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 0000000000..a8fc860ce8 --- /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 0000000000..f29b1f4f45 --- /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 0000000000..eba70123a7 --- /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 0000000000..508a45138f --- /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 0000000000..d6ca504b5a --- /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 0000000000..86e54a9ecb --- /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 0000000000..6b7b4c4aa5 --- /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 0000000000..7edc53abac --- /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 0000000000..bd3f0a5799 --- /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 0000000000..cd54fd6feb --- /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 0000000000..3c1d2ab611 --- /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 0000000000..f981215ffb --- /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 0000000000..1d7e3a8cd4 --- /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 0000000000..8fb6b113c3 --- /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 0000000000..f86898692a --- /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 0000000000..7206139b73 --- /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 0000000000..b5950dcabf --- /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 0000000000..bbdb19f0fe --- /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 0000000000..8d84a97517 --- /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 0000000000..3635f09f64 --- /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 0000000000..a895d7208d --- /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 0000000000..4bfafa4a4f --- /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 0000000000..d50626208a --- /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 0000000000..df8a3b2c2c --- /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 0000000000..6218094bca --- /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 0000000000..ae80c4edb9 --- /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 0000000000..1cd26dbd10 --- /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 0000000000..7d074ea67a --- /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 0000000000..0c7ebd8502 --- /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 0000000000..91d7b86df4 --- /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 0000000000..732c2154e3 --- /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 0000000000..bf06e61130 --- /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 0000000000..42196f192c --- /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 0000000000..5cce0ff601 --- /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 0000000000..d9e491ae6a --- /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 0000000000..0228ada96e --- /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 0000000000..339e5ad333 --- /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 0000000000..223731a8b6 Binary files /dev/null and b/demo/src/app/scout/data/about.rgba differ diff --git a/demo/src/app/scout/data/backward.rgba b/demo/src/app/scout/data/backward.rgba new file mode 100644 index 0000000000..03a602281c Binary files /dev/null and b/demo/src/app/scout/data/backward.rgba differ diff --git a/demo/src/app/scout/data/closed_icon.rgba b/demo/src/app/scout/data/closed_icon.rgba new file mode 100644 index 0000000000..2f25260250 Binary files /dev/null and b/demo/src/app/scout/data/closed_icon.rgba differ diff --git a/demo/src/app/scout/data/cover.rgba b/demo/src/app/scout/data/cover.rgba new file mode 100644 index 0000000000..2300047659 --- /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 0000000000..1f6d42a7af Binary files /dev/null and b/demo/src/app/scout/data/downarrow.rgba differ diff --git a/demo/src/app/scout/data/forward.rgba b/demo/src/app/scout/data/forward.rgba new file mode 100644 index 0000000000..7bd57e6fec Binary files /dev/null and b/demo/src/app/scout/data/forward.rgba differ diff --git a/demo/src/app/scout/data/home.rgba b/demo/src/app/scout/data/home.rgba new file mode 100644 index 0000000000..e527d08fc0 Binary files /dev/null and b/demo/src/app/scout/data/home.rgba differ diff --git a/demo/src/app/scout/data/index.rgba b/demo/src/app/scout/data/index.rgba new file mode 100644 index 0000000000..e5b863c7be Binary files /dev/null and b/demo/src/app/scout/data/index.rgba differ diff --git a/demo/src/app/scout/data/ior.map b/demo/src/app/scout/data/ior.map new file mode 100644 index 0000000000..4a4e0d9169 Binary files /dev/null and b/demo/src/app/scout/data/ior.map differ 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 0000000000..ce9f1268d9 Binary files /dev/null and b/demo/src/app/scout/data/kill_icon.rgba differ diff --git a/demo/src/app/scout/data/loadbar.rgba b/demo/src/app/scout/data/loadbar.rgba new file mode 100644 index 0000000000..b2a0846a32 Binary files /dev/null and b/demo/src/app/scout/data/loadbar.rgba differ diff --git a/demo/src/app/scout/data/mono16.tff b/demo/src/app/scout/data/mono16.tff new file mode 100644 index 0000000000..811e1b622b Binary files /dev/null and b/demo/src/app/scout/data/mono16.tff differ diff --git a/demo/src/app/scout/data/nav_next.rgba b/demo/src/app/scout/data/nav_next.rgba new file mode 100644 index 0000000000..1ea8caf944 Binary files /dev/null and b/demo/src/app/scout/data/nav_next.rgba differ diff --git a/demo/src/app/scout/data/nav_prev.rgba b/demo/src/app/scout/data/nav_prev.rgba new file mode 100644 index 0000000000..ab5c6ed12d Binary files /dev/null and b/demo/src/app/scout/data/nav_prev.rgba differ diff --git a/demo/src/app/scout/data/opened_icon.rgba b/demo/src/app/scout/data/opened_icon.rgba new file mode 100644 index 0000000000..26a0f90ca8 Binary files /dev/null and b/demo/src/app/scout/data/opened_icon.rgba differ diff --git a/demo/src/app/scout/data/pointer.rgba b/demo/src/app/scout/data/pointer.rgba new file mode 100644 index 0000000000..f030a72687 Binary files /dev/null and b/demo/src/app/scout/data/pointer.rgba differ diff --git a/demo/src/app/scout/data/redbar.rgba b/demo/src/app/scout/data/redbar.rgba new file mode 100644 index 0000000000..cf943f4990 Binary files /dev/null and b/demo/src/app/scout/data/redbar.rgba differ diff --git a/demo/src/app/scout/data/sizer.rgba b/demo/src/app/scout/data/sizer.rgba new file mode 100644 index 0000000000..13b78a2ddc Binary files /dev/null and b/demo/src/app/scout/data/sizer.rgba differ diff --git a/demo/src/app/scout/data/slider.rgba b/demo/src/app/scout/data/slider.rgba new file mode 100644 index 0000000000..470c64946d Binary files /dev/null and b/demo/src/app/scout/data/slider.rgba differ diff --git a/demo/src/app/scout/data/test.png b/demo/src/app/scout/data/test.png new file mode 100644 index 0000000000..a00098feaa Binary files /dev/null and b/demo/src/app/scout/data/test.png differ diff --git a/demo/src/app/scout/data/titlebar.rgba b/demo/src/app/scout/data/titlebar.rgba new file mode 100644 index 0000000000..17a86e8ddc Binary files /dev/null and b/demo/src/app/scout/data/titlebar.rgba differ diff --git a/demo/src/app/scout/data/uparrow.rgba b/demo/src/app/scout/data/uparrow.rgba new file mode 100644 index 0000000000..0192d94ab0 Binary files /dev/null and b/demo/src/app/scout/data/uparrow.rgba differ diff --git a/demo/src/app/scout/data/vera16.tff b/demo/src/app/scout/data/vera16.tff new file mode 100644 index 0000000000..12175d8e44 Binary files /dev/null and b/demo/src/app/scout/data/vera16.tff differ diff --git a/demo/src/app/scout/data/vera18.tff b/demo/src/app/scout/data/vera18.tff new file mode 100644 index 0000000000..766385d4e2 Binary files /dev/null and b/demo/src/app/scout/data/vera18.tff differ diff --git a/demo/src/app/scout/data/vera20.tff b/demo/src/app/scout/data/vera20.tff new file mode 100644 index 0000000000..184d8b4cc7 Binary files /dev/null and b/demo/src/app/scout/data/vera20.tff differ diff --git a/demo/src/app/scout/data/vera22.tff b/demo/src/app/scout/data/vera22.tff new file mode 100644 index 0000000000..99bc189f0f Binary files /dev/null and b/demo/src/app/scout/data/vera22.tff differ diff --git a/demo/src/app/scout/data/vera24.tff b/demo/src/app/scout/data/vera24.tff new file mode 100644 index 0000000000..8a332e4174 Binary files /dev/null and b/demo/src/app/scout/data/vera24.tff differ diff --git a/demo/src/app/scout/data/verabi10.tff b/demo/src/app/scout/data/verabi10.tff new file mode 100644 index 0000000000..df938fa118 Binary files /dev/null and b/demo/src/app/scout/data/verabi10.tff differ diff --git a/demo/src/app/scout/data/verai16.tff b/demo/src/app/scout/data/verai16.tff new file mode 100644 index 0000000000..8d8e2f5c31 Binary files /dev/null and b/demo/src/app/scout/data/verai16.tff differ diff --git a/demo/src/app/scout/data/whitebar.rgba b/demo/src/app/scout/data/whitebar.rgba new file mode 100644 index 0000000000..8be87c1e26 Binary files /dev/null and b/demo/src/app/scout/data/whitebar.rgba differ diff --git a/demo/src/app/scout/genode/launcher.cc b/demo/src/app/scout/genode/launcher.cc new file mode 100644 index 0000000000..fa18897739 --- /dev/null +++ b/demo/src/app/scout/genode/launcher.cc @@ -0,0 +1,29 @@ +/* + * \brief Pseudo launcher for the Genode version of Scout + * \author Norman Feske + * \date 2006-08-28 + */ + +/* + * Copyright (C) 2006-2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include "elements.h" + +static Launchpad launchpad(Genode::env()->ram_session()->quota()); + + +/************************ + ** Launcher interface ** + ************************/ + +void Launcher::launch() +{ + launchpad.start_child(prg_name(), quota(), Genode::Dataspace_capability()); +} diff --git a/demo/src/app/scout/genode/platform_genode.cc b/demo/src/app/scout/genode/platform_genode.cc new file mode 100644 index 0000000000..2914ca1c78 --- /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 0000000000..f3ed6ee895 --- /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 0000000000..3955624fd3 --- /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 0000000000..9968a959a6 --- /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 0000000000..d908fd02b7 --- /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 0000000000..e8b99ed6b5 --- /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 0000000000..56f7e5024f --- /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 0000000000..2603ebef24 --- /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 0000000000..83ed6f73a5 --- /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 0000000000..3dc5982567 --- /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 0000000000..81603b5e86 --- /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 0000000000..c2dfce1384 --- /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 0000000000..c39a62cc24 --- /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 0000000000..0b7258f02e --- /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 0000000000..66d5b9abd3 --- /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 0000000000..2f23c9b34a --- /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 0000000000..c5fc980aaa --- /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 0000000000..624961fd75 --- /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 0000000000..b2b44136f8 --- /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 0000000000..6e61febe6c --- /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 0000000000..6512b08ae2 --- /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 0000000000..f088242ff4 --- /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 0000000000..6dc4507dcc --- /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 0000000000..98934774e6 --- /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 0000000000..4f7bb44482 --- /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 0000000000..70d98997bc --- /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 0000000000..3ba5db9ab7 --- /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 0000000000..5a46b4b2df --- /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 0000000000..11afbd4db6 --- /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 0000000000..6ef3097bbc --- /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 0000000000..0b183576e3 --- /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 0000000000..4fa815ca11 --- /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 0000000000..668ffe1ddd --- /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 0000000000..1118e3ca77 --- /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 0000000000..ad6ae0e822 --- /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 0000000000..df7658587e --- /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 0000000000..cfe3e0fded --- /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 0000000000..7a0d13a78f --- /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 0000000000..28706aad6f --- /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 0000000000..ce05cada75 --- /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 0000000000..bd1f59787c --- /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 0000000000..5b68b21e77 --- /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 0000000000..9e499c9bcd --- /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 0000000000..9c4d67cd05 --- /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 0000000000..a9c1dc53d1 --- /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 0000000000..95984f6e58 --- /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 0000000000..0372fe656c --- /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 0000000000..4a9c52b939 --- /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 0000000000..736fe2c239 --- /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 0000000000..e69de29bb2 diff --git a/demo/src/lib/libpng/target.mk b/demo/src/lib/libpng/target.mk new file mode 100644 index 0000000000..317818db74 --- /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 0000000000..007ba26277 --- /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 0000000000..df04f0148e --- /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 0000000000..f658a9ef55 --- /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 0000000000..8053b6117c --- /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 0000000000..29ce1f64a5 --- /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 0000000000..05a5ab3a2c --- /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 0000000000..7e90f4928f --- /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 0000000000..455dbc9ee8 --- /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 0000000000..bbee92ed1e --- /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 0000000000..1e88d2d97b --- /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 0000000000..75ed4b5978 --- /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 0000000000..792fdee8e9 --- /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 0000000000..07bd3e78a7 --- /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 0000000000..8a9c13ff03 --- /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 0000000000..b1104c87e7 --- /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 0000000000..395e4e1681 --- /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 0000000000..72facf900f --- /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 0000000000..b59e3d0def --- /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 0000000000..d55f5948a3 --- /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 0000000000..b7d5eff81b --- /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 0000000000..ef2ffd5796 --- /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 0000000000..ea586c2699 --- /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 0000000000..d4c88c123c --- /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 0000000000..c5a2316d6c --- /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 0000000000..8a4bde7c46 --- /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 0000000000..6564942ceb --- /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 0000000000..9f621a1794 --- /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 0000000000..07d287595a --- /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 0000000000..7a3cee6d8a --- /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 0000000000..72c644cc1c --- /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 0000000000..3374964589 --- /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 0000000000..3f5beccca2 --- /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 0000000000..f9537ec74e --- /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 0000000000..43b6202871 --- /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 0000000000..b9abb8088f --- /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 0000000000..8705e66aec --- /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 0000000000..3edf54934e --- /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 0000000000..388b90dab5 --- /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 0000000000..e31904642c --- /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 0000000000..2b4ccf3f59 --- /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 0000000000..0a58f395b2 Binary files /dev/null and b/demo/src/server/nitlog/mono.tff differ diff --git a/demo/src/server/nitlog/target.mk b/demo/src/server/nitlog/target.mk new file mode 100644 index 0000000000..17eed8e945 --- /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 0000000000..4d633d1ba4 --- /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 0000000000..5ef6e8e3e3 --- /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 0000000000..647f77bec7 --- /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 0000000000..03307e5e52 --- /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 0000000000..3963c74e6c --- /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 0000000000..86c74cdeed --- /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 0000000000..ff367df7bc --- /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 0000000000..0e78089b83 --- /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 0000000000..508a5a69e0 --- /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 0000000000..6fb0928785 --- /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 0000000000..6106669416 --- /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 0000000000..a41f5db27e --- /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 0000000000..5d78eb5cb6 --- /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 0000000000..3be144657c --- /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 0000000000..54aea49540 --- /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 0000000000..97c099a7cb --- /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 0000000000..022186ad51 --- /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 0000000000..1da35addcf --- /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 0000000000..fb010e52f3 --- /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 0000000000..15d3b30952 --- /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 0000000000..37ba9c12fc --- /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 0000000000..f125c354f4 --- /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 0000000000..bfa16e99a2 --- /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 0000000000..2f880888f0 --- /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 0000000000..f2a0e5ebcc --- /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 0000000000..f224cd17ae --- /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 0000000000..ff2bc16e6a --- /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 0000000000..0d6ce3a092 --- /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 0000000000..4e332d370d --- /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 0000000000..81aaf2a989 --- /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 0000000000..113f48e6f6 --- /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 0000000000..401bfdcf12 --- /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 0000000000..067a072631 --- /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 0000000000..ad10bd5306 --- /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 0000000000..c84a6d004f --- /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 0000000000..fe60b844c2 --- /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 0000000000..0920907000 --- /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 0000000000..dcf78949b4 Binary files /dev/null and b/gems/src/server/terminal/mono.tff differ diff --git a/gems/src/server/terminal/target.mk b/gems/src/server/terminal/target.mk new file mode 100644 index 0000000000..17761a610b --- /dev/null +++ b/gems/src/server/terminal/target.mk @@ -0,0 +1,4 @@ +TARGET = terminal +LIBS = cxx env server signal +SRC_CC = main.cc +SRC_BIN = mono.tff diff --git a/gems/src/test/terminal_decoder/main.cc b/gems/src/test/terminal_decoder/main.cc new file mode 100644 index 0000000000..9c27c49dfe --- /dev/null +++ b/gems/src/test/terminal_decoder/main.cc @@ -0,0 +1,275 @@ +/* + * \brief Test for decoding terminal input + * \author Norman Feske + * \date 2011-07-05 + */ + +/* + * Copyright (C) 2011 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* Terminal includes */ +#include + + +class Static_character_screen : public Terminal::Character_screen +{ + private: + + Terminal::Character_array &_char_array; + Terminal::Boundary _boundary; + Terminal::Position _cursor_pos; + + public: + + Static_character_screen(Terminal::Character_array &char_array) + : _char_array(char_array), _boundary(_char_array.boundary()) { } + + void dump() const; + + + /******************************** + ** Character_screen interface ** + ********************************/ + + void output(Terminal::Character c) + { +// Genode::printf("output '%c'\n", c.ascii()); + + if (c.ascii() > 0x10) + _char_array.set(_cursor_pos, c); + _cursor_pos.x++; + if (_cursor_pos.x >= _boundary.width) { + _cursor_pos.x = 0; + _cursor_pos.y++; + } + if (_cursor_pos.y >= _boundary.height) { +// _char_array.newline(); + _cursor_pos.y = _boundary.height - 1; + } + } + + void civis() + { + } + + void cnorm() + { + } + + void cvvis() + { + } + + void cpr() + { + } + + void csr(int, int) + { + } + + void cuf(int) + { + if (_cursor_pos.x >= _boundary.width) + _cursor_pos.x++; +// +// _cursor_pos = _cursor_pos + Terminal::Offset(1, 0); + } + + void cup(int y, int x) + { + using namespace Genode; + x = max(0, min(x, _boundary.width - 1)); + y = max(0, min(y, _boundary.height - 1)); + _cursor_pos = Terminal::Position(x, y); + } + + void cuu1() + { +// if (_cursor_pos.y > 0) +// _cursor_pos.y--; + } + + void dch(int) + { + } + + void dl(int) + { + } + + void ech(int) + { + } + + void ed() + { + } + + void el() + { + } + + void el1() + { + } + + void home() + { + _cursor_pos = Terminal::Position(0, 0); + } + + void hpa(int x) + { + PDBG("hpa %d", x); + } + + void hts() + { + } + + void ich(int) + { + } + + void il(int) + { + } + + void oc() + { + } + + void op() + { + } + + void rc() + { + } + + void ri() + { + } + + void ris() + { + } + + void rmam() + { + } + + void rmir() + { + } + + void setab(int) + { + } + + void setaf(int) + { + } + + void sgr(int) + { + } + + void sc() + { + } + + void smam() + { + } + + void smir() + { + } + + void tbc() + { + } + + void u6(int, int) + { + } + + void u7() + { + } + + void u8() + { + } + + void u9() + { + } + + void vpa(int y) + { + PDBG("vpa %d", y); + } +}; + + +void Static_character_screen::dump() const +{ + using namespace Terminal; + + Genode::printf("--- screen dump follows ---\n"); + + Boundary const boundary = _char_array.boundary(); + for (int y = 0; y < boundary.height; y++) { + + char line[boundary.width + 1]; + + for (int x = 0; x < boundary.width; x++) { + + Character c = _char_array.get(Position(x, y)); + if (c.is_valid()) + line[x] = c.ascii(); + else + line[x] = ' '; + } + + line[boundary.width] = 0; + Genode::printf("%s\n", line); + } + + Genode::printf("--- end of screen dump ---\n"); +} + + +extern "C" char _binary_vim_vt_start; +extern "C" char _binary_vim_vt_end; + + +int main(int argc, char **argv) +{ + using namespace Terminal; + + Static_character_array<81, 26> char_array; + + Static_character_screen screen(char_array); + + Decoder decoder(screen); + for (char *c = &_binary_vim_vt_start; c < &_binary_vim_vt_end; c++) { + decoder.insert(*c); + } + + screen.dump(); + + return 0; +} diff --git a/gems/src/test/terminal_decoder/target.mk b/gems/src/test/terminal_decoder/target.mk new file mode 100644 index 0000000000..0a87601f7f --- /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 0000000000..3bbc6a66db --- /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 0000000000..1e1639cb4d --- /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 0000000000..80a9c9a61a --- /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 0000000000..151ec7e2c5 --- /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 0000000000..521a5ebf49 --- /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 0000000000..357d60f30b --- /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 0000000000..d861a7a1d8 --- /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 0000000000..b3db48a166 --- /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 0000000000..8045cad67d --- /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 0000000000..173d307fbe --- /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 0000000000..9ccead7497 --- /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 0000000000..a005e0afae --- /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 0000000000..ccfe6accd6 --- /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 0000000000..92ab1bdb9d --- /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 0000000000..fdb991f6e4 --- /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 0000000000..0388d624c2 --- /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 0000000000..3c0b8b1641 --- /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 0000000000..76d271a74b --- /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 0000000000..da551a271e --- /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 0000000000..11e7013995 --- /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 0000000000..a564fa4792 --- /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 0000000000..09ddf43c66 --- /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 0000000000..558aeb64ba --- /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 0000000000..fa4e2f78e6 --- /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 0000000000..73ddaf6d0c --- /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 0000000000..af9a40eacf --- /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 0000000000..97ca0268b4 --- /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 0000000000..f0527dfff0 --- /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 0000000000..03445b17d2 --- /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 0000000000..e69de29bb2 diff --git a/libports/include/libc-plugin/fd_alloc.h b/libports/include/libc-plugin/fd_alloc.h new file mode 100644 index 0000000000..fcf906a637 --- /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 0000000000..bcf45d5012 --- /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 0000000000..4e43f68f39 --- /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 0000000000..4e6b1199a7 --- /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 0000000000..869b7422c6 --- /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 0000000000..d82e4849be --- /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 0000000000..637498ca02 --- /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 0000000000..afd8a52aa3 --- /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 0000000000..92f69a43b2 --- /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 0000000000..b84e708161 --- /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 0000000000..32608bedec --- /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 0000000000..fc7a142d72 --- /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 0000000000..4a20f25f0e --- /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 0000000000..6690ea6225 --- /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 0000000000..f6dbe8a038 --- /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 0000000000..93a3ca5be1 --- /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 0000000000..142247e022 --- /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 0000000000..4e78f03e43 --- /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 0000000000..90092d31c0 --- /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 0000000000..398c59adee --- /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 0000000000..e575780f3b --- /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 0000000000..1f74bdc169 --- /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 0000000000..37af131b3f --- /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 0000000000..91e2e2f588 --- /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 0000000000..bfebe9a603 --- /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 0000000000..577e61344b --- /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 0000000000..6f6d066875 --- /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 0000000000..fb7bc71bda --- /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 0000000000..bc5ec5c41e --- /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 0000000000..a56c6fda0f --- /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 0000000000..32b43b8a7a --- /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 0000000000..a2d9728b9e --- /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 0000000000..ed8529bc06 --- /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 0000000000..12d9f6d945 --- /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 0000000000..a6aed5cf20 --- /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 0000000000..6c7ba23621 --- /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 0000000000..9452a6bb3a --- /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 0000000000..8f7e698995 --- /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 0000000000..6db64a0c39 --- /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 0000000000..2c1e4a8b76 --- /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 0000000000..b0a02e8fe3 --- /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 0000000000..42152c1a68 --- /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 0000000000..055abf6c4e --- /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 0000000000..0ca844a8a9 --- /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 0000000000..39869865b4 --- /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 0000000000..338e661bf5 --- /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 0000000000..edf8af319c --- /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 0000000000..9ca30d75d3 --- /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 0000000000..0e43ff82e6 --- /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 0000000000..781b0128b4 --- /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 0000000000..38ce537dbb --- /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 0000000000..3243c33ee7 --- /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 0000000000..4372fbdba8 --- /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 0000000000..8e29221dc8 --- /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 0000000000..ad229671e3 --- /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 0000000000..4ed5238e40 --- /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 0000000000..0df2b07293 --- /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 0000000000..d6e9e20697 --- /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 0000000000..4d6cab0acc --- /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 0000000000..926db97faf --- /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 0000000000..237ef81ab0 --- /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 0000000000..bf7137f7c4 --- /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 0000000000..cad66a5258 --- /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 0000000000..72be3b718e --- /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 0000000000..06dffa1dbe --- /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 0000000000..7f4869e5a7 --- /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 0000000000..5173920ad9 --- /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 0000000000..1f987ff673 --- /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 0000000000..5538346770 --- /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 0000000000..6caf40e577 --- /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 0000000000..4cc365c851 --- /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 0000000000..3702e2057a --- /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 0000000000..580ed1bf75 --- /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 0000000000..9ede73aa35 --- /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 0000000000..f52b69100a --- /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 0000000000..ae16b549fb --- /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 0000000000..e4b763ed6e --- /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 0000000000..a7c08a6a8b --- /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 0000000000..31293d8f54 --- /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 0000000000..123b6f3986 --- /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 0000000000..079fae8538 --- /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 0000000000..093cca1c70 --- /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 0000000000..fbbab81907 --- /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 0000000000..f92044949f --- /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 0000000000..52560adbe2 --- /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 0000000000..65681cbada --- /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 0000000000..f0e306162a --- /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 0000000000..b104858e3a --- /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 0000000000..e5f0d2faad --- /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 0000000000..0cea4d1be3 --- /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 0000000000..02c98d4658 --- /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 0000000000..7d391f4097 --- /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 0000000000..b7cdb9f760 --- /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 0000000000..bbf5528b50 --- /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 0000000000..8ae569564e --- /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 0000000000..03f8eef627 --- /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 0000000000..8476ba5f6d --- /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 0000000000..5cfe04433a --- /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 0000000000..1c05ec7883 --- /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 0000000000..8ff85438ec --- /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 0000000000..53c19f1a62 --- /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 { + + + + + + + + + + + + + + + + + +