diff --git a/base-hw/doc/hw.txt b/base-hw/doc/hw.txt new file mode 100644 index 0000000000..c13aa7f218 --- /dev/null +++ b/base-hw/doc/hw.txt @@ -0,0 +1,67 @@ + + ====================================== + How to use Genode directly on hardware + ====================================== + + Martin Stein + + +The 'base-hw' repository provides an implementation of Genodes core that runs +directly on hardware, without an intermediate third-party kernel. Currently it +runs on the ARM platforms Realview PBXA9, Versatile Express A9X4 and +PandaBoard A2. + +This document provides brief instructions about building and booting Genode +directly on hardware. + + +Prerequisites +############# + +To build Genode 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 + +If you want to examine the examples that are given in this document, you will +need Qemu for ARM emulations to run them on your machine: + +! apt-get install qemu-kvm-extras + +Building Genode to run directly on hardware +########################################### + +The current version of the Genode source code is available at this page: + +:http://genode.org/download/repository: + Donwloading the Genode source code + +Now, go to a directory where you want the Genode 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 system to run +Genode on. Choose 'hw_pbxa9', 'hw_vea9x4', or 'hw_panda_a2' depending on the +hardware system you aim at. + +! /tool/create_builddir hw_pbxa9 BUILD_DIR= + +Now, go to the newly created build directory make a test: + +! cd +! make run/nested_init + +This will build the Genode components that are needed to run a simple test +with 3 nested init programs, and than execute it via Qemu. + +For further informations according to the specific hardware systems, have +look into the other documentations: + +! base-hw/doc/.txt + diff --git a/base-hw/doc/panda_a2.txt b/base-hw/doc/panda_a2.txt new file mode 100644 index 0000000000..fc5b014445 --- /dev/null +++ b/base-hw/doc/panda_a2.txt @@ -0,0 +1,95 @@ + + ====================================================== + Getting started with 'genode/base-hw' on PandaBoard A2 + ====================================================== + + + Martin Stein + +Abstract +######## + +This is a short tutorial that depicts a handy way to get a Genode ELF image, +build with 'base-hw', started on the PandaBoard A2. It is dedicated to common +Linux systems, but all examples originate from a Ubuntu 10.10. + + +Tutorial +######## + +Connect the PandaBoard to your local Ethernet through its RJ45 connector. +Additionally connect the PandaBoard to your machine through its COM port. +Ensure that you have installed the genode tool chain that is available at: + + [http://genode.org/download/tool-chain - Get the genode tool chain] + +Ensure that '/bin/' is in your 'PATH' variable. +Get the linaro U-Boot repository and compile U-Boot for PandaBoard: + +! git clone git://git.linaro.org/boot/u-boot-linaro-stable.git +! cd +! make CROSS_COMPILE=genode-arm- omap4_panda_config +! make CROSS_COMPILE=genode-arm- + +During the compilation i had some errors. The first was in assembly code, +it seemed to originate from a slip with the typo and was easy to fix. +The second kind of errors occured because the GCC version had no support for +direct array initialization, thus i avoided them by simply initialize +the array elements separately. + +Now install the following packages to communicate with the PandaBoard: + +! sudo apt-get install tftp-hpa minicom + +Open '/etc/default/tftpd-hpa' with a text editor and ensure that it has +the following content: + +! # /etc/default/tftpd-hpa +! TFTP_USERNAME="tftp" +! TFTP_DIRECTORY="/var/lib/tftpboot" +! TFTP_ADDRESS="0.0.0.0:69" +! TFTP_OPTIONS="-l" + +Tell U-Boot wich image to load on boot command: + +! cd /var/lib/tftpboot/ +! ln -s image.elf + +Start TFTP to enable the upload of the image: + +! sudo service tftp-hpa restart + +Start Minicom in configuration mode: + +! minicom -s + +Go to 'Serial port setting' and ensure that the device is set the +TTY of the COM port you've conntected PandaBoard with, in my case it was +'/dev/ttyS0'. Configure the other settings for a baud rate of '115200', +8 bit char length, no parity and 1 stop bit. Quit Minicom and start +it once more: + +! minicom + +Mount your SD-card and copy the U-Boot files to its boot partition: + +! cd ; cp MLO /media/boot/; cp u-boot.bin /media/boot/ + +Unmount the SD card and insert it into the appropriate PandaBoard slot. +Plug in the power connector or push the 'S1' button if the PandaBoard is +already powered. + +Minicom should now show the following message: + +! Hit any key to stop autoboot: + +We have to stop autoboot and type in this line to load and boot the genode +image via ethernet: + +! usb start; dhcp; bootelf 0x82000000 + +Now the ELF image should start correctly and offer some debug output in +minicom. You can now boot further images by redirecting the link +'/var/lib/tftpboot/image.elf' accordingly, restarting your pandaboard +and instructing 'uboot' again as described above. + diff --git a/base-hw/include/arm_v7a/base/syscall.h b/base-hw/include/arm_v7a/base/syscall.h new file mode 100644 index 0000000000..562c17ced9 --- /dev/null +++ b/base-hw/include/arm_v7a/base/syscall.h @@ -0,0 +1,62 @@ +/* + * \brief Syscall declarations specific for ARM V7A systems + * \author Martin Stein + * \date 2011-11-30 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This 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_V7A__BASE__SYSCALL_H_ +#define _INCLUDE__ARM_V7A__BASE__SYSCALL_H_ + +/* Genode includes */ +#include + +namespace Kernel +{ + typedef Genode::uint32_t Syscall_arg; + typedef Genode::uint32_t Syscall_ret; + + /***************************************************************** + ** Syscall with 1 to 6 arguments ** + ** ** + ** These functions must not be inline to ensure that objects, ** + ** wich are referenced by arguments, are tagged as "used" even ** + ** though only the pointer gets handled in here. ** + *****************************************************************/ + + Syscall_ret syscall(Syscall_arg arg_0); + + Syscall_ret syscall(Syscall_arg arg_0, + Syscall_arg arg_1); + + Syscall_ret syscall(Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2); + + Syscall_ret syscall(Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3); + + Syscall_ret syscall(Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4); + + Syscall_ret syscall(Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4, + Syscall_arg arg_5); +} + +#endif /* _INCLUDE__ARM_V7A__BASE__SYSCALL_H_ */ + diff --git a/base-hw/include/base/ipc_msgbuf.h b/base-hw/include/base/ipc_msgbuf.h new file mode 100644 index 0000000000..175426c08f --- /dev/null +++ b/base-hw/include/base/ipc_msgbuf.h @@ -0,0 +1,71 @@ +/* + * \brief IPC message buffers + * \author Martin Stein + * \date 2012-01-03 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This 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; /* buffer size in bytes */ + + public: + + char buf[]; /* begin of actual message buffer */ + + /************************************************* + ** 'buf' must be the last member of this class ** + *************************************************/ + + /** + * Return size of message buffer + */ + inline size_t size() const { return _size; } + + /** + * Return address of message buffer + */ + inline void *addr() { return &buf[0]; } + }; + + /** + * Instance of IPC message buffer with specified buffer size + * + * 'Msgbuf_base' must be the last class this class inherits from. + */ + template + class Msgbuf : public Msgbuf_base + { + public: + + /************************************************** + ** 'buf' must be the first member of this class ** + **************************************************/ + + char buf[BUF_SIZE]; + + /** + * Constructor + */ + Msgbuf() { _size = BUF_SIZE; } + }; +} + +#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */ + diff --git a/base-hw/include/base/ipc_pager.h b/base-hw/include/base/ipc_pager.h new file mode 100644 index 0000000000..40b8ab92a3 --- /dev/null +++ b/base-hw/include/base/ipc_pager.h @@ -0,0 +1,172 @@ +/* + * \brief IPC backend for a Genode pager + * \author Martin Stein + * \date 2012-03-28 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This 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 + +namespace Genode +{ + class Pager_object; + + /** + * Translation of a virtual page frame + */ + struct Mapping + { + addr_t virt_address; + addr_t phys_address; + bool write_combined; + unsigned size_log2; + bool writable; + + /** + * Construct valid mapping + */ + Mapping(addr_t const va, addr_t const pa, bool const wc, + unsigned const sl2 = MIN_MAPPING_SIZE_LOG2, bool w = 1) + : + virt_address(va), phys_address(pa), write_combined(wc), + size_log2(sl2), writable(w) + { } + + /** + * Construct invalid mapping + */ + Mapping() : size_log2(0) { } + + /** + * Dummy, all data is available since construction + */ + void prepare_map_operation() { } + + /** + * Validation + */ + bool valid() { return size_log2 > 0; } + }; + + /** + * Message format for the acknowledgment of a resolved pagefault + */ + struct Pagefault_resolved + { + Native_thread_id const reply_dst; + Pager_object * const pager_object; + }; + + /** + * Special paging server class + */ + class Ipc_pager : public Native_capability + { + enum { VERBOSE = 1 }; + + Pagefault _pagefault; /* data of lastly received pagefault */ + Mapping _mapping; /* mapping to resolve last pagefault */ + + public: + + /** + * Constructor + */ + Ipc_pager() : + Native_capability(Genode::thread_get_my_native_id(), 0) + { + /* check if we can distinguish all message types */ + if (sizeof(Pagefault) == sizeof(Pagefault_resolved)) + { + kernel_log() << __PRETTY_FUNCTION__ + << ": Message types indiscernible\n"; + while (1) ; + } + } + + /** + * Wait for the next pagefault request + */ + void wait_for_fault(); + + /** + * Resolve current pagefault and wait for a new one + */ + void resolve_and_wait_for_fault(); + + /** + * Request instruction pointer of current page fault + */ + addr_t fault_ip() { return _pagefault.virt_ip; } + + /** + * Request fault address of current page fault + */ + addr_t fault_addr() { return _pagefault.virt_address; } + + /** + * Set parameters for next reply + */ + void set_reply_mapping(Mapping m) { _mapping = m; } + + /** + * Set destination for next reply + */ + void set_reply_dst(Native_capability pager_object) { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + } + + /** + * 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() { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + } + + /** + * Return thread ID of last faulter + */ + Native_thread_id last() const { return _pagefault.thread_id; } + + /** + * Return badge for faulting thread + */ + unsigned long badge() const { return _pagefault.thread_id; } + + /** + * Return true if last fault was a write fault + */ + bool is_write_fault() const { return _pagefault.write; } + + /** + * Return true if last fault was an exception + */ + bool is_exception() const + { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + return false; + } + }; +} + +#endif /* _INCLUDE__BASE__IPC_PAGER_H_ */ + diff --git a/base-hw/include/base/native_types.h b/base-hw/include/base/native_types.h new file mode 100644 index 0000000000..0a7962704d --- /dev/null +++ b/base-hw/include/base/native_types.h @@ -0,0 +1,142 @@ +/* + * \brief Platform specific basic Genode types + * \author Martin Stein + * \date 2012-01-02 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This 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_ + +/* Genode includes */ +#include +#include + +namespace Genode +{ + class Platform_thread; + + typedef int volatile Native_lock; + typedef Platform_thread * Native_thread; + typedef unsigned long Native_thread_id; + typedef int Native_connection_state; + + /* FIXME needs to be MMU dependent */ + enum { MIN_MAPPING_SIZE_LOG2 = 12 }; + + /** + * Get kernel-object identifier of the current thread + */ + inline Native_thread_id thread_get_my_native_id() + { return Kernel::current_thread_id(); } + + /** + * Get the thread ID, wich is handled as invalid by the kernel + */ + inline Native_thread_id thread_invalid_id() { return 0; } + + /** + * Describes a pagefault + */ + struct Pagefault + { + unsigned long thread_id; /* thread ID of the faulter */ + Software_tlb * software_tlb; /* TLB to wich the faulter is assigned */ + addr_t virt_ip; /* the faulters virtual instruction pointer */ + addr_t virt_address; /* virtual fault address */ + bool write; /* write access attempted at fault? */ + + /** + * Placement new operator + */ + void * operator new (size_t, void * p) { return p; } + + /** + * Construct invalid pagefault + */ + Pagefault() : thread_id(0) { } + + /** + * Construct valid pagefault + */ + Pagefault(unsigned const tid, Software_tlb * const sw_tlb, + addr_t const vip, addr_t const va, bool const w) + : + thread_id(tid), software_tlb(sw_tlb), virt_ip(vip), + virt_address(va), write(w) + { } + + /** + * Validation + */ + bool valid() const { return thread_id != 0; } + }; + + /** + * Describes a userland-thread-context region + */ + struct Native_utcb + { + /* UTCB payload */ + union { + char bytes[1< * src); + }; + + typedef Native_capability_tpl Native_capability; + + /** + * A coherent address region + */ + struct Native_region + { + addr_t base; + size_t size; + }; +} + +#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */ + diff --git a/base-hw/include/base/signal.h b/base-hw/include/base/signal.h new file mode 100644 index 0000000000..567aa597a3 --- /dev/null +++ b/base-hw/include/base/signal.h @@ -0,0 +1,172 @@ +/* + * \brief Delivery and reception of asynchronous notifications on HW-core + * \author Martin Stein + * \date 2012-05-05 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This 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__ + +/* Genode includes */ +#include +#include +#include + +namespace Genode +{ + /** + * A bunch of asynchronuosly triggered events that target the same context + * + * Because a signal can trigger asynchronously at a context, + * the kernel accumulates them and provides them as such a bunch, + * once the receiver indicates that he is ready to receive. + */ + class Signal + { + unsigned long _imprint; /* receiver-local signal-context pointer */ + int _num; /* how often this signal has been triggered */ + + public: + + /** + * Construct valid signal + */ + Signal(unsigned long const imprint, int const num) + : _imprint(imprint), _num(num) { } + + /*************** + ** Accessors ** + ***************/ + + Signal_context * context() { return (Signal_context *)_imprint; } + + int num() { return _num; } + }; + + typedef List Context_list; + + /** + * A specific signal type that a transmitter can target when it submits + * + * One receiver might handle multiple signal contexts, + * but a signal context is owned by exactly one signal receiver. + */ + class Signal_context : public Context_list::Element + { + friend class Signal_receiver; + + Signal_receiver * _receiver; /* receiver that manages us */ + Lock _lock; /* serialize object access */ + Signal_context_capability _cap; /* holds the name of our context + * kernel-object as 'dst' */ + + public: + + /** + * Construct context that is not yet managed by a receiver + */ + Signal_context() : _receiver(0), _lock(Lock::UNLOCKED) { } + + /** + * Destructor + */ + virtual ~Signal_context() { } + + /* solely needed to enable one to type a capability with us */ + GENODE_RPC_INTERFACE(); + }; + + /** + * To submit signals to one specific context + * + * Multiple transmitters can submit to the same context. + */ + class Signal_transmitter + { + /* names the targeted context kernel-object with its 'dst' field */ + Signal_context_capability _context; + + public: + + /** + * Constructor + */ + Signal_transmitter(Signal_context_capability const c = + Signal_context_capability()) + : _context(c) { } + + /** + * Trigger a signal 'num' times at the context we target + */ + void submit(int const num = 1) + { Kernel::submit_signal(_context.dst(), num); } + + /*************** + ** Accessors ** + ***************/ + + void context(Signal_context_capability const c) { _context = c; } + }; + + /** + * Manage multiple signal contexts and receive signals targeted to them + */ + class Signal_receiver + { + Context_list _contexts; /* contexts that we manage */ + Lock _contexts_lock; /* serialize access to + * 'contexts' */ + Signal_receiver_capability _cap; /* holds name of our receiver + * kernel-object as 'dst' */ + + /** + * Let a context 'c' no longer be managed by us + * + * Doesn't serialize any member access. + */ + void _unsync_dissolve(Signal_context * const c); + + public: + + class Context_already_in_use : public Exception { }; + class Context_not_associated : public Exception { }; + + /** + * Constructor + */ + Signal_receiver(); + + /** + * Destructor + */ + ~Signal_receiver(); + + /** + * Let a context 'c' be managed by us + * + * \return Capability wich names the context kernel-object in its + * 'dst' field . Can be used as target for transmitters. + */ + Signal_context_capability manage(Signal_context * const c); + + /** + * Let a context 'c' no longer be managed by us + */ + void dissolve(Signal_context * const c); + + /** + * Block on any signal that is triggered at one of our contexts + */ + Signal wait_for_signal(); + }; +} + +#endif /* _INCLUDE__BASE__SIGNAL_H__ */ + diff --git a/base-hw/include/kernel/log.h b/base-hw/include/kernel/log.h new file mode 100644 index 0000000000..a60d3f127b --- /dev/null +++ b/base-hw/include/kernel/log.h @@ -0,0 +1,109 @@ +/* + * \brief Print to kernel log output + * \author Martin stein + * \date 2012-04-05 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This 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__LOG_H_ +#define _INCLUDE__KERNEL__LOG_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + /** + * Prints incoming streams to the kernel log output + */ + class Kernel_log + { + /** + * Print an unsigned 4 bit integer as hexadecimal value + */ + void _print_4bit_hex(unsigned char x) const + { + /* decode to ASCII char */ + x &= 0x0f; + if (x > 9) x += 39; + x += 48; + + /* print ASCII char */ + Kernel::print_char(x); + } + + public: + + /** + * Print zero-terminated string + */ + Kernel_log & operator << (char const * s) + { + while (*s) Kernel::print_char(*s++); + return *this; + } + + /** + * Print an unsigned integer as hexadecimal value + */ + Kernel_log & operator << (unsigned int const & x) + { + enum { + BYTE_WIDTH = 8, + CW = sizeof(unsigned char)*BYTE_WIDTH, + IW = sizeof(unsigned int)*BYTE_WIDTH + }; + + /* + * Walk from most significant to least significant bit and + * process 2 hex digits per step. + */ + bool leading = true; + for (int i = IW - CW; i >= 0; i = i - CW) + { + /* fetch the 2 current digits */ + unsigned char c = (char)((x >> i) & 0xff); + + /* has the payload part of the value already begun? */ + if (leading) + { + /* ignore leading zeros ... */ + if (!c) + { + /* ... expect they are the only digits */ + if (i == 0) _print_4bit_hex(c); + continue; + } + /* transit from leading to payload part */ + leading = false; + if (c < 0x10) { + _print_4bit_hex(c); + continue; + } + } + /* print both digits */ + _print_4bit_hex(c >> 4); + _print_4bit_hex(c); + } + return *this; + } + }; + + /** + * Give static 'Kernel_log' reference as target for common log output + */ + inline Kernel_log & kernel_log() + { + static Kernel_log _log; + return _log; + } +} + +#endif /* _INCLUDE__KERNEL__LOG_H_ */ + diff --git a/base-hw/include/kernel/syscalls.h b/base-hw/include/kernel/syscalls.h new file mode 100644 index 0000000000..2274672b4a --- /dev/null +++ b/base-hw/include/kernel/syscalls.h @@ -0,0 +1,427 @@ +/* + * \brief Kernels syscall frontend + * \author Martin stein + * \date 2011-11-30 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This 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__SYSCALLS_H_ +#define _INCLUDE__KERNEL__SYSCALLS_H_ + +/* Genode includes */ +#include + +class Software_tlb; + +namespace Genode +{ + class Platform_thread; +} + +namespace Kernel +{ + /** + * Unique opcodes of all syscalls supported by the kernel + */ + enum Syscall_type + { + INVALID_SYSCALL = 0, + + /* execution control */ + NEW_THREAD = 1, + START_THREAD = 2, + PAUSE_THREAD = 3, + RESUME_THREAD = 4, + GET_THREAD = 5, + CURRENT_THREAD_ID = 6, + YIELD_THREAD = 7, + READ_REGISTER = 18, + WRITE_REGISTER = 19, + + /* interprocess communication */ + REQUEST_AND_WAIT = 8, + REPLY_AND_WAIT = 9, + WAIT_FOR_REQUEST = 10, + + /* management of resource protection-domains */ + SET_PAGER = 11, + UPDATE_PD = 12, + NEW_PD = 13, + + /* interrupt handling */ + ALLOCATE_IRQ = 14, + AWAIT_IRQ = 15, + FREE_IRQ = 16, + + /* debugging */ + PRINT_CHAR = 17, + + /* asynchronous signalling */ + NEW_SIGNAL_RECEIVER = 20, + NEW_SIGNAL_CONTEXT = 21, + AWAIT_SIGNAL = 22, + SUBMIT_SIGNAL = 23, + }; + + /** + * Virtual range of the mode transition region in every PD + */ + Genode::addr_t mode_transition_virt_base(); + Genode::size_t mode_transition_size(); + + /** + * Get sizes of the kernel objects + */ + Genode::size_t thread_size(); + Genode::size_t pd_size(); + Genode::size_t signal_context_size(); + Genode::size_t signal_receiver_size(); + + /** + * Get alignment constraints of the kernel objects + */ + unsigned kernel_pd_alignm_log2(); + + + /** + * Create a new PD + * + * \param dst physical base of an appropriate portion of memory + * that is thereupon allocated to the kernel + * + * \retval >0 ID of the new PD + * \retval 0 if no new PD was created + * + * Restricted to core threads. Regaining of the supplied memory is not + * supported by now. + */ + inline int new_pd(void * const dst) + { return syscall(NEW_PD, (Syscall_arg)dst); } + + + /** + * Propagate changes in PD configuration + * + * \param pd_id ID of the PD that has been configured + * + * It might be, that the kernel and/or the hardware caches parts of PD + * configurations such as virtual address translations. This syscall + * ensures that the current configuration of the targeted PD gets fully + * applied from the moment it returns to the userland. This syscall is + * inappropriate in case that a PD wants to change its own configuration. + * There's no need for this syscall after a configuration change that + * can't affect the kernel and/or hardware caches. + * + * Restricted to core threads. + */ + inline void update_pd(unsigned long const pd_id) + { syscall(UPDATE_PD, (Syscall_arg)pd_id); } + + + /** + * Create a new thread that is stopped initially + * + * \param dst physical base of an appropriate portion of memory + * that is thereupon allocated to the kernel + * \param pt assigned platform thread + * + * \retval >0 ID of the new thread + * \retval 0 if no new thread was created + * + * Restricted to core threads. Regaining of the supplied memory is not + * supported by now. + */ + inline int new_thread(void * const dst, Genode::Platform_thread * const pt) + { return syscall(NEW_THREAD, (Syscall_arg)dst, (Syscall_arg)pt); } + + + /** + * Start thread with a given context and let it participate in CPU scheduling + * + * \param id ID of targeted thread + * \param ip initial instruction pointer + * \param sp initial stack pointer + * + * \retval >0 success, return value is the software TLB of the thread + * \retval 0 the targeted thread wasn't started or was already started + * when this gets called (in both cases it remains untouched) + * + * Restricted to core threads. + */ + inline Software_tlb * + start_thread(Genode::Platform_thread * const phys_pt, void * ip, void * sp, + unsigned int cpu_no) + { + return (Software_tlb *)syscall(START_THREAD, + (Syscall_arg)phys_pt, + (Syscall_arg)ip, + (Syscall_arg)sp, + (Syscall_arg)cpu_no); + } + + + /** + * Prevent thread from participating in CPU scheduling + * + * \param id ID of the targeted thread. If not set + * this will target the current thread. + * + * \retval 0 syscall was successful + * \retval <0 if the targeted thread does not exist or still participates + * in CPU scheduling after + * + * If the caller doesn't target itself, this is restricted to core threads. + */ + inline int pause_thread(unsigned long const id = 0) + { return syscall(PAUSE_THREAD, id); } + + + /** + * Let an already started thread participate in CPU scheduling + * + * \param id ID of the targeted thread + * + * \retval 0 if syscall was successful and thread were paused beforehand + * \retval >0 if syscall was successful and thread were already active + * \retval <0 if targeted thread doesn't participate in CPU + * scheduling after + */ + inline int resume_thread(unsigned long const id = 0) + { return syscall(RESUME_THREAD, id); } + + + /** + * Let the current thread give up its remaining timeslice + * + * \param id if this thread ID is set and valid this will resume the + * targeted thread additionally + */ + inline void yield_thread(unsigned long const id = 0) + { syscall(YIELD_THREAD, id); } + + + /** + * Get the thread ID of the current thread + */ + inline int current_thread_id() { return syscall(CURRENT_THREAD_ID); } + + + /** + * Get platform thread by ID or 0 if target is "core main" or "idle" + * + * \param id ID of the targeted thread or 0 if caller targets itself + * + * Restricted to core threads. + */ + inline Genode::Platform_thread * get_thread(unsigned long const id = 0) + { return (Genode::Platform_thread *)syscall(GET_THREAD, id); } + + + /** + * Send IPC request and wait for reply + * + * \param id ID of the receiver thread + * \param size request size (beginning with the callers UTCB base) + * + * \return size of received reply (beginning with the callers UTCB base) + * + * If the receiver exists, this blocks execution until a dedicated reply + * message has been send by the receiver. The receiver may never do so. + */ + inline unsigned long request_and_wait(unsigned long const id, + unsigned long const size) + { return (unsigned long)syscall(REQUEST_AND_WAIT, id, size); } + + + /** + * Wait for next IPC request, discard current request + * + * \return size of received request (beginning with the callers UTCB base) + */ + inline unsigned long wait_for_request() + { return (unsigned long)syscall(WAIT_FOR_REQUEST); } + + + /** + * Send reply of the last received request and wait for next request + * + * \param size reply-message size (beginning with the callers UTCB base) + * + * \return size of received request (beginning with the callers UTCB base) + */ + inline unsigned long reply_and_wait(unsigned long const size) + { return (unsigned long)syscall(REPLY_AND_WAIT, size); } + + + /** + * Set a thread that gets informed about pagefaults of another thread + * + * \param pager_id ID of the thread that shall get informed. + * Subsequently this thread gets an IPC message, + * wich contains an according 'Pagefault' object for + * every pagefault the faulter throws. + * \param faulter_id ID of the thread that throws the pagefaults + * wich shall be notified. After every pagefault this + * thread remains paused to be reactivated by + * 'resume_thread'. + * + * Restricted to core threads. + */ + inline void set_pager(unsigned long const pager_id, + unsigned long const faulter_id) + { syscall(SET_PAGER, pager_id, faulter_id); } + + /** + * Print a char 'c' to the kernels serial ouput + */ + inline void print_char(char const c) + { syscall(PRINT_CHAR, (Syscall_arg)c); } + + /** + * Allocate an IRQ to the caller if the IRQ is not allocated already + * + * \param id ID of the targeted IRQ + * + * \return wether the IRQ has been allocated to this thread or not + * + * Restricted to core threads. + */ + inline bool allocate_irq(unsigned long const id) + { return syscall(ALLOCATE_IRQ, (Syscall_arg)id); } + + + /** + * Free an IRQ from allocation if it is allocated by the caller + * + * \param id ID of the targeted IRQ + * + * \return wether the IRQ has been freed or not + * + * Restricted to core threads. + */ + inline bool free_irq(unsigned long const id) + { return syscall(FREE_IRQ, (Syscall_arg)id); } + + + /** + * Block caller for the occurence of its IRQ + * + * Restricted to core threads. Blocks the caller forever + * if he has not allocated any IRQ. + */ + inline void await_irq() { syscall(AWAIT_IRQ); } + + + /** + * Get the current value of a register of a specific CPU context + * + * \param thread_id ID of the thread that owns the targeted context + * \param reg_id platform-specific ID of the targeted register + * + * Restricted to core threads. One can also read from its own context, + * or any thread that is active in the meantime. In these cases + * be aware of the fact, that the result reflects the context + * state that were backed at the last kernel entry of the thread. + */ + inline unsigned long read_register(unsigned long const thread_id, + unsigned long const reg_id) + { + return syscall(READ_REGISTER, (Syscall_arg)thread_id, + (Syscall_arg)reg_id); + } + + + /** + * Write a value to a register of a specific CPU context + * + * \param thread_id ID of the thread that owns the targeted context + * \param reg_id platform-specific ID of the targeted register + * \param value value that shall be written to the register + * + * Restricted to core threads. One can also write to its own context, or + * to that of a thread that is active in the meantime. + */ + inline void write_register(unsigned long const thread_id, + unsigned long const reg_id, + unsigned long const value) + { + syscall(WRITE_REGISTER, (Syscall_arg)thread_id, (Syscall_arg)reg_id, + (Syscall_arg)value); + } + + + /** + * Create a kernel object that acts as receiver for asynchronous signals + * + * \param dst physical base of an appropriate portion of memory + * that is thereupon allocated to the kernel + * + * \return ID of the new kernel object + * + * Restricted to core threads. Regaining of the supplied memory is not + * supported by now. + */ + inline unsigned long new_signal_receiver(void * dst) + { return syscall(NEW_SIGNAL_RECEIVER, (Syscall_arg)dst); } + + + /** + * Create a kernel object that acts as a distinct signal type at a receiver + * + * \param dst physical base of an appropriate portion of memory + * that is thereupon allocated to the kernel + * \param receiver_id ID of the receiver kernel-object that shall + * provide the new signal context + * \param imprint Every signal, one receives at the new context, + * will hold this imprint. This enables the receiver + * to interrelate signals with the context. + * + * \return ID of the new kernel object + * + * Core-only syscall. Regaining of the supplied memory is not + * supported by now. + */ + inline unsigned long new_signal_context(void * dst, + unsigned long receiver_id, + unsigned long imprint) + { + return syscall(NEW_SIGNAL_CONTEXT, (Syscall_arg)dst, + (Syscall_arg)receiver_id, (Syscall_arg)imprint); + } + + + /** + * Wait for occurence of at least one signal at any context of a receiver + * + * \param receiver_id ID of the targeted receiver kernel-object + * + * When this call returns, an instance of 'Signal' is located at the base + * of the callers UTCB. It holds information about wich context was + * triggered how often. It is granted that every occurence of a signal is + * provided through this function, exactly till it gets delivered through + * this function. If multiple threads listen at the same receiver and/or + * multiple contexts trigger simultanously there is no assertion about + * wich thread receives the 'Signal' instance of wich context. + */ + inline void await_signal(unsigned long receiver_id) + { syscall(AWAIT_SIGNAL, (Syscall_arg)receiver_id); } + + + /** + * Trigger a specific signal context + * + * \param context_id ID of the targeted context kernel-object + * \param num how often the context shall be triggered by this call + */ + inline void submit_signal(unsigned long context_id, int num) + { syscall(SUBMIT_SIGNAL, (Syscall_arg)context_id, (Syscall_arg)num); } +} + +#endif /* _INCLUDE__KERNEL__SYSCALLS_H_ */ + diff --git a/base-hw/include/pl011/drivers/serial_log.h b/base-hw/include/pl011/drivers/serial_log.h new file mode 100644 index 0000000000..7cfaa2c8c5 --- /dev/null +++ b/base-hw/include/pl011/drivers/serial_log.h @@ -0,0 +1,43 @@ +/* + * \brief Serial output driver specific for the ARM PL011 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PL011__DRIVERS__SERIAL_LOG_H_ +#define _INCLUDE__PL011__DRIVERS__SERIAL_LOG_H_ + +/* Genode includes */ +#include +#include + +namespace Genode +{ + /** + * Serial output driver specific for the ARM PL011 + */ + class Serial_log : public Pl011_base + { + public: + + /** + * Constructor + * + * \param baud_rate targeted transfer baud-rate + */ + Serial_log(unsigned const baud_rate) : + Pl011_base(Board::LOG_PL011_MMIO_BASE, + Board::LOG_PL011_CLOCK, baud_rate) + { } + }; +} + +#endif /* _INCLUDE__PL011__DRIVERS__SERIAL_LOG_H_ */ + diff --git a/base-hw/include/platform/panda_a2/drivers/board.h b/base-hw/include/platform/panda_a2/drivers/board.h new file mode 100644 index 0000000000..c631a92c05 --- /dev/null +++ b/base-hw/include/platform/panda_a2/drivers/board.h @@ -0,0 +1,37 @@ +/** + * \brief Motherboard driver specific for the PandaBoard A2 + * \author Martin Stein + * \date 2012-03-08 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PLATFORM__PANDA_A2__DRIVERS__BOARD_H_ +#define _INCLUDE__PLATFORM__PANDA_A2__DRIVERS__BOARD_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + /** + * Provide specific board driver + */ + class Board : public Panda_a2 + { + public: + + enum { + LOG_TL16C750_MMIO_BASE = TL16C750_3_MMIO_BASE, + LOG_TL16C750_CLOCK = TL16C750_3_CLOCK, + }; + }; +} + +#endif /* _INCLUDE__PLATFORM__PANDA_A2__DRIVERS__BOARD_H_ */ + diff --git a/base-hw/include/platform/pbxa9/drivers/board.h b/base-hw/include/platform/pbxa9/drivers/board.h new file mode 100644 index 0000000000..12ab61dd72 --- /dev/null +++ b/base-hw/include/platform/pbxa9/drivers/board.h @@ -0,0 +1,38 @@ +/** + * \brief Motherboard driver specific for the Realview PBXA9 + * \author Martin Stein + * \date 2011-11-03 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PLATFORM__PBXA9__DRIVERS__BOARD_H_ +#define _INCLUDE__PLATFORM__PBXA9__DRIVERS__BOARD_H_ + +/* Genode includes */ +#include +#include + +namespace Genode +{ + /** + * Provide specific board driver + */ + class Board : public Pbxa9 + { + public: + + enum { + LOG_PL011_MMIO_BASE = PL011_0_MMIO_BASE, + LOG_PL011_CLOCK = PL011_0_CLOCK, + }; + }; +} + +#endif /* _INCLUDE__PLATFORM__PBXA9__DRIVERS__BOARD_H_ */ + diff --git a/base-hw/include/platform/vea9x4/drivers/board.h b/base-hw/include/platform/vea9x4/drivers/board.h new file mode 100644 index 0000000000..e9f1ccc43f --- /dev/null +++ b/base-hw/include/platform/vea9x4/drivers/board.h @@ -0,0 +1,37 @@ +/** + * \brief Provide specific board driver + * \author Martin Stein + * \date 2011-11-03 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_H_ +#define _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_H_ + +/* Genode inlcudes */ +#include + +namespace Genode +{ + /** + * Provide specific board driver + */ + class Board : public Vea9x4 + { + public: + + enum { + LOG_PL011_MMIO_BASE = PL011_0_MMIO_BASE, + LOG_PL011_CLOCK = PL011_0_CLOCK, + }; + }; +} + +#endif /* _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_H_ */ + diff --git a/base-hw/include/signal_session/client.h b/base-hw/include/signal_session/client.h new file mode 100644 index 0000000000..339a491b3b --- /dev/null +++ b/base-hw/include/signal_session/client.h @@ -0,0 +1,51 @@ +/* + * \brief Client-side implementation of the signal session interface + * \author Martin Stein + * \date 2012-05-05 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This 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_ + +/* Genode includes */ +#include +#include +#include + +namespace Genode +{ + /** + * Client-side implementation of the signal session interface + */ + struct Signal_session_client : Rpc_client + { + /** + * Constructor + * + * \param s targeted signal session + */ + explicit Signal_session_client(Signal_session_capability const s) + : Rpc_client(s) { } + + /****************************** + ** Signal_session interface ** + ******************************/ + + Signal_receiver_capability alloc_receiver() + { return call(); } + + Signal_context_capability + alloc_context(Signal_receiver_capability const r, + unsigned long const imprint) + { return call(r, imprint); } + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__CLIENT_H_ */ diff --git a/base-hw/include/signal_session/signal_session.h b/base-hw/include/signal_session/signal_session.h new file mode 100644 index 0000000000..ece09d4201 --- /dev/null +++ b/base-hw/include/signal_session/signal_session.h @@ -0,0 +1,96 @@ +/* + * \brief Signal session interface + * \author Martin Stein + * \date 2012-05-05 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This 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_ + +/* Genode includes */ +#include +#include +#include + +namespace Genode +{ + class Signal_receiver; + class Signal_context; + + /* + * The 'dst' of this cap is used to communicate the ID of the + * corresponding signal-receiver kernel-object or 0 if the cap is invalid. + */ + typedef Capability Signal_receiver_capability; + + /* + * The 'dst' of this cap is used to communicate the ID of the + * corresponding signal-context kernel-object or 0 if the cap is invalid. + */ + typedef Capability Signal_context_capability; + + /** + * Signal session interface + */ + struct Signal_session : Session + { + class Out_of_metadata : public Exception { }; + + /** + * String that can be used to refer to this service + */ + static const char * service_name() { return "SIGNAL"; } + + /** + * Destructor + */ + virtual ~Signal_session() { } + + /** + * Create a new signal-receiver kernel-object + * + * \return a cap that acts as reference to the created object + * + * \throw Out_of_metadata + */ + virtual Signal_receiver_capability alloc_receiver() = 0; + + /** + * Create a new signal-context kernel-object + * + * \param r names the signal receiver that shall provide + * the new context + * \param imprint every signal that occures on the new context gets + * signed with this value + * + * \return a cap that acts as reference to the created object + * + * \throw Out_of_metadata + */ + virtual Signal_context_capability + alloc_context(Signal_receiver_capability const r, + unsigned long const imprint) = 0; + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC_THROW(Rpc_alloc_receiver, Signal_receiver_capability, + alloc_receiver, GENODE_TYPE_LIST(Out_of_metadata)); + GENODE_RPC_THROW(Rpc_alloc_context, Signal_context_capability, + alloc_context, GENODE_TYPE_LIST(Out_of_metadata), + Signal_receiver_capability, unsigned long); + + GENODE_RPC_INTERFACE(Rpc_alloc_receiver, Rpc_alloc_context); + }; +} + +#endif /* _INCLUDE__SIGNAL_SESSION__SIGNAL_SESSION_H_ */ + diff --git a/base-hw/include/tl16c750/drivers/serial_log.h b/base-hw/include/tl16c750/drivers/serial_log.h new file mode 100644 index 0000000000..707c57afd2 --- /dev/null +++ b/base-hw/include/tl16c750/drivers/serial_log.h @@ -0,0 +1,43 @@ +/* + * \brief Serial output driver specific for the Texas Instruments TL16C750 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__TL16C750__DRIVERS__SERIAL_LOG_H_ +#define _INCLUDE__TL16C750__DRIVERS__SERIAL_LOG_H_ + +/* Genode includes */ +#include +#include + +namespace Genode +{ + /** + * Serial output driver specific for the Texas Instruments TL16C750 + */ + class Serial_log : public Tl16c750_base + { + public: + + /** + * Constructor + * + * \param baud_rate targeted transfer baud-rate + */ + Serial_log(unsigned const baud_rate) : + Tl16c750_base(Board::LOG_TL16C750_MMIO_BASE, + Board::LOG_TL16C750_CLOCK, baud_rate) + { } + }; +} + +#endif /* _INCLUDE__TL16C750__DRIVERS__SERIAL_LOG_H_ */ + diff --git a/base-hw/lib/mk/arm_v7a/startup.mk b/base-hw/lib/mk/arm_v7a/startup.mk new file mode 100755 index 0000000000..27308ccf52 --- /dev/null +++ b/base-hw/lib/mk/arm_v7a/startup.mk @@ -0,0 +1,22 @@ +# +# \brief Essential platform specific sources for common programs +# \author Martin Stein +# \date 2012-04-16 +# + +# add libraries +LIBS += cxx lock + +# add C++ sources +SRC_CC += _main.cc + +# add assembly sources +SRC_S += crt0.s syscall.cc + +# add include paths +INC_DIR += $(REP_DIR)/src/platform $(BASE_DIR)/src/platform + +# declare source paths +vpath crt0.s $(REP_DIR)/src/platform +vpath _main.cc $(BASE_DIR)/src/platform +vpath syscall.cc $(REP_DIR)/src/base/arm_v7a diff --git a/base-hw/lib/mk/arm_v7a/startup_core.mk b/base-hw/lib/mk/arm_v7a/startup_core.mk new file mode 100644 index 0000000000..955d4bb172 --- /dev/null +++ b/base-hw/lib/mk/arm_v7a/startup_core.mk @@ -0,0 +1,29 @@ +# +# \brief Essential platform specific sources for core +# \author Martin Stein +# \date 2012-04-16 +# + +# Aad C++ sources +SRC_CC += syscall.cc + +# add assembly sources +SRC_S += crt0.s mode_transition.s boot_modules.s + +# add inlucde paths +INC_DIR += $(BASE_DIR)/src/core/include/ + +# +# Check if there are other images wich shall be linked to core. +# If not use a dummy boot-modules file wich includes only the symbols. +# +ifeq ($(wildcard $(BUILD_BASE_DIR)/boot_modules.s),) +vpath boot_modules.s $(REP_DIR)/src/core/arm_v7a +else +INC_DIR += $(BUILD_BASE_DIR) +vpath boot_modules.s $(BUILD_BASE_DIR) +endif + +# declare remaining source paths +vpath syscall.cc $(REP_DIR)/src/base/arm_v7a +vpath % $(REP_DIR)/src/core/arm_v7a diff --git a/base-hw/lib/mk/core_support.inc b/base-hw/lib/mk/core_support.inc new file mode 100644 index 0000000000..a981d8895c --- /dev/null +++ b/base-hw/lib/mk/core_support.inc @@ -0,0 +1,20 @@ +# +# \brief Generic parts of the core-support lib +# \author Martin Stein +# \date 2012-04-27 +# + +# add include paths +INC_DIR += $(BOARD_DIR) $(REP_DIR)/src/core/include $(REP_DIR)/include \ + $(BASE_DIR)/src/core/include $(BASE_DIR)/include + +# set entry point of core's first thread +CC_OPT += -DCORE_MAIN=_main + +# add C++ sources +SRC_CC += platform_support.cc kernel.cc rm_session_support.cc + +# declare source paths +vpath platform_support.cc $(BOARD_DIR) +vpath % $(REP_DIR)/src/core + diff --git a/base-hw/lib/mk/ipc.mk b/base-hw/lib/mk/ipc.mk new file mode 100644 index 0000000000..ebf40787e8 --- /dev/null +++ b/base-hw/lib/mk/ipc.mk @@ -0,0 +1,12 @@ +# +# \brief Interprocess communication +# \author Martin Stein +# \date 2012-04-16 +# + +# add library dependencies +LIBS += thread + +# include implied libraries +include $(REP_DIR)/lib/mk/raw_ipc.mk + diff --git a/base-hw/lib/mk/lock.mk b/base-hw/lib/mk/lock.mk new file mode 100644 index 0000000000..f7096c18a0 --- /dev/null +++ b/base-hw/lib/mk/lock.mk @@ -0,0 +1,14 @@ +# +# \brief Synchronisation through locks +# \author Martin Stein +# \date 2012-04-16 +# + +# add C++ sources +SRC_CC += lock.cc + +# add include paths +INC_DIR += $(REP_DIR)/src/base/lock + +# declare source paths +vpath % $(BASE_DIR)/src/base/lock diff --git a/base-hw/lib/mk/pager.mk b/base-hw/lib/mk/pager.mk new file mode 100644 index 0000000000..7ac60ef90c --- /dev/null +++ b/base-hw/lib/mk/pager.mk @@ -0,0 +1,11 @@ +# +# \brief Genode pager threads +# \author Martin Stein +# \date 2012-04-16 +# + +# add C++ sources +SRC_CC += pager.cc + +# declare source paths +vpath % $(REP_DIR)/src/base diff --git a/base-hw/lib/mk/platform_panda_a2/core_support.mk b/base-hw/lib/mk/platform_panda_a2/core_support.mk new file mode 100644 index 0000000000..60ab9f316f --- /dev/null +++ b/base-hw/lib/mk/platform_panda_a2/core_support.mk @@ -0,0 +1,12 @@ +# +# \brief Parts of core that depend on the PandaBoard A2 +# \author Martin Stein +# \date 2012-04-27 +# + +# declare location of core files that are board specific +BOARD_DIR = $(REP_DIR)/src/core/panda_a2 + +# include generic parts of core support +include $(REP_DIR)/lib/mk/core_support.inc + diff --git a/base-hw/lib/mk/platform_panda_a2/platform_support.mk b/base-hw/lib/mk/platform_panda_a2/platform_support.mk new file mode 100644 index 0000000000..cc4a186932 --- /dev/null +++ b/base-hw/lib/mk/platform_panda_a2/platform_support.mk @@ -0,0 +1,16 @@ +# +# \brief Platform implementations specific for base-hw and Panda A2 +# \author Martin Stein +# \date 2012-05-10 +# + +# add include paths +INC_DIR += $(REP_DIR)/src/core/include \ + $(BASE_DIR)/src/core/include + +# add C++ sources +SRC_CC += platform_support.cc + +# declare source paths +vpath % $(REP_DIR)/src/core/panda_a2 + diff --git a/base-hw/lib/mk/platform_pbxa9/core_support.mk b/base-hw/lib/mk/platform_pbxa9/core_support.mk new file mode 100644 index 0000000000..0a908f3053 --- /dev/null +++ b/base-hw/lib/mk/platform_pbxa9/core_support.mk @@ -0,0 +1,12 @@ +# +# \brief Parts of core that depend on the Realview PBXA9 +# \author Martin Stein +# \date 2012-04-27 +# + +# declare location of core files that are board specific +BOARD_DIR = $(REP_DIR)/src/core/pbxa9 + +# include generic part of core support +include $(REP_DIR)/lib/mk/core_support.inc + diff --git a/base-hw/lib/mk/platform_vea9x4/core_support.mk b/base-hw/lib/mk/platform_vea9x4/core_support.mk new file mode 100644 index 0000000000..51a9cf5dd6 --- /dev/null +++ b/base-hw/lib/mk/platform_vea9x4/core_support.mk @@ -0,0 +1,12 @@ +# +# \brief Parts of core that depend on the Versatile VEA9X4 +# \author Martin Stein +# \date 2012-04-27 +# + +# declare location of core files that are board specific +BOARD_DIR = $(REP_DIR)/src/core/vea9x4 + +# include generic part of core support +include $(REP_DIR)/lib/mk/core_support.inc + diff --git a/base-hw/lib/mk/raw_ipc.mk b/base-hw/lib/mk/raw_ipc.mk new file mode 100644 index 0000000000..41add43576 --- /dev/null +++ b/base-hw/lib/mk/raw_ipc.mk @@ -0,0 +1,11 @@ +# +# \brief Interprocess communication without thread implementations +# \author Martin Stein +# \date 2012-04-16 +# + +# add C++ source files +SRC_CC += ipc.cc + +# declare source paths +vpath % $(REP_DIR)/src/base diff --git a/base-hw/lib/mk/raw_signal.mk b/base-hw/lib/mk/raw_signal.mk new file mode 100644 index 0000000000..01cc737fde --- /dev/null +++ b/base-hw/lib/mk/raw_signal.mk @@ -0,0 +1,15 @@ +# +# \brief Submit and receive asynchronous events +# \author Martin Stein +# \date 2012-05-07 +# + +# add C++ sources +SRC_CC += signal.cc + +# add library dependencies +LIBS += slab + +# declare source paths +vpath % $(REP_DIR)/src/base/signal + diff --git a/base-hw/lib/mk/signal.mk b/base-hw/lib/mk/signal.mk new file mode 100644 index 0000000000..43d6803860 --- /dev/null +++ b/base-hw/lib/mk/signal.mk @@ -0,0 +1,9 @@ +# +# \brief Shortcut for raw-signal lib to ensure Genode compliance +# \author Martin Stein +# \date 2012-04-16 +# + +# include implied libraries +include $(REP_DIR)/lib/mk/raw_signal.mk + diff --git a/base-hw/lib/mk/thread.mk b/base-hw/lib/mk/thread.mk new file mode 100644 index 0000000000..703231cfa2 --- /dev/null +++ b/base-hw/lib/mk/thread.mk @@ -0,0 +1,12 @@ +# +# \brief Implementation of the Genode thread-API +# \author Martin Stein +# \date 2012-04-16 +# + +# add C++ sources +SRC_CC += thread.cc thread_bootstrap.cc thread_support.cc + +# declare source paths +vpath thread_support.cc $(REP_DIR)/src/base/ +vpath % $(BASE_DIR)/src/base/thread/ diff --git a/base-hw/mk/spec-hw.mk b/base-hw/mk/spec-hw.mk new file mode 100644 index 0000000000..8187cb6e8a --- /dev/null +++ b/base-hw/mk/spec-hw.mk @@ -0,0 +1,12 @@ +# +# \brief Offer build configurations that are specific to base-hw +# \author Martin Stein +# \date 2012-04-16 +# + +# +# Denote library that brings the setup sequence for C++ enviroment. +# Also add an according dependency. +# +STARTUP_LIB ?= startup +PRG_LIBS += $(STARTUP_LIB) diff --git a/base-hw/mk/spec-hw_panda_a2.mk b/base-hw/mk/spec-hw_panda_a2.mk new file mode 100644 index 0000000000..6ce54a29f1 --- /dev/null +++ b/base-hw/mk/spec-hw_panda_a2.mk @@ -0,0 +1,15 @@ +# +# \brief Offer build configurations that are specific to base-hw and Pandaboard A2 +# \author Martin Stein +# \date 2011-12-20 +# + +# denote wich specs are also fullfilled by this spec +SPECS += hw platform_panda_a2 + +# set address where to link the text segment at +LD_TEXT_ADDR ?= 0x80000000 + +# include implied specs +include $(call select_from_repositories,mk/spec-hw.mk) +include $(call select_from_repositories,mk/spec-platform_panda_a2.mk) diff --git a/base-hw/mk/spec-hw_pbxa9.mk b/base-hw/mk/spec-hw_pbxa9.mk new file mode 100644 index 0000000000..de2cbbad5d --- /dev/null +++ b/base-hw/mk/spec-hw_pbxa9.mk @@ -0,0 +1,15 @@ +# +# \brief Offer build configurations that are specific to base-hw and PBXA9 +# \author Martin Stein +# \date 2011-12-20 +# + +# denote wich specs are also fullfilled by this spec +SPECS += hw platform_pbxa9 + +# set address where to link text segment at +LD_TEXT_ADDR ?= 0x01000000 + +# include implied specs +include $(call select_from_repositories,mk/spec-hw.mk) +include $(call select_from_repositories,mk/spec-platform_pbxa9.mk) diff --git a/base-hw/mk/spec-hw_vea9x4.mk b/base-hw/mk/spec-hw_vea9x4.mk new file mode 100644 index 0000000000..f0e8a76bd7 --- /dev/null +++ b/base-hw/mk/spec-hw_vea9x4.mk @@ -0,0 +1,15 @@ +# +# \brief Offer build configurations that are specific to base-hw and VEA9X4 +# \author Martin Stein +# \date 2011-12-20 +# + +# denote wich specs are also fullfilled by this spec +SPECS += hw platform_vea9x4 + +# set address where to link text segment at +LD_TEXT_ADDR ?= 0x01000000 + +# include implied specs +include $(call select_from_repositories,mk/spec-hw.mk) +include $(call select_from_repositories,mk/spec-platform_vea9x4.mk) diff --git a/base-hw/run/env b/base-hw/run/env new file mode 100644 index 0000000000..def0e0a1e0 --- /dev/null +++ b/base-hw/run/env @@ -0,0 +1,229 @@ +#!/usr/bin/expect + +# +# \brief Implementation of the interface provided by 'tool/run' +# \author Martin Stein +# \date 2011-12-16 +# + + +###################### +## Utility routines ## +###################### + +# +# Build an assembly file that enables the creation of a single boot image +# +# \param file targeted file +# \parem binaries targeted boot modules, must reside in '[run_dir]/genode/' +# +# This file rawly includes all binaries given in +# 'binaries', minus 'core' if given, plus 'config' if available. It also +# provides a simple file system that enables Genode to access these BLOBs. +# +proc boot_modules_arm_v7a {{file} {binaries}} { + + set load_store_alignm 0x3 + set min_page_alignm 12 + + # introduce boot module headers + exec echo -e \ + "/**" \ + "\n * This file was automatically generated by the procedure" \ + "\n * 'boot_modules_arm_v7a' in 'run/env'." \ + "\n */" \ + "\n" \ + "\n.section .data" \ + "\n" \ + "\n.align ${load_store_alignm}" \ + "\n.global _boot_modules_begin" \ + "\n_boot_modules_begin:" \ + "\n.string \"GROM\"" \ + "\n" \ + "\n.align ${load_store_alignm}" \ + "\n.global _boot_module_headers_begin" \ + "\n_boot_module_headers_begin:" > $file + + # generate header for each boot module except core + set i 1 + foreach binary $binaries { + if {$binary == "core"} { continue } + exec echo -e \ + "\n.long mod${i}_name" \ + "\n.long mod${i}_start" \ + "\n.long mod${i}_end - mod${i}_start" >> $file + incr i + } + + # end boot module headers + exec echo -e \ + "\n.global _boot_module_headers_end" \ + "\n_boot_module_headers_end:" \ + "\n" >> $file + + # generate name string for each module except core + set i 1 + foreach binary $binaries { + if {$binary == "core"} { continue } + exec echo -e \ + ".align ${load_store_alignm}" \ + "\nmod${i}_name:" \ + "\n.string \"${binary}\"" \ + "\n.byte 0" \ + "\n" >> $file + incr i + } + + # include raw data of modules consecutively but page aligned + set i 1 + foreach binary $binaries { + if {$binary == "core"} { continue } + exec echo -e \ + ".align ${min_page_alignm}" \ + "\nmod${i}_start:" \ + "\n.incbin \"[run_dir]/genode/${binary}\"" \ + "\nmod${i}_end:" \ + "\n" >> $file + incr i + } + + # end boot-modules file + exec echo -e \ + ".global _boot_modules_end" \ + "\n_boot_modules_end:" >> $file +} + + +# +# Ensure that the next Genode build includes no target specific boot modules +# +proc disable_specific_boot_modules { } { + + exec rm -rf boot_modules.s + exec rm -rf var/libcache/boot_modules/boot_modules.o +} + + +# +# Ensure that the next Genode build includes target specific boot modules +# +proc enable_specific_boot_modules {file} { + + exec rm -rf boot_modules.s + exec ln -s $file boot_modules.s + exec rm -rf var/libcache/boot_modules/boot_modules.o +} + + +# +# Apply the boot modules file 'file' to the core image +# +proc apply_boot_modules_to_core {file} { + + # recompile 'core', with specific boot modules + puts "Building single boot image" + set timeout 10000 + enable_specific_boot_modules $file + set pid [eval "spawn make core"] + expect { eof { } } + if {[lindex [wait $pid] end] != 0} { + puts "Error: Genode build failed" + exit -4 + } + + # clean up for subsequent builds + disable_specific_boot_modules + puts "Single boot image built" +} + + +###################################### +## Interface supplied by 'tool/run' ## +###################################### + +proc build {targets} { + + # skip targets that shall not be build + if {[get_cmd_switch --skip-build]} return + + # handle false remnants of previous builds + disable_specific_boot_modules + + # + # Build all remaining targets. + # Core is build with a dummy boot-modules file first. + # + regsub -all {\s\s+} $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 install_config {config} { + + set fh [open "bin/config" "WRONLY CREAT TRUNC"] + puts $fh $config + close $fh +} + + +proc create_boot_directory { } { + + exec rm -rf [run_dir] + exec mkdir -p [run_dir]/genode +} + + +proc build_boot_image {binaries} { + + # create additional stripped version of any binary + copy_and_strip_genode_binaries_to_run_dir $binaries + + # append init configuration if existent to the binaries + if {[file exists "bin/config"] == 1} { + exec cp bin/config [run_dir]/genode + append binaries " config" + } + + # create scenario-specific 'boot_modules.s' of all given binaries + set boot_modules "[run_dir]/boot_modules.s" + if { [have_spec {arm_v7a}] } { + boot_modules_arm_v7a $boot_modules $binaries + } + + # preserve stand-alone core for debugging + exec mv core/core core/core.standalone + + # apply the new 'boot_modules.s' to 'core' to create single boot image + apply_boot_modules_to_core $boot_modules + exec mv core/core [run_dir]/image.elf + exec [cross_dev_prefix]strip [run_dir]/image.elf + + # remove stripped binaries and retrieve stand-alone core + exec mv core/core.standalone core/core + exec rm -r [run_dir]/genode +} + + +proc run_genode_until {{wait_for_re forever} {timeout_value 0}} { + + # get the targeted method of execution, the default is 'qemu' + set image [run_dir]/image.elf + set target [get_cmd_arg --target "qemu"] + + # try to execute image according to the targeted method of execution + if { $target == "qemu" } { + spawn_qemu $wait_for_re $timeout_value + } else { + puts stderr "Error: Target '${target}' is not supported" + puts stderr " Supported targets are: 'qemu'"; exit -3 + } +} + diff --git a/base-hw/run/nested_init.run b/base-hw/run/nested_init.run new file mode 100644 index 0000000000..823cad1aa0 --- /dev/null +++ b/base-hw/run/nested_init.run @@ -0,0 +1,75 @@ +# +# \brief Test genode basics by starting init instances in a nested manner +# \author Martin Stein +# \date 2012-05-30 +# + +# build program images +build "core init" + +# create directory where the boot files are written to +create_boot_directory + +# create XML configuration for init +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +# create single boot image from the compiled program images +build_boot_image "core init" + +# configure qemu to use 64 MB RAM and avoid GUI mode +append qemu_args " -m 64 -nographic" + +# execute the test in qemu if the targeted platform is supported +if {[have_spec hw_vea9x4] || [have_spec hw_pbxa9]} { + run_genode_until "No children to start.*\n" 10 +} + diff --git a/base-hw/src/base/arm_v7a/syscall.cc b/base-hw/src/base/arm_v7a/syscall.cc new file mode 100644 index 0000000000..3e7a42105d --- /dev/null +++ b/base-hw/src/base/arm_v7a/syscall.cc @@ -0,0 +1,160 @@ +/* + * \brief Syscall-framework implementation for ARM V7A systems + * \author Martin stein + * \date 2011-11-30 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file 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 Kernel; + + +/****************************************************************** + ** Inline assembly templates for syscalls with 1 to 6 arguments ** + ******************************************************************/ + +#define SYSCALL_6_ASM_OPS \ + "mov r5, #0 \n" \ + "add r5, %[arg_5] \n" \ + SYSCALL_5_ASM_OPS + +#define SYSCALL_5_ASM_OPS \ + "mov r4, #0 \n" \ + "add r4, %[arg_4] \n" \ + SYSCALL_4_ASM_OPS + +#define SYSCALL_4_ASM_OPS \ + "mov r3, #0 \n" \ + "add r3, %[arg_3] \n" \ + SYSCALL_3_ASM_OPS + +#define SYSCALL_3_ASM_OPS \ + "mov r2, #0 \n" \ + "add r2, %[arg_2] \n" \ + SYSCALL_2_ASM_OPS + +#define SYSCALL_2_ASM_OPS \ + "mov r1, #0 \n" \ + "add r1, %[arg_1] \n" \ + SYSCALL_1_ASM_OPS + +#define SYSCALL_1_ASM_OPS \ + "mov r0, #0 \n" \ + "add r0, %[arg_0] \n" \ + "svc 0x0 \n" \ + "mov %[result], #0 \n" \ + "add %[result], r0 " + + +/***************************************************************************** + ** Inline assembly "writeable" template-args for syscalls with 1 to 6 args ** + *****************************************************************************/ + +#define SYSCALL_6_ASM_WRITE [arg_5] "+r" (arg_5), SYSCALL_5_ASM_WRITE +#define SYSCALL_5_ASM_WRITE [arg_4] "+r" (arg_4), SYSCALL_4_ASM_WRITE +#define SYSCALL_4_ASM_WRITE [arg_3] "+r" (arg_3), SYSCALL_3_ASM_WRITE +#define SYSCALL_3_ASM_WRITE [arg_2] "+r" (arg_2), SYSCALL_2_ASM_WRITE +#define SYSCALL_2_ASM_WRITE [arg_1] "+r" (arg_1), SYSCALL_1_ASM_WRITE +#define SYSCALL_1_ASM_WRITE \ + [arg_0] "+r" (arg_0), \ + [result] "+r" (result) + + +/********************************************************************** + ** Inline assembly clobber lists for syscalls with 1 to 6 arguments ** + **********************************************************************/ + +#define SYSCALL_6_ASM_CLOBBER "r5", SYSCALL_5_ASM_CLOBBER +#define SYSCALL_5_ASM_CLOBBER "r4", SYSCALL_4_ASM_CLOBBER +#define SYSCALL_4_ASM_CLOBBER "r3", SYSCALL_3_ASM_CLOBBER +#define SYSCALL_3_ASM_CLOBBER "r2", SYSCALL_2_ASM_CLOBBER +#define SYSCALL_2_ASM_CLOBBER "r1", SYSCALL_1_ASM_CLOBBER +#define SYSCALL_1_ASM_CLOBBER "r0" + + +/************************************ + ** Syscalls with 1 to 6 arguments ** + ************************************/ + +Syscall_ret Kernel::syscall(Syscall_arg arg_0) +{ + Syscall_ret result = 0; + asm volatile(SYSCALL_1_ASM_OPS + : SYSCALL_1_ASM_WRITE + :: SYSCALL_1_ASM_CLOBBER); + return result; +} + + +Syscall_ret Kernel::syscall(Syscall_arg arg_0, + Syscall_arg arg_1) +{ + Syscall_ret result = 0; + asm volatile(SYSCALL_2_ASM_OPS + : SYSCALL_2_ASM_WRITE + :: SYSCALL_2_ASM_CLOBBER); + return result; +} + + +Syscall_ret Kernel::syscall(Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2) +{ + Syscall_ret result = 0; + asm volatile(SYSCALL_3_ASM_OPS + : SYSCALL_3_ASM_WRITE + :: SYSCALL_3_ASM_CLOBBER); + return result; +} + + +Syscall_ret Kernel::syscall(Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3) +{ + Syscall_ret result = 0; + asm volatile(SYSCALL_4_ASM_OPS + : SYSCALL_4_ASM_WRITE + :: SYSCALL_4_ASM_CLOBBER); + return result; +} + + +Syscall_ret Kernel::syscall(Syscall_arg arg_0, + Syscall_arg arg_1, + Syscall_arg arg_2, + Syscall_arg arg_3, + Syscall_arg arg_4) +{ + Syscall_ret result = 0; + asm volatile(SYSCALL_5_ASM_OPS + : SYSCALL_5_ASM_WRITE + :: SYSCALL_5_ASM_CLOBBER); + return result; +} + + +Syscall_ret Kernel::syscall(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_ret result = 0; + asm volatile(SYSCALL_6_ASM_OPS + : SYSCALL_6_ASM_WRITE + :: SYSCALL_6_ASM_CLOBBER); + return result; +} + diff --git a/base-hw/src/base/console.cc b/base-hw/src/base/console.cc new file mode 100644 index 0000000000..74072cebfb --- /dev/null +++ b/base-hw/src/base/console.cc @@ -0,0 +1,73 @@ +/** + * \brief Genode-console backend + * \author Martin Stein + * \date 2011-10-17 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file 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 Genode +{ + /** + * Platform specific Genode console + */ + class Platform_console : public Console, + public Serial_log + { + enum { BAUD_RATE = 115200 }; + + protected: + + /** + * Print a char to the console + */ + void _out_char(char c) { Serial_log::put_char(c); } + + public: + + /** + * Constructor + */ + Platform_console() : Serial_log(BAUD_RATE) { } + }; +} + +using namespace Genode; + + +/** + * Static object to print log output + */ +static Platform_console * platform_console() +{ + static Platform_console static_platform_console; + return &static_platform_console; +} + + +/**************************** + ** Genode print functions ** + ****************************/ + +void Genode::printf(const char *format, ...) +{ + va_list list; + va_start(list, format); + platform_console()->vprintf(format, list); + va_end(list); +} + + +void Genode::vprintf(const char *format, va_list list) +{ platform_console()->vprintf(format, list); } + diff --git a/base-hw/src/base/ipc.cc b/base-hw/src/base/ipc.cc new file mode 100644 index 0000000000..5b8cfce089 --- /dev/null +++ b/base-hw/src/base/ipc.cc @@ -0,0 +1,220 @@ +/* + * \brief Implementation of the Genode IPC-framework + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + +enum +{ + /* size of the callee-local name of a targeted RPC object */ + RPC_OBJECT_ID_SIZE = sizeof(umword_t), + + /* + * The RPC framework marshalls a return value into reply messages to + * deliver exceptions, wich occured during the RPC call to the caller. + * This defines the size of this value. + */ + RPC_RETURN_VALUE_SIZE = sizeof(umword_t), +}; + + +/*************** + ** Utilities ** + ***************/ + +/** + * Translate byte size 's' to size in words + */ +static unsigned long size_in_words(unsigned long const s) +{ return (s + sizeof(unsigned long) - 1) / sizeof(unsigned long); } + + +/** + * Copy message payload to message buffer + */ +static void copy_utcb_to_msgbuf(Msgbuf_base * const receive_buffer, + unsigned long const message_size) +{ + /* log data that is received via IPC */ + enum { VERBOSE = 0 }; + + /* get pointers and message attributes */ + Native_utcb * const utcb = Thread_base::myself()->utcb(); + unsigned long * const msgbuf = (unsigned long *)receive_buffer->buf; + unsigned long const message_wsize = size_in_words(message_size); + + /* assertions, avoid 'printf' in here, it may lead to infinite recursion */ + if (message_wsize > size_in_words(utcb->size())) + { + kernel_log() << __PRETTY_FUNCTION__ << ": Oversized message\n"; + while (1) ; + } + /* fill message buffer with message */ + for (unsigned i=0; i < message_wsize; i++) + msgbuf[i] = *utcb->word(i); +} + + +/** + * Copy message payload to the UTCB + */ +static void copy_msgbuf_to_utcb(Msgbuf_base * const send_buffer, + unsigned long const message_size, + unsigned long const local_name) +{ + /* log data that is send via IPC */ + enum { VERBOSE = 0 }; + + /* get pointers and message attributes */ + Native_utcb * const utcb = Thread_base::myself()->utcb(); + unsigned long * const msgbuf = (unsigned long *)send_buffer->buf; + unsigned long const message_wsize = size_in_words(message_size); + + /* assertions, avoid 'printf' in here, it may lead to infinite recursion */ + if (message_wsize > size_in_words(utcb->size())) + { + kernel_log() << __PRETTY_FUNCTION__ << ": Oversized message\n"; + while (1) ; + } + /* address message to an object that the targeted thread knows */ + *utcb->word(0) = local_name; + + /* write message payload */ + for (unsigned long i = 1; i < message_wsize; i++) + *utcb->word(i) = msgbuf[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 = RPC_OBJECT_ID_SIZE; +} + + +/***************** + ** Ipc_istream ** + *****************/ + +void Ipc_istream::_wait() +{ + /* FIXME this shall be not supported */ + Kernel::pause_thread(); +} + + +Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) +: + Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), + Native_capability(Genode::thread_get_my_native_id(), 0), + _rcv_msg(rcv_msg), _rcv_cs(-1) +{ _read_offset = RPC_OBJECT_ID_SIZE; } + + +Ipc_istream::~Ipc_istream() { } + + +/**************** + ** Ipc_client ** + ****************/ + +void Ipc_client::_call() +{ + using namespace Kernel; + + /* send request and receive reply */ + copy_msgbuf_to_utcb(_snd_msg, _write_offset, + Ipc_ostream::_dst.local_name()); + size_t const s = request_and_wait(Ipc_ostream::_dst.dst(), _write_offset); + copy_utcb_to_msgbuf(_rcv_msg, s); + + /* reset unmarshaller */ + _write_offset = _read_offset = RPC_OBJECT_ID_SIZE; +} + + +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 ** + ****************/ + +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) +{ } + + +void Ipc_server::_prepare_next_reply_wait() +{ + /* now we have a request to reply */ + _reply_needed = true; + + /* leave space for RPC method return value */ + _write_offset = RPC_OBJECT_ID_SIZE + RPC_RETURN_VALUE_SIZE; + + /* reset unmarshaller */ + _read_offset = RPC_OBJECT_ID_SIZE; +} + + +void Ipc_server::_wait() +{ + /* receive next request */ + copy_utcb_to_msgbuf(_rcv_msg, Kernel::wait_for_request()); + + /* update server state */ + _prepare_next_reply_wait(); +} + + +void Ipc_server::_reply() +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Unexpected call\n"; + while (1) ; +} + + +void Ipc_server::_reply_wait() +{ + /* if there is no reply simply do wait for request */ + /* FIXME this shall be not supported */ + if (!_reply_needed) { + _wait(); + return; + } + /* send reply and receive next request */ + copy_msgbuf_to_utcb(_snd_msg, _write_offset, + Ipc_ostream::_dst.local_name()); + copy_utcb_to_msgbuf(_rcv_msg, Kernel::reply_and_wait(_write_offset)); + + /* update server state */ + _prepare_next_reply_wait(); +} + diff --git a/base-hw/src/base/lock/lock_helper.h b/base-hw/src/base/lock/lock_helper.h new file mode 100644 index 0000000000..d56d6e349e --- /dev/null +++ b/base-hw/src/base/lock/lock_helper.h @@ -0,0 +1,58 @@ +/* + * \brief Helper functions for the Lock implementation + * \author Martin Stein + * \date 2011-01-02 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * 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__BASE__LOCK__LOCK_HELPER_H_ +#define _SRC__BASE__LOCK__LOCK_HELPER_H_ + +/* Genode includes */ +#include + + +/** + * Yield CPU to any other thread + */ +static inline void thread_yield() +{ Kernel::yield_thread(); } + + +/** + * Yield CPU to a specified thread 't' + */ +static inline void +thread_switch_to(Genode::Native_thread_id const t) +{ Kernel::yield_thread(t); } + + +/** + * Resume another thread 't' and return if it were paused or not + */ +static inline bool +thread_check_stopped_and_restart(Genode::Native_thread_id const t) +{ return Kernel::resume_thread(t) == 0; } + + +/** + * Validation kernel thread-identifier 'id' + */ +static inline bool thread_id_valid(Genode::Native_thread_id const id) +{ return id != Genode::thread_invalid_id(); } + + +/** + * Exclude ourselves from CPU scheduling for now + */ +static inline void thread_stop_myself() { Kernel::pause_thread(); } + + +#endif /* _SRC__BASE__LOCK__LOCK_HELPER_H_ */ + diff --git a/base-hw/src/base/pager.cc b/base-hw/src/base/pager.cc new file mode 100644 index 0000000000..d8df0347ac --- /dev/null +++ b/base-hw/src/base/pager.cc @@ -0,0 +1,127 @@ +/* + * \brief Pager implementations that are specific for the HW-core + * \author Martin Stein + * \date 2012-03-29 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + + +/*************************** + ** Pager_activation_base ** + ***************************/ + +void Pager_activation_base::entry() +{ + /* acknowledge that we're ready to work */ + Ipc_pager pager; + _cap = pager; + _cap_valid.unlock(); + + /* wait for the first pagefault */ + pager.wait_for_fault(); + while (1) + { + /* lookup pager object for the current faulter */ + Pager_object * o = _ep ? _ep->obj_by_id(pager.badge()) : 0; + if (!o) { + PERR("%s:%d: Invalid pager object", __FILE__, __LINE__); + while (1) ; + } + /* let pager handle the pagefault, apply mapping, await pagefault */ + if (o->pager(pager)) pager.wait_for_fault(); + else pager.resolve_and_wait_for_fault(); + } +} + + +/********************** + ** Pager_entrypoint ** + **********************/ + +Pager_entrypoint::Pager_entrypoint(Cap_session *, + Pager_activation_base * const a) +: _activation(a) { _activation->ep(this); } + + +void Pager_entrypoint::dissolve(Pager_object * const o) { remove(o); } + + +Pager_capability Pager_entrypoint::manage(Pager_object * const o) +{ + /* do we have an activation */ + if (!_activation) return Pager_capability(); + + /* create cap with the object badge as local name */ + Native_capability c; + c = Native_capability(_activation->cap().dst(), o->badge()); + + /* let activation provide the pager object */ + o->cap(c); + insert(o); + + /* return pager-object cap */ + return reinterpret_cap_cast(c); +} + + +/*************** + ** Ipc_pager ** + ***************/ + + +void Ipc_pager::wait_for_fault() +{ + /* receive first message */ + size_t s = Kernel::wait_for_request(); + while (1) { + switch (s) { + + case sizeof(Pagefault): { + + /* message is a pagefault */ + Native_utcb * const utcb = Thread_base::myself()->utcb(); + Pagefault * const pf = (Pagefault *)utcb; + if (pf->valid()) + { + /* give our caller the chance to handle the fault */ + _pagefault = *pf; + return; + } + /* pagefault is invalid so get the next message */ + else { + PERR("%s:%d: Invalid pagefault", __FILE__, __LINE__); + continue; + } + continue; } + + case sizeof(Pagefault_resolved): { + + /* message is a release request from a RM session */ + Native_utcb * const utcb = Thread_base::myself()->utcb(); + Pagefault_resolved * const msg = (Pagefault_resolved *)utcb; + + /* resume faulter, send ack to RM and get the next message */ + Kernel::resume_thread(msg->pager_object->badge()); + s = Kernel::reply_and_wait(0); + continue; } + + default: { + + PERR("%s:%d: Invalid message format", __FILE__, __LINE__); + continue; } + } + } +} + diff --git a/base-hw/src/base/signal/signal.cc b/base-hw/src/base/signal/signal.cc new file mode 100644 index 0000000000..f1df666b6f --- /dev/null +++ b/base-hw/src/base/signal/signal.cc @@ -0,0 +1,130 @@ +/* + * \brief Implementations of the signaling framework specific for HW-core + * \author Martin Stein + * \date 2012-05-05 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + + +/** + * Provide a static signal connection + */ +static Signal_connection * signal_connection() +{ + static Signal_connection _object; + return &_object; +} + + +/********************* + ** Signal_receiver ** + *********************/ + + +Signal_receiver::Signal_receiver() : + _cap(signal_connection()->alloc_receiver()) +{ + if (!_cap.valid()) { + PERR("%s: Failed to create receiver", __PRETTY_FUNCTION__); + while (1) ; + } +} + + +Signal_receiver::~Signal_receiver() +{ + /* dissolve all contexts that are managed by us */ + Lock::Guard contexts_guard(_contexts_lock); + while (1) { + Signal_context * const c = _contexts.first(); + if (!c) break; + Lock::Guard context_guard(c->_lock); + _unsync_dissolve(c); + } +} + + +Signal_context_capability Signal_receiver::manage(Signal_context * const c) +{ + /* check if the context is already managed */ + Lock::Guard contexts_guard(_contexts_lock); + Lock::Guard context_guard(c->_lock); + if (c->_receiver) throw Context_already_in_use(); + + /* create a kernel object that corresponds to the context */ + bool session_upgraded = 0; + Signal_connection * const s = signal_connection(); + while (1) { + try { + c->_cap = s->alloc_context(_cap, (unsigned long)c); + break; + } catch (Signal_session::Out_of_metadata) + { + /* upgrade session quota and try again, but only once */ + if (session_upgraded) return Signal_context_capability(); + env()->parent()->upgrade(s->cap(), "ram_quota=4K"); + session_upgraded = 1; + } + } + /* assign the context to us */ + c->_receiver = this; + _contexts.insert(c); + return c->_cap; +} + + +Signal Signal_receiver::wait_for_signal() +{ + /* await a signal */ + Kernel::await_signal(_cap.dst()); + Signal s = *(Signal *)Thread_base::myself()->utcb(); + Signal_context * c = s.context(); + + /* check if the context of the signal is managed by us */ + Lock::Guard context_guard(c->_lock); + if (c->_receiver != this) { + PERR("%s: Context not managed by this receiver", __PRETTY_FUNCTION__); + while (1) ; + } + /* check attributes of the signal and return it */ + if (s.num() == 0) PWRN("Returning signal with num == 0"); + return s; +} + + +void Signal_receiver::dissolve(Signal_context * const c) +{ + /* check if the context is managed by us */ + Lock::Guard contexts_guard(_contexts_lock); + Lock::Guard context_guard(c->_lock); + if (c->_receiver != this) throw Context_not_associated(); + + /* unassign the context */ + _unsync_dissolve(c); +} + + +void Signal_receiver::_unsync_dissolve(Signal_context * const c) +{ + /* reset the context */ + c->_receiver = 0; + c->_cap = Signal_context_capability(); + + /* forget the context */ + _contexts.remove(c); +} + diff --git a/base-hw/src/base/thread_support.cc b/base-hw/src/base/thread_support.cc new file mode 100644 index 0000000000..2d11b8ffc3 --- /dev/null +++ b/base-hw/src/base/thread_support.cc @@ -0,0 +1,91 @@ +/** + * \brief Platform specific parts of the thread API + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + +extern Native_utcb * _main_utcb; + +namespace Genode { Rm_session *env_context_area_rm_session(); } + + +/***************** + ** Thread_base ** + *****************/ + +Native_utcb * Thread_base::utcb() +{ + /* this is a main thread, so CRT0 provides UTCB through '_main_utcb' */ + if (!this) return _main_utcb; + + /* otherwise we have a valid thread base */ + return &_context->utcb; +} + + +void Thread_base::_thread_start() +{ + Thread_base::myself()->_thread_bootstrap(); + Thread_base::myself()->entry(); + Genode::sleep_forever(); +} + + +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)); + Cpu_session * cpu = env()->cpu_session(); + _thread_cap = cpu->create_thread(buf, (addr_t)&_context->utcb); + + /* 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); + + /* attach UTCB */ + try { + Ram_dataspace_capability ds = env()->cpu_session()->utcb(_thread_cap); + size_t const size = sizeof(_context->utcb); + addr_t dst = Context_allocator::addr_to_base(_context) + + CONTEXT_VIRTUAL_SIZE - size - CONTEXT_AREA_VIRTUAL_BASE; + env_context_area_rm_session()->attach_at(ds, dst, size); + } catch (...) { + PERR("%s: Failed to attach UTCB", __PRETTY_FUNCTION__); + sleep_forever(); + } + /* start thread with its initial IP and aligned SP */ + addr_t thread_sp = (addr_t)&_context->stack[-4]; + thread_sp &= ~0xf; + 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-hw/src/core/arm_v7a/boot_modules.s b/base-hw/src/core/arm_v7a/boot_modules.s new file mode 100644 index 0000000000..61dbde6efa --- /dev/null +++ b/base-hw/src/core/arm_v7a/boot_modules.s @@ -0,0 +1,33 @@ +/* + * \brief Dummy version of a boot modules file to enable a 'core' standalone image + * \author Martin Stein + * \date 2011-12-16 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +.section .data + +.align 3 +.global _boot_modules_begin +_boot_modules_begin: +.string "GROM" + +.align 3 +.global _boot_module_headers_begin +_boot_module_headers_begin: + +/* no module headers */ + +.global _boot_module_headers_end +_boot_module_headers_end: + +/* no modules */ + +.global _boot_modules_end +_boot_modules_end: diff --git a/base-hw/src/core/arm_v7a/crt0.s b/base-hw/src/core/arm_v7a/crt0.s new file mode 100644 index 0000000000..1d0cb22616 --- /dev/null +++ b/base-hw/src/core/arm_v7a/crt0.s @@ -0,0 +1,72 @@ +/* + * \brief Startup code for the Genode Kernel on ARM + * \author Martin Stein + * \author Stefan Kalkowski + * \date 2011-10-01 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +.section .text + + /* ELF entry symbol */ + .global _start + _start: + + /* idle a little initially because 'u-boot' likes it this way */ + .rept 8 + nop + .endr + + /* zero-fill BSS segment */ + .extern _bss_start + .extern _bss_end + ldr r0, =_bss_start + ldr r1, =_bss_end + mov r2, #0 + sub r1, r1, #4 + 1: + str r2, [r0] + add r0, r0, #4 + cmp r0, r1 + bne 1b + + /* call kernel routine */ + .extern kernel + _start_kernel: + ldr sp, =_kernel_stack_high + bl kernel + + /* jump to code that kernel has designated for when he has returned */ + ldr r1, =_call_after_kernel + ldr r1, [r1] + add pc, r1, #0 + + /* handle for dynamic symbol objects */ + .align 3 + .global __dso_handle + __dso_handle: .long 0 + +.section .bss + + /* instruction pointer wich gets loaded when kernel returns */ + .align 3 + .global _call_after_kernel + _call_after_kernel: .long 0 + + /* kernel stack */ + .align 3 + .space 64*1024 + .global _kernel_stack_high + _kernel_stack_high: + + /* main thread UTCB pointer for the Genode thread API */ + .align 3 + .global _main_utcb + _main_utcb: .long 0 + diff --git a/base-hw/src/core/arm_v7a/mode_transition.s b/base-hw/src/core/arm_v7a/mode_transition.s new file mode 100644 index 0000000000..12fda28a75 --- /dev/null +++ b/base-hw/src/core/arm_v7a/mode_transition.s @@ -0,0 +1,205 @@ +/* + * \brief Transition between kernel and userland + * \author Martin stein + * \date 2011-11-15 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/** + * Invalidate all entries of the branch predictor array + */ +.macro _flush_branch_predictor + mcr p15, 0, sp, c7, c5, 6 + isb +.endm + +/** + * Switch from an interrupted user context to a kernel context + * + * \param exception_type immediate exception type ID + * \param pc_adjust immediate value that gets subtracted from the + * user PC before it gets saved + */ +.macro _user_to_kernel_pic exception_type, pc_adjust + + /* + * We expect that privileged modes are never interrupted by an + * exception. Thus we can assume that we always come from + * user mode at this point. + */ + + /************************************************ + ** We're still in the user protection domain, ** + ** so we must avoid access to kernel memory ** + ************************************************/ + + /* load kernel contextidr */ + adr sp, _mt_kernel_context_begin + ldr sp, [sp, #17*4] + mcr p15, 0, sp, c13, c0, 1 + _flush_branch_predictor + + /* load kernel section table */ + adr sp, _mt_kernel_context_begin + ldr sp, [sp, #19*4] + mcr p15, 0, sp, c2, c0, 0 + _flush_branch_predictor + + /******************************************* + ** Now it's save to access kernel memory ** + *******************************************/ + + /* get user context pointer */ + ldr sp, _mt_user_context_ptr + + /* + * Save user r0 ... r12. We explicitely target user registers + * via '^' because we might be in FIQ exception-mode where + * some of them are banked. Doesn't affect other modes. + */ + stmia sp, {r0-r12}^ + + /* save user lr and sp */ + add r0, sp, #13*4 + stmia r0, {sp,lr}^ + + /* adjust and save user pc */ + .if \pc_adjust != 0 + sub lr, lr, #\pc_adjust + .endif + str lr, [sp, #15*4] + + /* save user psr */ + mrs r0, spsr + str r0, [sp, #16*4] + + /* save type of exception that interrupted the user */ + mov r0, #\exception_type + str r0, [sp, #18*4] + + /* + * Switch to supervisor mode + * FIXME This is done due to incorrect behavior when running the kernel + * high-level-code in FIQ-exception mode. Please debug this behavior + * and remove this switch. + */ + cps #19 + + /* get kernel context pointer */ + adr r0, _mt_kernel_context_begin + + /* load kernel context */ + add r0, r0, #13*4 + ldmia r0, {sp, lr, pc} + +.endm + + +.section .text + + /* + * The mode transition PIC switches between a kernel context and a user + * context and thereby between their address spaces. Due to the latter + * it must be mapped executable to the same region in every address space. + * To enable such switching, the kernel context must be stored within this + * region, thus one should map it solely accessable for privileged modes. + */ + .align 3 + .global _mode_transition_begin + _mode_transition_begin: + + /* + * On user exceptions the CPU has to jump to one of the following + * 7 entry vectors to switch to a kernel context. + */ + .align 3 + .global _mt_kernel_entry_pic + _mt_kernel_entry_pic: + + b _rst_entry /* reset */ + b _und_entry /* undefined instruction */ + b _svc_entry /* supervisor call */ + b _pab_entry /* prefetch abort */ + b _dab_entry /* data abort */ + nop /* reserved */ + b _irq_entry /* interrupt request */ + b _fiq_entry /* fast interrupt request */ + + /* PICs that switch from an user exception to the kernel */ + _rst_entry: _user_to_kernel_pic 1, 0 + _und_entry: _user_to_kernel_pic 2, 4 + _svc_entry: _user_to_kernel_pic 3, 0 + _pab_entry: _user_to_kernel_pic 4, 4 + _dab_entry: _user_to_kernel_pic 5, 8 + _irq_entry: _user_to_kernel_pic 6, 4 + _fiq_entry: _user_to_kernel_pic 7, 4 + + /* kernel must jump to this point to switch to a user context */ + .align 3 + .global _mt_user_entry_pic + _mt_user_entry_pic: + + /* get user context pointer */ + ldr lr, _mt_user_context_ptr + + /* buffer user pc */ + ldr r0, [lr, #15*4] + adr r1, _mt_buffer + str r0, [r1] + + /* buffer user psr */ + ldr r0, [lr, #16*4] + msr spsr, r0 + + /* load user r0 ... r12 */ + ldmia lr, {r0-r12} + + /* load user sp and lr */ + add sp, lr, #13*4 + ldmia sp, {sp,lr}^ + + /* get user contextidr and section table */ + ldr sp, [lr, #17*4] + ldr lr, [lr, #19*4] + + /******************************************************** + ** From now on, until we leave kernel mode, we must ** + ** avoid access to memory that is not mapped globally ** + ********************************************************/ + + /* apply user contextidr and section table */ + mcr p15, 0, sp, c13, c0, 1 + mcr p15, 0, lr, c2, c0, 0 + _flush_branch_predictor + + /* load user pc (implies application of the user psr) */ + adr lr, _mt_buffer + ldmia lr, {pc}^ + + /* leave some space for the kernel context */ + .align 3 + .global _mt_kernel_context_begin + _mt_kernel_context_begin: .space 32*4 + .global _mt_kernel_context_end + _mt_kernel_context_end: + + /* pointer to the user context backup space */ + .align 3 + .global _mt_user_context_ptr + _mt_user_context_ptr: .long 0 + + /* a local word-sized buffer */ + .align 3 + .global _mt_buffer + _mt_buffer: .long 0 + + .align 3 + .global _mode_transition_end + _mode_transition_end: + diff --git a/base-hw/src/core/cpu_session_support.cc b/base-hw/src/core/cpu_session_support.cc new file mode 100644 index 0000000000..2535895648 --- /dev/null +++ b/base-hw/src/core/cpu_session_support.cc @@ -0,0 +1,34 @@ +/* + * \brief Platform specific parts of CPU session + * \author Martin Stein + * \date 2012-04-17 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + + +Ram_dataspace_capability +Cpu_session_component::utcb(Thread_capability thread_cap) +{ + /* serialize access */ + Lock::Guard lock_guard(_thread_list_lock); + + /* lookup requested UTCB dataspace */ + Cpu_thread_component * t = _lookup_thread(thread_cap); + if (!t) return Ram_dataspace_capability(); + return t->platform_thread()->utcb(); +} + diff --git a/base-hw/src/core/include/assert.h b/base-hw/src/core/include/assert.h new file mode 100644 index 0000000000..f1f3ea1dbf --- /dev/null +++ b/base-hw/src/core/include/assert.h @@ -0,0 +1,36 @@ +/* + * \brief Assertion macro + * \author Martin Stein + * \date 2012-04-04 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__ASSERT_H_ +#define _CORE__INCLUDE__ASSERT_H_ + +/* Genode includes */ +#include + +/** + * Assert a condition + * + * \param expression Expression that must be true + */ +#define assert(expression) \ + do { \ + if (!(expression)) { \ + PERR("Assertion failed: "#expression""); \ + PERR(" File: %s:%d", __FILE__, __LINE__); \ + PERR(" Function: %s", __PRETTY_FUNCTION__); \ + while (1) ; \ + } \ + } while (0) ; + +#endif /* _CORE__INCLUDE__ASSERT_H_ */ + diff --git a/base-hw/src/core/include/cortex_a9/kernel_support.h b/base-hw/src/core/include/cortex_a9/kernel_support.h new file mode 100644 index 0000000000..7b7ea766a9 --- /dev/null +++ b/base-hw/src/core/include/cortex_a9/kernel_support.h @@ -0,0 +1,54 @@ +/* + * \brief Parts of kernel support that are identical for all Cortex A9 systems + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORTEX_A9__KERNEL_SUPPORT_H_ +#define _CORE__INCLUDE__CORTEX_A9__KERNEL_SUPPORT_H_ + +/* Genode includes */ +#include +#include + +/** + * CPU driver + */ +class Cpu : public Genode::Cortex_a9 { }; + +namespace Kernel +{ + /* import Genode types */ + typedef Genode::Cortex_a9 Cortex_a9; + typedef Genode::Pl390_base Pl390_base; + + /** + * Kernel interrupt-controller + */ + class Pic : public Pl390_base + { + public: + + /** + * Constructor + */ + Pic() : Pl390_base(Cortex_a9::PL390_DISTRIBUTOR_MMIO_BASE, + Cortex_a9::PL390_CPU_MMIO_BASE) + { } + }; + + /** + * Kernel timer + */ + class Timer : public Cortex_a9::Private_timer { }; +} + +#endif /* _CORE__INCLUDE__CORTEX_A9__KERNEL_SUPPORT_H_ */ + diff --git a/base-hw/src/core/include/cpu_thread_allocator.h b/base-hw/src/core/include/cpu_thread_allocator.h new file mode 100644 index 0000000000..a8ff5b79af --- /dev/null +++ b/base-hw/src/core/include/cpu_thread_allocator.h @@ -0,0 +1,68 @@ +/* + * \brief Platform specific parts of the core CPU session + * \author Martin Stein + * \date 2012-03-21 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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_SUPPORT_H_ +#define _CORE__INCLUDE__CPU_SESSION_SUPPORT_H_ + +#include +#include + +namespace Genode +{ + /** + * Thread allocator for cores CPU service + * + * Normally one would use a SLAB for threads because usually they + * are tiny objects, but in 'base-hw' they contain the whole kernel + * object in addition. Thus we use the given allocator directly. + */ + class Cpu_thread_allocator : public Allocator + { + Allocator * const _alloc; + + public: + + /** + * Constructor + * + * \param alloc allocator backend + */ + Cpu_thread_allocator(Allocator * alloc) : _alloc(alloc) { } + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t size, void **out_addr) { + return _alloc->alloc(size, out_addr); } + + void free(void *addr, size_t size) { + _alloc->free(addr, size); } + + size_t consumed() { + PDBG("Unexprected call"); + while (1) ; + return 0; + } + + size_t overhead(size_t size) { + PDBG("Unexprected call"); + while (1) ; + return 0; + } + + bool need_size_for_free() const { return true; } + }; +} + +#endif /* _CORE__INCLUDE__CPU_SESSION_SUPPORT_H_ */ diff --git a/base-hw/src/core/include/platform.h b/base-hw/src/core/include/platform.h new file mode 100644 index 0000000000..79b6d6006c --- /dev/null +++ b/base-hw/src/core/include/platform.h @@ -0,0 +1,111 @@ +/* + * \brief Platform interface + * \author Martin Stein + * \date 2011-12-21 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file 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 +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { + + /** + * Manages all platform ressources + */ + class Platform : public Platform_generic + { + typedef Synchronized_range_allocator Phys_allocator; + + 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 */ + + addr_t _vm_base; /* base of virtual address space */ + size_t _vm_size; /* size of virtual address space */ + + /** + * Get one of the consecutively numbered available resource regions + * + * \return >0 region pointer if region with index 'i' exists + * 0 if region with index 'i' doesn't exist + * + * These functions should provide all ressources that are available + * on the current platform. + */ + static Native_region * _ram_regions(unsigned i); + static Native_region * _mmio_regions(unsigned i); + static Native_region * _irq_regions(unsigned i); + + /** + * Get one of the consecutively numbered core regions + * + * \return >0 Region pointer if region with index 'i' exists + * 0 If region with index 'i' doesn't exist + * + * Core regions are address regions that must be permitted to + * core only, such as the core image ROM. These regions are normally + * a subset of the ressource regions provided above. + */ + static Native_region * _core_only_ram_regions(unsigned i); + static Native_region * _core_only_mmio_regions(unsigned i); + static Native_region * _core_only_irq_regions(unsigned i); + + public: + + /** + * Constructor + */ + Platform(); + + + /******************************** + ** Platform_generic interface ** + ********************************/ + + inline Range_allocator * core_mem_alloc() { return &_core_mem_alloc; } + + 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 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() { while (1) Kernel::pause_thread(); }; + + inline Range_allocator * region_alloc() + { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + return 0; + } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_H_ */ + diff --git a/base-hw/src/core/include/platform_pd.h b/base-hw/src/core/include/platform_pd.h new file mode 100644 index 0000000000..b2cde113ac --- /dev/null +++ b/base-hw/src/core/include/platform_pd.h @@ -0,0 +1,105 @@ +/* + * \brief Platform specific part of a Genode protection domain + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2009-2012 Genode Labs GmbH + * + * This file 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_ + +/* Genode includes */ +#include + +/* Core includes */ +#include +#include + +namespace Kernel +{ + Genode::size_t pd_size(); + unsigned pd_alignm_log2(); +} + +namespace Genode +{ + class Platform_thread; + + /** + * Platform specific part of a Genode protection domain + */ + class Platform_pd + { + unsigned long _id; /* ID of our kernel object */ + Native_capability _parent; /* our parent interface */ + Native_thread_id _main_thread; /* the first thread that gets + * executed in this PD */ + + public: + + /** + * Constructor + */ + Platform_pd() : _main_thread(0) + { + /* get some aligned space for the kernel object */ + void * kernel_pd; + Range_allocator * ram = platform()->ram_alloc(); + assert(ram->alloc_aligned(Kernel::pd_size(), &kernel_pd, + Kernel::pd_alignm_log2())); + + /* create kernel object */ + _id = Kernel::new_pd(kernel_pd); + assert(_id); + } + + /** + * Destructor + */ + ~Platform_pd(); + + /** + * Bind thread 't' to protection domain + * + * \return 0 on success or + * -1 if failed + */ + int bind_thread(Platform_thread * t) + { + /* is this the first and therefore main thread in this PD? */ + if (!_main_thread) + { + /* annotate that we've got a main thread from now on */ + _main_thread = t->id(); + return t->join_pd(_id, 1); + } + return t->join_pd(_id, 0); + } + + /** + * Unbind thread from protection domain + * + * Free the thread's slot and update thread object. + */ + void unbind_thread(Platform_thread * t); + + /** + * Assign parent interface to protection domain + */ + int assign_parent(Native_capability parent) + { + assert(parent.valid()); + _parent = parent; + return 0; + } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */ + diff --git a/base-hw/src/core/include/platform_thread.h b/base-hw/src/core/include/platform_thread.h new file mode 100644 index 0000000000..f056e4bcf1 --- /dev/null +++ b/base-hw/src/core/include/platform_thread.h @@ -0,0 +1,184 @@ +/* + * \brief Userland interface for the management of kernel thread-objects + * \author Martin Stein + * \date 2012-02-02 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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 + +/* core includes */ +#include + +namespace Genode { + + class Pager_object; + class Thread_state; + class Thread_base; + class Rm_client; + class Platform_thread; + + size_t kernel_thread_size(); + + /** + * Userland interface for the management of kernel thread-objects + */ + class Platform_thread + { + enum { NAME_MAX_LEN = 32 }; + + Thread_base * _thread_base; + unsigned long _stack_size; + unsigned long _pd_id; + unsigned long _id; + Rm_client * _rm_client; + bool _main_thread; + Native_utcb * _phys_utcb; + Native_utcb * _virt_utcb; + Software_tlb * _software_tlb; + Ram_dataspace_capability _utcb; + char _name[NAME_MAX_LEN]; + + /** + * Common construction part + */ + void _init(); + + public: + + /** + * Constructor for core threads + */ + Platform_thread(const char * name, + Thread_base * const thread_base, + unsigned long const stack_size, + unsigned long const pd_id); + + /** + * Constructor for threads outside of core + */ + Platform_thread(const char * name, unsigned int priority, + addr_t utcb); + + /** + * Join PD identified by 'pd_id' + * + * \param pd_id ID of targeted PD + * \param main_thread wether we are the main thread in this PD + * + * \retval 0 on success + * \retval <0 otherwise + */ + int join_pd(unsigned long const pd_id, + bool const main_thread); + + /** + * Run this thread + */ + int start(void * ip, void * sp, unsigned int cpu_no = 0); + + /** + * Pause this thread + */ + void pause() { Kernel::pause_thread(_id); } + + /** + * Resume this thread + */ + void resume() { Kernel::resume_thread(_id); } + + /** + * Cancel currently blocking operation + */ + void cancel_blocking() + { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + }; + + /** + * Request our raw thread state + * + * \param state_dst destination state buffer + * + * \retval 0 successful + * \retval -1 thread state not accessible + */ + int state(Genode::Thread_state * state_dst) + { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + return -1; + }; + + /** + * Destructor + */ + ~Platform_thread() + { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + } + + /** + * Return unique identification of this thread as faulter + */ + unsigned long pager_object_badge() { return _id; } + + /** + * Set the executing CPU for this thread. + */ + void set_cpu(unsigned int cpu_no) + { + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + }; + + + /*************** + ** Accessors ** + ***************/ + + inline char const * name() const { return _name; } + + void pager(Pager_object * const pager); + + Pager_object * pager() const; + + unsigned long pd_id() const { return _pd_id; } + + Native_thread_id id() const { return _id; } + + unsigned long stack_size() const { return _stack_size; } + + Thread_base * thread_base() + { + if (!_thread_base) assert(_main_thread); + return _thread_base; + } + + Native_utcb * phys_utcb() const { return _phys_utcb; } + + Native_utcb * virt_utcb() const { return _virt_utcb; } + + Ram_dataspace_capability utcb() const { return _utcb; } + + Software_tlb * software_tlb() const { return _software_tlb; } + }; +} + +#endif /* _CORE__INCLUDE__PLATFORM_THREAD_H_ */ + diff --git a/base-hw/src/core/include/signal_root.h b/base-hw/src/core/include/signal_root.h new file mode 100644 index 0000000000..cecfa01492 --- /dev/null +++ b/base-hw/src/core/include/signal_root.h @@ -0,0 +1,93 @@ +/* + * \brief Signal root interface on HW-core + * \author Martin Stein + * \date 2012-05-06 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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 +{ + /** + * Provide EP to signal root before it initialises root component + */ + class Signal_handler + { + enum { STACK_SIZE = 4096 }; + + Rpc_entrypoint _entrypoint; + + public: + + /** + * Constructor + */ + Signal_handler(Cap_session * const c) + : _entrypoint(c, STACK_SIZE, "signal") { } + + /*************** + ** Accessors ** + ***************/ + + Rpc_entrypoint * entrypoint() { return &_entrypoint; } + }; + + /** + * Provides signal service by managing appropriate sessions to the clients + */ + class Signal_root : private Signal_handler, + public Root_component + { + public: + + /** + * Constructor + * + * \param md Meta-data allocator to be used by root component + * \param c CAP session to be used by the root entrypoint + */ + Signal_root(Allocator * const md, Cap_session * const c) : + Signal_handler(c), + Root_component(entrypoint(), md) + { } + + protected: + + /******************************** + ** 'Root_component' interface ** + ********************************/ + + 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(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); + } + }; +} + +#endif /* _CORE__INCLUDE__SIGNAL_ROOT_H_ */ + diff --git a/base-hw/src/core/include/signal_session_component.h b/base-hw/src/core/include/signal_session_component.h new file mode 100644 index 0000000000..6e643f7b84 --- /dev/null +++ b/base-hw/src/core/include/signal_session_component.h @@ -0,0 +1,68 @@ +/* + * \brief Signal service on the HW-core + * \author Martin stein + * \date 2012-05-05 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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_ + +/* Genode includes */ +#include +#include +#include +#include + +namespace Genode +{ + /** + * Provides the signal service + */ + class Signal_session_component : public Rpc_object + { + Allocator_guard _md_alloc; /* Metadata allocator */ + Slab _receivers_slab; /* SLAB to allocate receiver kernel-objects */ + Slab _contexts_slab; /* SLAB to allocate context kernel-objects */ + + public: + + /** + * Constructor + * + * \param md Metadata allocator + * \param ram_quota Amount of RAM quota donated to this session + */ + Signal_session_component(Allocator * const md, + size_t const ram_quota); + + /** + * Destructor + */ + ~Signal_session_component(); + + /** + * Raise the quota of this session by 'q' + */ + void upgrade_ram_quota(size_t const q) { _md_alloc.upgrade(q); } + + /****************************** + ** Signal_session interface ** + ******************************/ + + Signal_receiver_capability alloc_receiver(); + + Signal_context_capability + alloc_context(Signal_receiver_capability const r, + unsigned long const imprint); + }; +} + +#endif /* _CORE__INCLUDE__CAP_SESSION_COMPONENT_H_ */ + diff --git a/base-hw/src/core/include/util.h b/base-hw/src/core/include/util.h new file mode 100644 index 0000000000..42afead283 --- /dev/null +++ b/base-hw/src/core/include/util.h @@ -0,0 +1,109 @@ +/* + * \brief Core-internal utilities + * \author Martin Stein + * \date 2012-01-02 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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 +{ + enum { MIN_PAGE_SIZE_LOG2 = 12 }; + + + /** + * Get the the minimal supported page-size log 2 + */ + inline size_t get_page_size_log2() { return MIN_PAGE_SIZE_LOG2; } + + + /** + * Get the the minimal supported page-size + */ + inline size_t get_page_size() { return 1 << get_page_size_log2(); } + + + /** + * Get the base mask for the minimal supported page-size + */ + inline addr_t get_page_mask() { return ~(get_page_size() - 1); } + + + /** + * Round down to the minimal page-size alignment + */ + inline addr_t trunc_page(addr_t addr) { return addr & get_page_mask(); } + + + /** + * Round up to the minimal page-size alignment + */ + inline addr_t round_page(addr_t addr) + { return trunc_page(addr + get_page_size() - 1); } + + + /** + * Round down to a specific alignment + */ + inline addr_t trunc(addr_t const addr, unsigned const alignm_log2) + { return addr & ~((1 << alignm_log2) - 1); } + + + /** + * Round up to a specific alignment + */ + inline addr_t round(addr_t const addr, unsigned const alignm_log2) + { return trunc(addr + (1< + +/* 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 base; } + diff --git a/base-hw/src/core/io_port_session_component.cc b/base-hw/src/core/io_port_session_component.cc new file mode 100644 index 0000000000..6a9841871b --- /dev/null +++ b/base-hw/src/core/io_port_session_component.cc @@ -0,0 +1,85 @@ +/* + * \brief Implementation of the IO_PORT session interface + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + + +unsigned char Io_port_session_component::inb(unsigned short address) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + return 0; +} + + +unsigned short Io_port_session_component::inw(unsigned short address) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + return 0; +} + + +unsigned Io_port_session_component::inl(unsigned short address) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; + return 0; +} + + +void Io_port_session_component::outb(unsigned short address, + unsigned char value) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + + +void Io_port_session_component::outw(unsigned short address, + unsigned short value) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + + +void Io_port_session_component::outl(unsigned short address, unsigned value) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + + +Io_port_session_component:: +Io_port_session_component(Range_allocator * io_port_alloc, + const char * args) +: _io_port_alloc(io_port_alloc) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + + +Io_port_session_component::~Io_port_session_component() +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + diff --git a/base-hw/src/core/irq_session_component.cc b/base-hw/src/core/irq_session_component.cc new file mode 100644 index 0000000000..1f25f15eba --- /dev/null +++ b/base-hw/src/core/irq_session_component.cc @@ -0,0 +1,69 @@ +/* + * \brief Implementation of IRQ session component + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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) +{ return Kernel::allocate_irq(irq); } + + +void Irq_session_component::wait_for_irq() { Kernel::await_irq(); } + + +Irq_session_component::~Irq_session_component() +{ + /* free IRQ for other threads */ + if (Kernel::free_irq(_irq_number)) + PERR("Could not free IRQ %u", _irq_number); +} + + +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) +{ + /* check arguments */ + bool shared = Arg_string::find_arg(args, "irq_shared").bool_value(false); + if (shared) { + PERR("IRQ sharing not supported"); + throw Root::Invalid_args(); + } + /* allocate 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 %lu requested", irq_number); + throw Root::Invalid_args(); + } + _irq_number = irq_number; + + /* configure control client */ + if (!_control_client.associate_to_irq(irq_number)) { + PERR("IRQ association failed"); + throw Root::Invalid_args(); + } + /* create IRQ capability */ + _irq_cap = Irq_session_capability(_ep.manage(this)); +} diff --git a/base-hw/src/core/kernel.cc b/base-hw/src/core/kernel.cc new file mode 100644 index 0000000000..7babbc34af --- /dev/null +++ b/base-hw/src/core/kernel.cc @@ -0,0 +1,2088 @@ +/* + * \brief Singlethreaded minimalistic kernel + * \author Martin Stein + * \date 2011-10-20 + * + * This kernel is the only code except the mode transition PIC, that runs in + * privileged CPU mode. It has two tasks. First it initializes the process + * 'core', enriches it with the whole identically mapped address range, + * joins and applies it, assigns one thread to it with a userdefined + * entrypoint (the core main thread) and starts this thread in userland. + * Afterwards it is called each time an exception occurs in userland to do + * a minimum of appropriate exception handling. Thus it holds a CPU context + * for itself as for any other thread. But due to the fact that it never + * relies on prior kernel runs this context only holds some constant pointers + * such as SP and IP. + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file 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 + +using namespace Kernel; + +/* get core configuration */ +extern Genode::addr_t _call_after_kernel; +extern Genode::Native_utcb * _main_utcb; +extern int _kernel_stack_high; +extern "C" void CORE_MAIN(); + +/* get structure of mode transition PIC */ +extern int _mode_transition_begin; +extern int _mode_transition_end; +extern int _mt_user_entry_pic; +extern int _mt_kernel_entry_pic; +extern Genode::addr_t _mt_user_context_ptr; +extern Genode::addr_t _mt_kernel_context_begin; +extern Genode::addr_t _mt_kernel_context_end; + +namespace Kernel +{ + /* import Genode types */ + typedef Genode::size_t size_t; + typedef Genode::addr_t addr_t; + typedef Genode::umword_t umword_t; + typedef Genode::Signal Signal; + typedef Genode::Pagefault Pagefault; + typedef Genode::Native_utcb Native_utcb; + typedef Genode::Platform_thread Platform_thread; + template class Fifo : public Genode::Fifo { }; + template class Avl_node : public Genode::Avl_node { }; + template class Avl_tree : public Genode::Avl_tree { }; + + /* kernel configuration */ + enum { + DEFAULT_STACK_SIZE = 1*1024*1024, + USER_TIME_SLICE_MS = 10, + MAX_PDS = 256, + MAX_THREADS = 256, + MAX_SIGNAL_RECEIVERS = 256, + MAX_SIGNAL_CONTEXTS = 256, + }; + + /** + * Double connected list + * + * \param _ENTRY_T list entry type + */ + template + class Double_list + { + private: + + _ENTRY_T * _head; + _ENTRY_T * _tail; + + public: + + /** + * Provide 'Double_list'-entry compliance by inheritance + */ + class Entry + { + friend class Double_list<_ENTRY_T>; + + private: + + _ENTRY_T * _next; + _ENTRY_T * _prev; + Double_list<_ENTRY_T> * _list; + + public: + + /** + * Constructor + */ + Entry() : _next(0), _prev(0), _list(0) { } + + + /*************** + ** Accessors ** + ***************/ + + _ENTRY_T * next() const { return _next; } + + _ENTRY_T * prev() const { return _prev; } + }; + + public: + + /** + * Constructor + * + * Start with an empty list. + */ + Double_list(): _head(0), _tail(0) { } + + /** + * Insert entry from behind into list + */ + void insert_tail(_ENTRY_T * const e) + { + /* avoid leaking lists */ + if (e->Entry::_list) + e->Entry::_list->remove(e); + + /* update new entry */ + e->Entry::_prev = _tail; + e->Entry::_next = 0; + e->Entry::_list = this; + + /* update previous entry or _head */ + if (_tail) _tail->Entry::_next = e; /* List was not empty */ + else _head = e; /* List was empty */ + _tail = e; + } + + /** + * Remove specific entry from list + */ + void remove(_ENTRY_T * const e) + { + /* sanity checks */ + if (!_head || e->Entry::_list != this) return; + + /* update next entry or _tail */ + if (e != _tail) e->Entry::_next->Entry::_prev = e->Entry::_prev; + else _tail = e->Entry::_prev; + + /* update previous entry or _head */ + if (e != _head) e->Entry::_prev->Entry::_next = e->Entry::_next; + else _head = e->Entry::_next; + + /* update removed entry */ + e->Entry::_list = 0; + } + + /** + * Remove head from list and return it + */ + _ENTRY_T * remove_head() + { + /* sanity checks */ + if (!_head) return 0; + + /* update _head */ + _ENTRY_T * const e = _head; + _head = e->Entry::_next; + + /* update next entry or _tail */ + if (_head) _head->Entry::_prev = 0; + else _tail = 0; + + /* update removed entry */ + e->Entry::_list = 0; + return e; + } + + /** + * Remove head from list and insert it at the end + */ + void head_to_tail() + { + /* sanity checks */ + if (!_head || _head == _tail) return; + + /* remove entry */ + _ENTRY_T * const e = _head; + _head = _head->Entry::_next; + e->Entry::_next = 0; + _head->Entry::_prev = 0; + + /* insert entry */ + _tail->Entry::_next = e; + e->Entry::_prev = _tail; + _tail = e; + } + + + /*************** + ** Accessors ** + ***************/ + + _ENTRY_T * head() const { return _head; } + + _ENTRY_T * tail() const { return _tail; } + }; + + /** + * Map unique sortable IDs to object pointers + * + * \param OBJECT_T object type that should be inherited + * from 'Object_pool::Entry' + */ + template + class Object_pool + { + typedef _OBJECT_T Object; + + public: + + enum { INVALID_ID = 0 }; + + /** + * Provide 'Object_pool'-entry compliance by inheritance + */ + class Entry : public Avl_node + { + protected: + + unsigned long _id; + + public: + + /** + * Constructors + */ + Entry(unsigned long const id) : _id(id) { } + + /** + * Find entry with 'object_id' within this AVL subtree + */ + Entry * find(unsigned long const object_id) + { + if (object_id == id()) return this; + Entry * const subtree = child(object_id > id()); + return subtree ? subtree->find(object_id) : 0; + } + + /** + * ID of this object + */ + unsigned long const id() const { return _id; } + + + /************************ + * 'Avl_node' interface * + ************************/ + + bool higher(Entry *e) { return e->id() > id(); } + }; + + private: + + Avl_tree _tree; + + public: + + /** + * Add 'object' to pool + */ + void insert(Object * const object) { _tree.insert(object); } + + /** + * Remove 'object' from pool + */ + void remove(Object * const object) { _tree.remove(object); } + + /** + * Lookup object + */ + Object * object(unsigned long const id) + { + Entry * object = _tree.first(); + return (Object *)(object ? object->find(id) : 0); + } + }; + + + /** + * Copy 'size' successive bytes from 'src_base' to 'dst_base' + */ + inline void copy_range(void * const src_base, void * const dst_base, + size_t size) + { + for (unsigned long off = 0; off < size; off += sizeof(umword_t)) + *(umword_t *)((addr_t)dst_base + off) = + *(umword_t *)((addr_t)src_base + off); + } + + + /** + * Sends requests to other IPC nodes, accumulates request announcments, + * provides serial access to them and replies to them if expected. + * Synchronizes communication. + * + * IPC node states: + * + * +----------+ +---------------+ +---------------+ + * --new-->| inactive |--send-request-await-reply---->| await reply | +--send-note--| prepare reply | + * | |<--receive-reply---------------| | | | | + * | | +---------------+ +------------>| | + * | |<--request-is-a-note-------+---request-is-not-a-note------------------------>| | + * | | | +---------------+ | | + * | |--await-request------------+-->| await request |<--send-reply-await-request--| | + * | |--send-reply-await-request-+-->| |--announce-request-+-------->| | + * | |--send-note--+ | +---------------+ | | | + * | | | request|available | | | + * | |<------------+ | | | | + * | |<--request-is-a-note-------+---request-is-not-a-note---------------|-------->| | + * | |<--request-is-a-note-----------------------------------------------+ | | + * +----------+ +-------------------------+ | | + * | prepare and await reply |<--send-request-and-await-reply--| | + * | |--receive-reply----------------->| | + * +-------------------------+ +---------------+ + * + * State model propagated to deriving classes: + * + * +--------------+ +----------------+ + * --new-->| has received |--send-request-await-reply---->| awaits receipt | + * | |--await-request------------+-->| | + * | | | | | + * | |<--request-available-------+ | | + * | |--send-reply-await-request-+-->| | + * | |--send-note--+ | | | + * | | | | | | + * | |<------------+ | | | + * | |<--request-available-------+ | | + * | |<--announce-request------------| | + * | |<--receive-reply---------------| | + * +--------------+ +----------------+ + */ + class Ipc_node + { + /** + * IPC node states as depicted initially + */ + enum State + { + INACTIVE = 1, + AWAIT_REPLY = 2, + AWAIT_REQUEST = 3, + PREPARE_REPLY = 4, + PREPARE_AND_AWAIT_REPLY = 5, + }; + + /** + * Describes the buffer for incoming or outgoing messages + */ + struct Message_buf : public Fifo::Element + { + void * base; + size_t size; + Ipc_node * origin; + }; + + Fifo _request_queue; /* requests that waits to be + * received by us */ + Message_buf _inbuf; /* buffers message we have received lastly */ + Message_buf _outbuf; /* buffers the message we aim to send */ + State _state; /* current node state */ + + /** + * Buffer next request from request queue in 'r' to handle it + */ + inline void _receive_request(Message_buf * const r) + { + /* assertions */ + assert(r->size <= _inbuf.size); + + /* fetch message */ + copy_range(r->base, _inbuf.base, r->size); + _inbuf.size = r->size; + _inbuf.origin = r->origin; + + /* update state */ + _state = r->origin->_awaits_reply() ? PREPARE_REPLY : + INACTIVE; + } + + /** + * Receive a given reply if one is expected + * + * \param base base of the reply payload + * \param size size of the reply payload + */ + inline void _receive_reply(void * const base, size_t const size) + { + /* assertions */ + assert(_awaits_reply()); + assert(size <= _inbuf.size); + + /* receive reply */ + copy_range(base, _inbuf.base, size); + _inbuf.size = size; + + /* update state */ + if (_state != PREPARE_AND_AWAIT_REPLY) _state = INACTIVE; + else _state = PREPARE_REPLY; + _has_received(_inbuf.size); + } + + /** + * Insert 'r' into request queue, buffer it if we were waiting for it + */ + inline void _announce_request(Message_buf * const r) + { + /* directly receive request if we've awaited it */ + if (_state == AWAIT_REQUEST) { + _receive_request(r); + _has_received(_inbuf.size); + return; + } + /* cannot receive yet, so queue request */ + _request_queue.enqueue(r); + } + + /** + * Wether we expect to receive a reply message + */ + bool _awaits_reply() + { + return _state == AWAIT_REPLY || + _state == PREPARE_AND_AWAIT_REPLY; + } + + public: + + /** + * Construct an initially inactive IPC node + */ + inline Ipc_node() : _state(INACTIVE) + { + _inbuf.size = 0; + _outbuf.size = 0; + } + + /** + * Destructor + */ + virtual ~Ipc_node() { } + + /** + * Send a request and wait for the according reply + * + * \param dest targeted IPC node + * \param req_base base of the request payload + * \param req_size size of the request payload + * \param inbuf_base base of the reply buffer + * \param inbuf_size size of the reply buffer + */ + inline void send_request_await_reply(Ipc_node * const dest, + void * const req_base, + size_t const req_size, + void * const inbuf_base, + size_t const inbuf_size) + { + /* assertions */ + assert(_state == INACTIVE || _state == PREPARE_REPLY); + + /* prepare transmission of request message */ + _outbuf.base = req_base; + _outbuf.size = req_size; + _outbuf.origin = this; + + /* prepare reception of reply message */ + _inbuf.base = inbuf_base; + _inbuf.size = inbuf_size; + + /* update state */ + if (_state != PREPARE_REPLY) _state = AWAIT_REPLY; + else _state = PREPARE_AND_AWAIT_REPLY; + _awaits_receipt(); + + /* announce request */ + dest->_announce_request(&_outbuf); + } + + /** + * Wait until a request has arrived and load it for handling + * + * \param inbuf_base base of the request buffer + * \param inbuf_size size of the request buffer + */ + inline void await_request(void * const inbuf_base, + size_t const inbuf_size) + { + /* assertions */ + assert(_state == INACTIVE); + + /* prepare receipt of request */ + _inbuf.base = inbuf_base; + _inbuf.size = inbuf_size; + + /* if anybody already announced a request receive it */ + if (!_request_queue.empty()) { + _receive_request(_request_queue.dequeue()); + _has_received(_inbuf.size); + return; + } + /* no request announced, so wait */ + _state = AWAIT_REQUEST; + _awaits_receipt(); + } + + /** + * Reply last request if there's any and await next request + * + * \param reply_base base of the reply payload + * \param reply_size size of the reply payload + * \param inbuf_base base of the request buffer + * \param inbuf_size size of the request buffer + */ + inline void send_reply_await_request(void * const reply_base, + size_t const reply_size, + void * const inbuf_base, + size_t const inbuf_size) + { + /* reply to the last request if we have to */ + if (_state == PREPARE_REPLY) { + _inbuf.origin->_receive_reply(reply_base, reply_size); + _state = INACTIVE; + } + /* await next request */ + await_request(inbuf_base, inbuf_size); + } + + /** + * Send a notification and stay inactive + * + * \param dest targeted IPC node + * \param note_base base of the note payload + * \param note_size size of the note payload + * + * The caller must ensure that the note payload remains + * until it is buffered by the targeted node. + */ + inline void send_note(Ipc_node * const dest, + void * const note_base, + size_t const note_size) + { + /* assert preconditions */ + assert(_state == INACTIVE || _state == PREPARE_REPLY); + + /* announce request message, our state says: No reply needed */ + _outbuf.base = note_base; + _outbuf.size = note_size; + _outbuf.origin = this; + dest->_announce_request(&_outbuf); + } + + private: + + /** + * IPC node waits for a message to receive to its inbuffer + */ + virtual void _awaits_receipt() = 0; + + /** + * IPC node has received a message in its inbuffer + * + * \param s size of the message + */ + virtual void _has_received(size_t const s) = 0; + }; + + /** + * Manage allocation of a static set of IDs + * + * \param _SIZE How much IDs shall be assignable simultaneously + */ + template + class Id_allocator + { + enum { MIN = 1, MAX = _SIZE }; + + bool _free[MAX + 1]; /* assignability bitmap */ + unsigned _first_free_id; /* hint to optimze access */ + + /** + * Update first free ID after assignment + */ + void _first_free_id_assigned() + { + _first_free_id++; + while (_first_free_id <= MAX) { + if (_free[_first_free_id]) break; + _first_free_id++; + } + } + + /** + * Validate ID + */ + bool _valid_id(unsigned const id) const + { return id >= MIN && id <= MAX; } + + public: + + /** + * Constructor, makes all IDs unassigned + */ + Id_allocator() : _first_free_id(MIN) + { for (unsigned i = MIN; i <= MAX; i++) _free[i] = 1; } + + /** + * Allocate an unassigned ID + * + * \return ID that has been allocated by the call + */ + unsigned alloc() + { + if (!_valid_id(_first_free_id)) assert(0); + _free[_first_free_id] = 0; + unsigned const id = _first_free_id; + _first_free_id_assigned(); + return id; + } + + /** + * Free a given ID + */ + void free(unsigned const id) + { + if (!_valid_id(id)) return; + _free[id] = 1; + if (id < _first_free_id) _first_free_id = id; + } + }; + + /** + * Provides kernel object management for 'T'-objects if 'T' derives from it + */ + template + class Object : public Object_pool::Entry + { + typedef Id_allocator Id_alloc; + + /** + * Allocator for unique IDs for all instances of 'T' + */ + static Id_alloc * _id_alloc() + { + static Id_alloc _id_alloc; + return &_id_alloc; + } + + public: + + typedef Object_pool Pool; + + /** + * Gets every instance of 'T' by its ID + */ + static Pool * pool() + { + static Pool _pool; + return &_pool; + } + + /** + * Placement new + * + * Kernel objects are normally constructed on a memory + * donation so we must be enabled to place them explicitly. + */ + void * operator new (size_t, void * p) { return p; } + + protected: + + /** + * Constructor + * + * Ensures that we have a unique ID and + * can be found through the static object pool. + */ + Object() : Pool::Entry(_id_alloc()->alloc()) + { pool()->insert(static_cast(this)); } + }; + + /** + * Provides the mode transition PIC in a configurable and mappable manner + * + * Initially there exists only the code that switches between kernelmode + * and usermode. It must be present in a continuous region that is located + * at an arbitray RAM address. Its size must not exceed the smallest page + * size supported by the MMU. The Code must be position independent. This + * control then duplicates the code to an aligned region and declares a + * virtual region, where the latter has to be mapped to in every PD, to + * ensure appropriate kernel invokation on CPU interrupts. + */ + struct Mode_transition_control + { + enum { + SIZE_LOG2 = Cpu::MIN_PAGE_SIZE_LOG2, + SIZE = 1 << SIZE_LOG2, + VIRT_BASE = Cpu::HIGHEST_EXCEPTION_ENTRY, + VIRT_END = VIRT_BASE + SIZE, + ALIGNM_LOG2 = SIZE_LOG2, + }; + + /* writeable, page-aligned backing store */ + char _payload[SIZE] __attribute__((aligned(1 << ALIGNM_LOG2))); + + /* labels within the aligned mode transition PIC */ + Cpu::Context * * const _user_context_ptr; + Cpu::Context * const _kernel_context; + addr_t const _virt_user_entry; + addr_t const _virt_kernel_entry; + + /** + * Constructor + */ + Mode_transition_control() : + _user_context_ptr((Cpu::Context * *)((addr_t)_payload + + ((addr_t)&_mt_user_context_ptr - + (addr_t)&_mode_transition_begin))), + + _kernel_context((Cpu::Context *)((addr_t)_payload + + ((addr_t)&_mt_kernel_context_begin - + (addr_t)&_mode_transition_begin))), + + _virt_user_entry(VIRT_BASE + ((addr_t)&_mt_user_entry_pic - + (addr_t)&_mode_transition_begin)), + + _virt_kernel_entry(VIRT_BASE + ((addr_t)&_mt_kernel_entry_pic - + (addr_t)&_mode_transition_begin)) + { + /* check if mode transition PIC fits into aligned region */ + addr_t const pic_begin = (addr_t)&_mode_transition_begin; + addr_t const pic_end = (addr_t)&_mode_transition_end; + size_t const pic_size = pic_end - pic_begin; + assert(pic_size <= SIZE); + + /* check if kernel context fits into the mode transition */ + addr_t const kc_begin = (addr_t)&_mt_kernel_context_begin; + addr_t const kc_end = (addr_t)&_mt_kernel_context_end; + size_t const kc_size = kc_end - kc_begin; + assert(sizeof(Cpu::Context) <= kc_size) + + /* fetch mode transition PIC */ + unsigned * dst = (unsigned *)_payload; + unsigned * src = (unsigned *)pic_begin; + while ((addr_t)src < pic_end) *dst++ = *src++; + + /* try to set CPU exception entry accordingly */ + assert(!Cpu::exception_entry_at(_virt_kernel_entry)) + } + + /** + * Set next usermode-context pointer + */ + void user_context(Cpu::Context * const c) { *_user_context_ptr = c; } + + /** + * Fetch next kernelmode context + */ + void fetch_kernel_context(Cpu::Context * const c) + { *_kernel_context = *c; } + + /** + * Page aligned physical base of the mode transition PIC + */ + addr_t phys_base() { return (addr_t)_payload; } + + /** + * Virtual pointer to the usermode entry PIC + */ + addr_t virt_user_entry() { return _virt_user_entry; } + }; + + + /** + * Static mode transition control + */ + static Mode_transition_control * mtc() + { static Mode_transition_control _object; return &_object; } + + + /** + * Kernel object that represents a Genode PD + */ + class Pd : public Object, + public Software_tlb + { + /* keep ready memory for size aligned extra costs at construction */ + enum { EXTRA_SPACE_SIZE = 2*Software_tlb::MAX_COSTS_PER_TRANSLATION }; + char _extra_space[EXTRA_SPACE_SIZE]; + + public: + + /** + * Constructor + */ + Pd() + { + /* try to add translation for mode transition region */ + enum Mtc_attributes { W = 1, X = 1, K = 1, G = 1 }; + unsigned const slog2 = insert_translation(mtc()->VIRT_BASE, + mtc()->phys_base(), + mtc()->SIZE_LOG2, + W, X, K, G); + + /* extra space needed to translate mode transition region */ + if (slog2) + { + /* Get size aligned extra space */ + addr_t const es = (addr_t)&_extra_space; + addr_t const es_end = es + sizeof(_extra_space); + addr_t const aligned_es = (es_end - (1<= es && aligned_es_end <= es_end) + + /* translate mode transition region globally */ + insert_translation(mtc()->VIRT_BASE, mtc()->phys_base(), + mtc()->SIZE_LOG2, W, X, K, G, + (void *)aligned_es); + } + } + + /** + * Add the CPU context 'c' to this PD + */ + void append_context(Cpu::Context * const c) + { + c->protection_domain(id()); + c->software_tlb((Software_tlb *)this); + } + }; + + /** + * Simple round robin scheduler for 'ENTRY_T' typed clients + */ + template + class Scheduler + { + public: + + /** + * Base class for 'ENTRY_T' to support scheduling + */ + class Entry : public Double_list::Entry + { + friend class Scheduler; + + unsigned _time; /* time wich remains for current lap */ + + /** + * Apply consumption of 'time' + */ + void _consume(unsigned const time) + { _time = _time > time ? _time - time : 0; } + + public: + + /** + * Constructor + */ + Entry() : _time(0) { } + }; + + protected: + + ENTRY_T * const _idle; /* Default entry, can't be removed */ + Double_list _entries; /* List of entries beside '_idle' */ + unsigned const _lap_time; /* Time that an entry gets for one + * scheduling lap to consume */ + + public: + + /** + * Constructor + */ + Scheduler(ENTRY_T * const idle, unsigned const lap_time) + : _idle(idle), _lap_time(lap_time) { assert(_lap_time && _idle); } + + /** + * Returns the entry wich shall scheduled next + * + * \param t At the call it contains the time, wich was consumed + * by the last entry. At the return it is updated to + * the next timeslice. + */ + ENTRY_T * next_entry(unsigned & t) + { + /* update current entry */ + ENTRY_T * e = _entries.head(); + if (!e) { + t = _idle->Entry::_time; + return _idle; + } + e->Entry::_consume(t); + + /* lookup entry with time > 0, refresh depleted timeslices */ + while (!e->Entry::_time) { + e->Entry::_time = _lap_time; + _entries.head_to_tail(); + e = _entries.head(); + } + + /* return next entry and appropriate portion of time */ + t = e->Entry::_time; + return e; + } + + /** + * Get the currently scheduled entry + */ + ENTRY_T * current_entry() const { + return _entries.head() ? _entries.head() : _idle; } + + /** + * Ensure that 'e' does participate in scheduling afterwards + */ + void insert(ENTRY_T * const e) + { + if (e == _idle) return; + e->Entry::_time = _lap_time; + _entries.insert_tail(e); + } + + /** + * Ensures that 'e' doesn't participate in scheduling afterwards + */ + void remove(ENTRY_T * const e) { _entries.remove(e); } + + /** + * Set remaining time of currently scheduled entry to 0 + */ + void yield() + { + ENTRY_T * const e = _entries.head(); + if (e) e->_time = 0; + return; + } + }; + + + /** + * Access to static interrupt-controller + */ + static Pic * pic() { static Pic _object; return &_object; } + + + /** + * Exclusive ownership and handling of one IRQ per instance at a max + */ + class Irq_owner : public Object_pool::Entry + { + /** + * To get any instance of this class by its ID + */ + typedef Object_pool Pool; + static Pool * _pool() { static Pool _pool; return &_pool; } + + /** + * Is called when the IRQ we were waiting for has occured + */ + virtual void _received_irq() = 0; + + /** + * Is called when we start waiting for the occurence of an IRQ + */ + virtual void _awaits_irq() = 0; + + public: + + /** + * Translate 'Irq_owner_pool'-entry ID to IRQ ID + */ + static unsigned id_to_irq(unsigned id) { return id - 1; } + + /** + * Translate IRQ ID to 'Irq_owner_pool'-entry ID + */ + static unsigned irq_to_id(unsigned irq) { return irq + 1; } + + /** + * Constructor + */ + Irq_owner() : Pool::Entry(0) { } + + /** + * Destructor + */ + virtual ~Irq_owner() { } + + /** + * Ensure that our 'receive_irq' gets called on IRQ 'irq' + * + * \return wether the IRQ is allocated to the caller or not + */ + bool allocate_irq(unsigned const irq) + { + /* Check if an allocation is needed and possible */ + unsigned const id = irq_to_id(irq); + if (_id) return _id == id; + if (_pool()->object(id)) return 0; + + /* Let us own the IRQ, but mask it till we await it */ + pic()->mask(irq); + _id = id; + _pool()->insert(this); + return 1; + } + + /** + * Release the ownership of the IRQ 'irq' if we own it + * + * \return wether the IRQ is freed or not + */ + bool free_irq(unsigned const irq) + { + if (_id != irq_to_id(irq)) return 0; + _pool()->remove(this); + _id = 0; + return 1; + } + + /** + * If we own an IRQ, enable it and await 'receive_irq' + */ + void await_irq() + { + assert(_id); + unsigned const irq = id_to_irq(_id); + pic()->unmask(irq); + _awaits_irq(); + } + + /** + * Denote occurence of an IRQ if we own it and awaited it + */ + void receive_irq(unsigned const irq) + { + assert(_id == irq_to_id(irq)); + pic()->mask(irq); + _received_irq(); + } + + /** + * Get owner of IRQ or 0 if the IRQ is not owned by anyone + */ + static Irq_owner * owner(unsigned irq) + { return _pool()->object(irq_to_id(irq)); } + }; + + + /** + * Idle thread entry + */ + static void idle_main() { while (1) ; } + + + /** + * Access to static kernel timer + */ + static Timer * timer() { static Timer _object; return &_object; } + + + class Thread; + + typedef Scheduler Cpu_scheduler; + + + /** + * Access to the static CPU scheduler + */ + static Cpu_scheduler * cpu_scheduler(); + + + /** + * Static kernel PD that describes core + */ + static Pd * core() { static Pd _object; return &_object; } + + + /** + * Get core attributes + */ + unsigned core_id() { return core()->id(); } + + + /** + * Kernel object that represents a Genode thread + */ + class Thread : public Cpu::User_context, + public Object, + public Cpu_scheduler::Entry, + public Ipc_node, + public Irq_owner, + public Fifo::Element + { + enum State { STOPPED, ACTIVE, AWAIT_IPC, AWAIT_RESUMPTION, + AWAIT_IRQ, AWAIT_SIGNAL }; + + Platform_thread * const _platform_thread; /* userland object wich + * addresses this thread */ + State _state; /* thread state, description given at the beginning */ + Pagefault _pagefault; /* last pagefault triggered by this thread */ + Thread * _pager; /* gets informed if thread throws a pagefault */ + unsigned _pd_id; /* ID of the PD this thread runs on */ + Native_utcb * _phys_utcb; /* physical UTCB base */ + Native_utcb * _virt_utcb; /* virtual UTCB base */ + + /** + * Resume execution of thread + */ + void _activate() + { + cpu_scheduler()->insert(this); + _state = ACTIVE; + } + + public: + + void * operator new (size_t, void * p) { return p; } + + /** + * Constructor + */ + Thread(Platform_thread * const platform_thread) : + _platform_thread(platform_thread), + _state(STOPPED), _pager(0), _pd_id(0), + _phys_utcb(0), _virt_utcb(0) + { } + + /** + * Start this 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 cpu_no, + unsigned const pd_id, Native_utcb * const phys_utcb, + Native_utcb * const virt_utcb); + + /** + * Pause this thread + */ + void pause() + { + assert(_state == AWAIT_RESUMPTION || _state == ACTIVE); + cpu_scheduler()->remove(this); + _state = AWAIT_RESUMPTION; + } + + /** + * Stop this thread + */ + void stop() + { + cpu_scheduler()->remove(this); + _state = STOPPED; + } + + /** + * Resume this thread + */ + int resume() + { + assert (_state == AWAIT_RESUMPTION || _state == ACTIVE) + cpu_scheduler()->insert(this); + if (_state == ACTIVE) return 1; + _state = ACTIVE; + return 0; + } + + /** + * Send a request and await the reply + */ + void request_and_wait(Thread * const dest, size_t const size) + { + Ipc_node::send_request_await_reply(dest, phys_utcb()->base(), + size, phys_utcb()->base(), + phys_utcb()->size()); + } + + /** + * Wait for any request + */ + void wait_for_request() + { + Ipc_node::await_request(phys_utcb()->base(), + phys_utcb()->size()); + } + + /** + * Reply to the last request and await the next one + */ + void reply_and_wait(size_t const size) + { + Ipc_node::send_reply_await_request(phys_utcb()->base(), size, + phys_utcb()->base(), + phys_utcb()->size()); + } + + /** + * Initialize our execution context + * + * \param ip instruction pointer + * \param sp stack pointer + * \param pd_id identifies protection domain we're assigned to + */ + void init_context(void * const ip, void * const sp, + unsigned const pd_id); + + /** + * Handle a pagefault that originates from this thread + * + * \param va Virtual fault address + * \param w Was it a write access? + */ + void pagefault(addr_t const va, bool const w); + + /** + * Get unique thread ID, avoid method ambiguousness + */ + unsigned id() const { return Object::id(); } + + /** + * Gets called when we await a signal at a signal receiver + */ + void await_signal() + { + cpu_scheduler()->remove(this); + _state = AWAIT_IRQ; + } + + /** + * Gets called when we have received a signal at a signal receiver + */ + void receive_signal(Signal const s) + { + *(Signal *)phys_utcb()->base() = s; + _activate(); + } + + + /*************** + ** Accessors ** + ***************/ + + Platform_thread * platform_thread() const + { return _platform_thread; } + + void pager(Thread * const p) { _pager = p; } + + unsigned pd_id() const { return _pd_id; } + + Native_utcb * phys_utcb() const { return _phys_utcb; } + + private: + + + /************** + ** Ipc_node ** + **************/ + + void _has_received(size_t const s) + { + user_arg_0(s); + if (_state != ACTIVE) _activate(); + } + + void _awaits_receipt() + { + cpu_scheduler()->remove(this); + _state = AWAIT_IPC; + } + + + /*************** + ** Irq_owner ** + ***************/ + + void _received_irq() { _activate(); } + + void _awaits_irq() + { + cpu_scheduler()->remove(this); + _state = AWAIT_IRQ; + } + }; + + class Signal_receiver; + + /** + * Specific signal type, owned by a receiver, can be triggered asynchr. + */ + class Signal_context : public Object, + public Fifo::Element + { + friend class Signal_receiver; + + Signal_receiver * const _receiver; /* the receiver that owns us */ + unsigned const _imprint; /* every of our signals gets signed with */ + unsigned _number; /* how often we got triggered */ + + public: + + /** + * Constructor + */ + Signal_context(Signal_receiver * const r, + unsigned const imprint) + : _receiver(r), _imprint(imprint), _number(0) { } + + /** + * Trigger this context + * + * \param number how often this call triggers us at once + */ + void trigger_signal(unsigned const number); + }; + + /** + * Manage signal contexts and enable threads to trigger and await them + */ + class Signal_receiver : + public Object + { + Fifo _listeners; + Fifo _pending_contexts; + + /** + * Deliver as much submitted signals to listening threads as possible + */ + void _listen() + { + while (1) + { + /* any pending context? */ + if (_pending_contexts.empty()) return; + Signal_context * const c = _pending_contexts.dequeue(); + + /* if there is no listener, enqueue context again and return */ + if (_listeners.empty()) { + _pending_contexts.enqueue(c); + return; + } + /* awake a listener and transmit signal info to it */ + Thread * const t = _listeners.dequeue(); + t->receive_signal(Signal(c->_imprint, c->_number)); + + /* reset context */ + c->_number = 0; + } + } + + public: + + /** + * Let a thread listen to our contexts + */ + void add_listener(Thread * const t) + { + t->await_signal(); + _listeners.enqueue(t); + _listen(); + } + + /** + * Recognize that one of our contexts was triggered + */ + void add_pending_context(Signal_context * const c) + { + assert(c->_receiver == this); + _pending_contexts.enqueue(c); + _listen(); + } + }; + + /** + * Access to static CPU scheduler + */ + Cpu_scheduler * cpu_scheduler() + { + /* create idle thread */ + static char idle_stack[DEFAULT_STACK_SIZE] + __attribute__((aligned(Cpu::DATA_ACCESS_ALIGNM))); + static Thread idle((Platform_thread *)0); + static bool initial = 1; + if (initial) + { + /* initialize idle thread */ + void * sp; + sp = (void *)&idle_stack[sizeof(idle_stack)/sizeof(idle_stack[0])]; + idle.init_context((void *)&idle_main, sp, core_id()); + initial = 0; + } + /* create scheduler with a permanent idle thread */ + static unsigned const user_time_slice = + timer()->ms_to_tics(USER_TIME_SLICE_MS); + static Cpu_scheduler cpu_sched(&idle, user_time_slice); + return &cpu_sched; + } + + /** + * Get attributes of the mode transition region in every PD + */ + addr_t mode_transition_virt_base() { return mtc()->VIRT_BASE; } + size_t mode_transition_size() { return mtc()->SIZE; } + + /** + * Get attributes of the kernel objects + */ + size_t thread_size() { return sizeof(Thread); } + size_t pd_size() { return sizeof(Pd); } + size_t signal_context_size() { return sizeof(Signal_context); } + size_t signal_receiver_size() { return sizeof(Signal_receiver); } + unsigned pd_alignm_log2() { return Pd::ALIGNM_LOG2; } + + + /** + * Handle the occurence of an unknown exception + */ + void handle_invalid_excpt(Thread * const) { assert(0); } + + + /** + * Handle an interrupt request + */ + void handle_interrupt(Thread * const) + { + /* determine handling for specific interrupt */ + unsigned irq; + if (pic()->take_request(irq)) + { + switch (irq) { + + case Timer::IRQ: { + + /* clear interrupt at timer */ + timer()->clear_interrupt(); + break; } + + default: { + + /* IRQ not owned by core, thus notify IRQ owner */ + Irq_owner * const o = Irq_owner::owner(irq); + assert(o); + o->receive_irq(irq); + break; } + } + } + /* disengage interrupt controller from IRQ */ + pic()->finish_request(); + } + + + /** + * Handle an usermode pagefault + * + * \param user thread that has caused the pagefault + */ + void handle_pagefault(Thread * const user) + { + /* check out cause and attributes of abort */ + addr_t va; + bool w; + assert(user->translation_miss(va, w)); + + /* the user might be able to resolve the pagefault */ + user->pagefault(va, w); + } + + + /** + * Handle request of an unknown signal type + */ + void handle_invalid_syscall(Thread * const) { assert(0); } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_new_pd(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* create PD */ + void * dst = (void *)user->user_arg_1(); + Pd * const pd = new (dst) Pd(); + + /* return success */ + user->user_arg_0(pd->id()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_new_thread(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* dispatch arguments */ + Syscall_arg const arg1 = user->user_arg_1(); + Syscall_arg const arg2 = user->user_arg_2(); + + /* create thread */ + Thread * const t = new ((void *)arg1) + Thread((Platform_thread *)arg2); + + /* return thread ID */ + user->user_arg_0((Syscall_ret)t->id()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_start_thread(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* dispatch arguments */ + Platform_thread * pt = (Platform_thread *)user->user_arg_1(); + void * const ip = (void *)user->user_arg_2(); + void * const sp = (void *)user->user_arg_3(); + unsigned const cpu = (unsigned)user->user_arg_4(); + + /* get targeted thread */ + Thread * const t = Thread::pool()->object(pt->id()); + assert(t); + + /* start thread */ + assert(!t->start(ip, sp, cpu, pt->pd_id(), + pt->phys_utcb(), pt->virt_utcb())) + + /* return software TLB that the thread is assigned to */ + Pd::Pool * const pp = Pd::pool(); + Pd * const pd = pp->object(t->pd_id()); + assert(pd); + Software_tlb * const tlb = static_cast(pd); + user->user_arg_0((Syscall_ret)tlb); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_pause_thread(Thread * const user) + { + unsigned const tid = user->user_arg_1(); + + /* shortcut for a thread to pause itself */ + if (!tid) { + user->pause(); + user->user_arg_0(0); + return; + } + + /* get targeted thread and check permissions */ + Thread * const t = Thread::pool()->object(tid); + assert(t && (user->pd_id() == core_id() || user==t)); + + /* pause targeted thread */ + t->pause(); + user->user_arg_0(0); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_resume_thread(Thread * const user) + { + /* get targeted thread */ + Thread * const t = Thread::pool()->object(user->user_arg_1()); + assert(t); + + /* check permissions */ + assert(user->pd_id() == core_id() || user->pd_id() == t->pd_id()); + + /* resume targeted thread */ + user->user_arg_0(t->resume()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_yield_thread(Thread * const user) + { + /* get targeted thread */ + Thread * const t = Thread::pool()->object(user->user_arg_1()); + + /* invoke kernel object */ + if (t) t->resume(); + cpu_scheduler()->yield(); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_current_thread_id(Thread * const user) + { user->user_arg_0((Syscall_ret)user->id()); } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_get_thread(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* get target */ + unsigned const tid = (unsigned)user->user_arg_1(); + Thread * t; + + /* user targets a thread by ID */ + if (tid) { + t = Thread::pool()->object(tid); + assert(t); + + /* user targets itself */ + } else t = user; + + /* return target platform thread */ + user->user_arg_0((Syscall_ret)t->platform_thread()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_wait_for_request(Thread * const user) + { + user->wait_for_request(); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_request_and_wait(Thread * const user) + { + /* get IPC receiver */ + Thread * const t = Thread::pool()->object(user->user_arg_1()); + assert(t); + + /* do IPC */ + user->request_and_wait(t, (size_t)user->user_arg_2()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_reply_and_wait(Thread * const user) + { user->reply_and_wait((size_t)user->user_arg_1()); } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_set_pager(Thread * const user) + { + /* assert preconditions */ + assert(user->pd_id() == core_id()); + + /* get faulter and pager thread */ + Thread * const p = Thread::pool()->object(user->user_arg_1()); + Thread * const f = Thread::pool()->object(user->user_arg_2()); + assert(p && f); + + /* assign pager */ + f->pager(p); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_update_pd(Thread * const user) + { + assert(user->pd_id() == core_id()); + Cpu::flush_tlb_by_pid(user->user_arg_1()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_allocate_irq(Thread * const user) + { + assert(user->pd_id() == core_id()); + unsigned irq = user->user_arg_1(); + user->user_arg_0(user->allocate_irq(irq)); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_free_irq(Thread * const user) + { + assert(user->pd_id() == core_id()); + unsigned irq = user->user_arg_1(); + user->user_arg_0(user->free_irq(irq)); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_await_irq(Thread * const user) + { + assert(user->pd_id() == core_id()); + user->await_irq(); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_print_char(Thread * const user) + { + Genode::printf("%c", (char)user->user_arg_1()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_read_register(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* get targeted thread */ + Thread * const t = Thread::pool()->object(user->user_arg_1()); + assert(t); + + /* return requested register */ + unsigned gpr; + assert(t->get_gpr(user->user_arg_2(), gpr)); + user->user_arg_0(gpr); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_write_register(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* get targeted thread */ + Thread * const t = Thread::pool()->object(user->user_arg_1()); + assert(t); + + /* write to requested register */ + unsigned const gpr = user->user_arg_3(); + assert(t->set_gpr(user->user_arg_2(), gpr)); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_new_signal_receiver(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* create receiver */ + void * dst = (void *)user->user_arg_1(); + Signal_receiver * const r = new (dst) Signal_receiver(); + + /* return success */ + user->user_arg_0(r->id()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_new_signal_context(Thread * const user) + { + /* check permissions */ + assert(user->pd_id() == core_id()); + + /* lookup receiver */ + unsigned rid = user->user_arg_2(); + Signal_receiver * const r = Signal_receiver::pool()->object(rid); + assert(r); + + /* create context */ + void * dst = (void *)user->user_arg_1(); + unsigned imprint = user->user_arg_3(); + Signal_context * const c = new (dst) Signal_context(r, imprint); + + /* return success */ + user->user_arg_0(c->id()); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_await_signal(Thread * const user) + { + /* lookup receiver */ + unsigned rid = user->user_arg_2(); + Signal_receiver * const r = Signal_receiver::pool()->object(rid); + assert(r); + + /* let user listen to receiver */ + r->add_listener(user); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_submit_signal(Thread * const user) + { + /* lookup context */ + Signal_context * const c = + Signal_context::pool()->object(user->user_arg_1()); + assert(c); + + /* trigger signal at context */ + c->trigger_signal(user->user_arg_2()); + } + + + /** + * Handle a syscall request + * + * \param user thread that called the syscall + */ + void handle_syscall(Thread * const user) + { + /* map syscall types to the according handler functions */ + typedef void (*Syscall_handler)(Thread * const); + static Syscall_handler const handle_sysc[] = + { + /* syscall ID */ /* handler */ + /*------------*/ /*-------------------*/ + /* 0 */ handle_invalid_syscall, + /* 1 */ do_new_thread, + /* 2 */ do_start_thread, + /* 3 */ do_pause_thread, + /* 4 */ do_resume_thread, + /* 5 */ do_get_thread, + /* 6 */ do_current_thread_id, + /* 7 */ do_yield_thread, + /* 8 */ do_request_and_wait, + /* 9 */ do_reply_and_wait, + /* 10 */ do_wait_for_request, + /* 11 */ do_set_pager, + /* 12 */ do_update_pd, + /* 13 */ do_new_pd, + /* 14 */ do_allocate_irq, + /* 15 */ do_await_irq, + /* 16 */ do_free_irq, + /* 17 */ do_print_char, + /* 18 */ do_read_register, + /* 19 */ do_write_register, + /* 20 */ do_new_signal_receiver, + /* 21 */ do_new_signal_context, + /* 22 */ do_await_signal, + /* 23 */ do_submit_signal, + }; + enum { MAX_SYSCALL = sizeof(handle_sysc)/sizeof(handle_sysc[0]) - 1 }; + + /* handle syscall that has been requested by the user */ + unsigned syscall = user->user_arg_0(); + if (syscall > MAX_SYSCALL) handle_sysc[INVALID_SYSCALL](user); + else handle_sysc[syscall](user); + } +} + + +/** + * Kernel main routine + */ +extern "C" void kernel() +{ + unsigned const timer_value = timer()->stop(); + static unsigned user_time = 0; + static bool initial_call = true; + + /* an exception occured */ + if (!initial_call) + { + /* update how much time the last user has consumed */ + user_time = timer_value < user_time ? user_time - timer_value : 0; + + /* map exception types to exception-handler functions */ + typedef void (*Exception_handler)(Thread * const); + static Exception_handler const handle_excpt[] = + { + /* exception ID */ /* handler */ + /*--------------*/ /*---------*/ + /* 0 */ handle_invalid_excpt, + /* 1 */ handle_interrupt, + /* 2 */ handle_pagefault, + /* 3 */ handle_syscall + }; + /* handle exception that interrupted the last user */ + Thread * const user = cpu_scheduler()->current_entry(); + enum { MAX_EXCPT = sizeof(handle_excpt)/sizeof(handle_excpt[0]) - 1 }; + unsigned const e = user->exception(); + if (e > MAX_EXCPT) handle_invalid_excpt(user); + else handle_excpt[e](user); + + /* kernel initialization */ + } else { + + /* tell the code that called kernel, what to do when kernel returns */ + _call_after_kernel = mtc()->virt_user_entry(); + + /* compose core address space */ + addr_t a = 0; + while (1) + { + /* map everything except the mode transition region */ + enum { + SIZE_LOG2 = Software_tlb::MAX_TRANSL_SIZE_LOG2, + SIZE = 1 << SIZE_LOG2, + }; + if (mtc()->VIRT_END <= a || mtc()->VIRT_BASE > (a + SIZE - 1)) + { + /* map 1:1 with rwx permissions */ + if (core()->insert_translation(a, a, SIZE_LOG2, 1, 1, 0, 0)) + assert(0); + } + /* check condition to continue */ + addr_t const next_a = a + SIZE; + if (next_a > a) a = next_a; + else break; + } + /* compose kernel CPU context */ + static Cpu::Context kernel_context; + kernel_context.instruction_ptr((addr_t)kernel); + kernel_context.return_ptr(mtc()->virt_user_entry()); + kernel_context.stack_ptr((addr_t)&_kernel_stack_high); + + /* add kernel to the core PD */ + core()->append_context(&kernel_context); + + /* offer the final kernel context to the mode transition page */ + mtc()->fetch_kernel_context(&kernel_context); + + /* switch to core address space */ + Cpu::enable_mmu(core(), core_id()); + + /* create the core main thread */ + static Native_utcb cm_utcb; + static char cm_stack[DEFAULT_STACK_SIZE] + __attribute__((aligned(Cpu::DATA_ACCESS_ALIGNM))); + static Thread core_main((Platform_thread *)0); + _main_utcb = &cm_utcb; + enum { CM_STACK_SIZE = sizeof(cm_stack)/sizeof(cm_stack[0]) + 1 }; + core_main.start((void *)&CORE_MAIN, + (void *)&cm_stack[CM_STACK_SIZE - 1], + 0, core_id(), &cm_utcb, &cm_utcb); + + /* kernel initialization finished */ + initial_call = false; + } + /* offer next user context to the mode transition PIC */ + Thread * const next = cpu_scheduler()->next_entry(user_time); + mtc()->user_context(next); + + /* limit user mode execution in time */ + timer()->start_one_shot(user_time); + pic()->unmask(Timer::IRQ); +} + + +/******************** + ** Kernel::Thread ** + ********************/ + +int Thread::start(void *ip, void *sp, unsigned cpu_no, + unsigned const pd_id, + Native_utcb * const phys_utcb, + Native_utcb * const virt_utcb) +{ + /* check state and arguments */ + assert(_state == STOPPED) + assert(!cpu_no); + + /* apply thread configuration */ + init_context(ip, sp, pd_id); + _phys_utcb = phys_utcb; + _virt_utcb = virt_utcb; + + /* offer thread-entry arguments */ + user_arg_0((unsigned)_virt_utcb); + + /* start thread */ + cpu_scheduler()->insert(this); + _state = ACTIVE; + return 0; +} + + +void Thread::init_context(void * const ip, void * const sp, + unsigned const pd_id) +{ + /* basic thread state */ + stack_ptr((addr_t)sp); + instruction_ptr((addr_t)ip); + + /* join a pd */ + _pd_id = pd_id; + Pd * const pd = Pd::pool()->object(_pd_id); + assert(pd) + protection_domain(pd_id); + software_tlb(pd); +} + + +void Thread::pagefault(addr_t const va, bool const w) +{ + /* pause faulter */ + cpu_scheduler()->remove(this); + _state = AWAIT_RESUMPTION; + + /* inform pager through IPC */ + assert(_pager); + Software_tlb * const tlb = + static_cast(software_tlb()); + _pagefault = + Pagefault(id(), tlb, instruction_ptr(), va, w); + Ipc_node::send_note(_pager, &_pagefault, sizeof(_pagefault)); +} + + +/**************************** + ** Kernel::Signal_context ** + ****************************/ + + +void Signal_context::trigger_signal(unsigned const number) +{ + /* raise our number */ + unsigned const old_nr = _number; + _number += number; + assert(old_nr <= _number); + + /* notify our receiver */ + if (_number) _receiver->add_pending_context(this); +} + diff --git a/base-hw/src/core/panda_a2/kernel_support.h b/base-hw/src/core/panda_a2/kernel_support.h new file mode 100644 index 0000000000..2af127493b --- /dev/null +++ b/base-hw/src/core/panda_a2/kernel_support.h @@ -0,0 +1,21 @@ +/* + * \brief Kernel support specific for the PandaBoard A2 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * 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__PANDA_A2__KERNEL_SUPPORT_H_ +#define _SRC__CORE__PANDA_A2__KERNEL_SUPPORT_H_ + +/* local includes */ +#include + +#endif /* _SRC__CORE__PANDA_A2__KERNEL_SUPPORT_H_ */ + diff --git a/base-hw/src/core/panda_a2/platform_support.cc b/base-hw/src/core/panda_a2/platform_support.cc new file mode 100644 index 0000000000..176967dddc --- /dev/null +++ b/base-hw/src/core/panda_a2/platform_support.cc @@ -0,0 +1,84 @@ +/* + * \brief Platform implementations specific for base-hw and Panda A2 + * \author Martin Stein + * \date 2012-04-27 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + + +Native_region * Platform::_ram_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { Panda_a2::EMIF1_EMIF2_CS0_SDRAM_BASE, + Panda_a2::EMIF1_EMIF2_CS0_SDRAM_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_irq_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { 0, Pl390_base::MAX_INTERRUPT_ID + 1 } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_core_only_irq_regions(unsigned const i) +{ + static Native_region _regions[] = + { + /* core timer */ + { Cortex_a9::PRIVATE_TIMER_IRQ, 1 }, + + /* core UART */ + { Panda_a2::TL16C750_3_IRQ, 1 } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_mmio_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { Panda_a2::L4_PER_BASE, Panda_a2::L4_PER_SIZE }, + { Panda_a2::L4_CFG_BASE, Panda_a2::L4_CFG_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_core_only_mmio_regions(unsigned const i) +{ + static Native_region _regions[] = + { + /* core timer and PIC */ + { Panda_a2::CORTEX_A9_PRIVATE_MEM_BASE, + Panda_a2::CORTEX_A9_PRIVATE_MEM_SIZE }, + + /* core UART */ + { Panda_a2::TL16C750_3_MMIO_BASE, Panda_a2::TL16C750_3_MMIO_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + diff --git a/base-hw/src/core/panda_a2/software_tlb.h b/base-hw/src/core/panda_a2/software_tlb.h new file mode 100644 index 0000000000..3789e2602b --- /dev/null +++ b/base-hw/src/core/panda_a2/software_tlb.h @@ -0,0 +1,26 @@ +/* + * \brief Software TLB controls specific for the PandaBoard A2 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * 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__PANDA_A2__SOFTWARE_TLB_H_ +#define _SRC__CORE__PANDA_A2__SOFTWARE_TLB_H_ + +/* Genode includes */ +#include + +/** + * Software TLB controls + */ +class Software_tlb : public Genode::Section_table { }; + +#endif /* _SRC__CORE__PANDA_A2__SOFTWARE_TLB_H_ */ + diff --git a/base-hw/src/core/pbxa9/kernel_support.h b/base-hw/src/core/pbxa9/kernel_support.h new file mode 100644 index 0000000000..ff3080242a --- /dev/null +++ b/base-hw/src/core/pbxa9/kernel_support.h @@ -0,0 +1,21 @@ +/* + * \brief Kernel support specific for the Realview PBXA9 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * 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__PBXA9__KERNEL_SUPPORT_H_ +#define _SRC__CORE__PBXA9__KERNEL_SUPPORT_H_ + +/* local includes */ +#include + +#endif /* _SRC__CORE__PBXA9__KERNEL_SUPPORT_H_ */ + diff --git a/base-hw/src/core/pbxa9/platform_support.cc b/base-hw/src/core/pbxa9/platform_support.cc new file mode 100644 index 0000000000..120ec49bbd --- /dev/null +++ b/base-hw/src/core/pbxa9/platform_support.cc @@ -0,0 +1,82 @@ +/* + * \brief Parts of platform that are specific to PBXA9 + * \author Martin Stein + * \date 2012-04-27 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + + +Native_region * Platform::_ram_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { Pbxa9::NORTHBRIDGE_DDR_0_BASE, Pbxa9::NORTHBRIDGE_DDR_0_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_irq_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { 0, Pl390_base::MAX_INTERRUPT_ID + 1 } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_core_only_irq_regions(unsigned const i) +{ + static Native_region _regions[] = + { + /* core timer */ + { Cortex_a9::PRIVATE_TIMER_IRQ, 1 }, + + /* core UART */ + { Pbxa9::PL011_0_IRQ, 1 } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_mmio_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { Pbxa9::SOUTHBRIDGE_APB_BASE, Pbxa9::SOUTHBRIDGE_APB_SIZE }, + { Pbxa9::NORTHBRIDGE_AHB_BASE, Pbxa9::NORTHBRIDGE_AHB_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_core_only_mmio_regions(unsigned const i) +{ + static Native_region _regions[] = + { + /* core timer and PIC */ + { Pbxa9::CORTEX_A9_PRIVATE_MEM_BASE, Pbxa9::CORTEX_A9_PRIVATE_MEM_SIZE }, + + /* core UART */ + { Pbxa9::PL011_0_MMIO_BASE, Pbxa9::PL011_0_MMIO_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + diff --git a/base-hw/src/core/pbxa9/software_tlb.h b/base-hw/src/core/pbxa9/software_tlb.h new file mode 100644 index 0000000000..e1ba767df3 --- /dev/null +++ b/base-hw/src/core/pbxa9/software_tlb.h @@ -0,0 +1,26 @@ +/* + * \brief Software TLB controls specific for the Realview PBXA9 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * 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__PBXA9__SOFTWARE_TLB_H_ +#define _SRC__CORE__PBXA9__SOFTWARE_TLB_H_ + +/* Genode includes */ +#include + +/** + * Software TLB controls + */ +class Software_tlb : public Genode::Section_table { }; + +#endif /* _SRC__CORE__PBXA9__SOFTWARE_TLB_H_ */ + diff --git a/base-hw/src/core/platform.cc b/base-hw/src/core/platform.cc new file mode 100644 index 0000000000..0e8c7df93d --- /dev/null +++ b/base-hw/src/core/platform.cc @@ -0,0 +1,162 @@ +/* + * \brief Platform implementation specific for hw + * \author Martin Stein + * \date 2011-12-21 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file 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 + +using namespace Genode; + +extern int _prog_img_beg; +extern int _prog_img_end; + +/** + * Format of a boot-module header + */ +struct Bm_header +{ + long name; /* physical address of null-terminated string */ + long base; /* physical address of module data */ + long size; /* size of module data in bytes */ +}; + +extern int _boot_modules_begin; +extern Bm_header _boot_module_headers_begin; +extern Bm_header _boot_module_headers_end; +extern int _boot_modules_end; + +/** + * Functionpointer that provides accessor to a pool of address regions + */ +typedef Native_region * (*Region_pool)(unsigned const); + +namespace Kernel +{ + addr_t mode_transition_virt_base(); + size_t mode_transition_size(); +} + + +/** + * Helper to initialise allocators through include/exclude region lists + */ +static void init_alloc(Range_allocator * const alloc, + Region_pool incl_regions, Region_pool excl_regions, + unsigned const granu_log2 = 0) +{ + /* make all include regions available */ + Native_region * r = incl_regions(0); + for (unsigned i = 0; r; r = incl_regions(++i)) { + if (granu_log2) { + addr_t const b = trunc(r->base, granu_log2); + addr_t const s = round(r->size, granu_log2); + alloc->add_range(b, s); + } + else alloc->add_range(r->base, r->size); + } + /* preserve all exclude regions */ + r = excl_regions(0); + for (unsigned i = 0; r; r = excl_regions(++i)) { + if (granu_log2) { + addr_t const b = trunc(r->base, granu_log2); + addr_t const s = round(r->size, granu_log2); + alloc->remove_range(b, s); + } + else alloc->remove_range(r->base, r->size); + } +} + + +/************** + ** Platform ** + **************/ + +Native_region * Platform::_core_only_ram_regions(unsigned const i) +{ + static Native_region _r[] = + { + /* avoid null pointers */ + { 0, 1 }, + + /* mode transition region */ + { Kernel::mode_transition_virt_base(), Kernel::mode_transition_size() }, + + /* core image */ + { (addr_t)&_prog_img_beg, + (size_t)((addr_t)&_prog_img_end - (addr_t)&_prog_img_beg) }, + + /* boot modules */ + { (addr_t)&_boot_modules_begin, + (size_t)((addr_t)&_boot_modules_end - (addr_t)&_boot_modules_begin) } + }; + return i < sizeof(_r)/sizeof(_r[0]) ? &_r[i] : 0; +} + + +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(0xffff0000) +{ + /* + * Initialise platform resource allocators. + * Core mem alloc must come first because it is + * used by the other allocators. + */ + enum { VERBOSE = 0 }; + unsigned const psl2 = get_page_size_log2(); + init_alloc(&_core_mem_alloc, _ram_regions, _core_only_ram_regions, psl2); + init_alloc(&_irq_alloc, _irq_regions, _core_only_irq_regions); + init_alloc(&_io_mem_alloc, _mmio_regions, _core_only_mmio_regions, psl2); + + /* add boot modules to ROM FS */ + Bm_header * header = &_boot_module_headers_begin; + for (; header < &_boot_module_headers_end; header++) { + Rom_module * rom_module = new (core_mem_alloc()) + Rom_module(header->base, header->size, (const char*)header->name); + _rom_fs.insert(rom_module); + } + /* print ressource summary */ + if (VERBOSE) { + printf("Core memory allocator\n"); + printf("---------------------\n"); + _core_mem_alloc.raw()->dump_addr_tree(); + printf("\n"); + printf("IO memory allocator\n"); + printf("-------------------\n"); + _io_mem_alloc.raw()->dump_addr_tree(); + printf("\n"); + printf("ROM filesystem\n"); + printf("--------------\n"); + _rom_fs.print_fs(); + printf("\n"); + } +} + + +/***************** + ** Core_parent ** + *****************/ + +void Core_parent::exit(int exit_value) +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + diff --git a/base-hw/src/core/platform_pd.cc b/base-hw/src/core/platform_pd.cc new file mode 100644 index 0000000000..c782bc6fa4 --- /dev/null +++ b/base-hw/src/core/platform_pd.cc @@ -0,0 +1,28 @@ +/* + * \brief Protection-domain facility + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs 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; + + +/***************** + ** Platform PD ** + *****************/ + +void Platform_pd::unbind_thread(Platform_thread *thread) { assert(0); } + + +Platform_pd::~Platform_pd() { assert(0); } + diff --git a/base-hw/src/core/platform_thread.cc b/base-hw/src/core/platform_thread.cc new file mode 100644 index 0000000000..efc90c3ab3 --- /dev/null +++ b/base-hw/src/core/platform_thread.cc @@ -0,0 +1,145 @@ +/* + * \brief Thread facility + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs 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; + +namespace Kernel { unsigned core_id(); } + + +Platform_thread::Platform_thread(const char * name, + Thread_base * const thread_base, + unsigned long const stack_size, + unsigned long const pd_id) +: + _thread_base(thread_base), _stack_size(stack_size), + _pd_id(pd_id), _rm_client(0), _virt_utcb(0) +{ + strncpy(_name, name, NAME_MAX_LEN); + + /* create UTCB for a core thread */ + Range_allocator * const ram = platform()->ram_alloc(); + assert(ram->alloc_aligned(sizeof(Native_utcb), (void **)&_phys_utcb, + MIN_MAPPING_SIZE_LOG2)); + _virt_utcb = _phys_utcb; + + /* common constructor parts */ + _init(); +} + + +Platform_thread::Platform_thread(const char * name, unsigned int priority, + addr_t utcb) +: + _thread_base(0), _stack_size(0), _pd_id(0), _rm_client(0), + _virt_utcb((Native_utcb *)utcb) +{ + strncpy(_name, name, NAME_MAX_LEN); + + /* + * Allocate UTCB backing store for a thread outside of core. Page alignment + * is done by RAM session by default. It's save to use core env because + * this cannot be its server activation thread. + */ + try { + Ram_session_component * const ram = + dynamic_cast(core_env()->ram_session()); + assert(ram); + _utcb = ram->alloc(sizeof(Native_utcb), 1); + _phys_utcb = (Native_utcb *)ram->phys_addr(_utcb); + } + catch (...) { assert(0); } + + /* common constructor parts */ + _init(); +} + + +int Platform_thread::join_pd(unsigned long const pd_id, + bool const main_thread) +{ + /* check if we're already in another PD */ + if (_pd_id && _pd_id != pd_id) return -1; + + /* denote configuration for start method */ + _pd_id = pd_id; + _main_thread = main_thread; + return 0; +} + + +void Platform_thread::_init() +{ + /* create kernel object */ + void * kernel_thread; + Range_allocator * ram = platform()->ram_alloc(); + assert(ram->alloc(Kernel::thread_size(), &kernel_thread)); + _id = Kernel::new_thread(kernel_thread, this); + assert(_id); +} + + +int Platform_thread::start(void * ip, void * sp, unsigned int cpu_no) +{ + /* check thread attributes */ + assert(_pd_id); + + /* + * If this is a main thread outside of core it'll not manage its + * virtual context area by itself, as it is done for other threads + * through a sub RM-session. Therefore we attach the UTCB to its + * address space before it gets started. + */ + if (_pd_id != Kernel::core_id() && _main_thread) + { + /* + * Declare page aligned virtual UTCB outside the context area. + * Kernel afterwards offers this as bootstrap argument to the thread. + */ + _virt_utcb = (Native_utcb *)((platform()->vm_start() + + platform()->vm_size() - sizeof(Native_utcb)) + & ~((1<member_rm_session(); + try { rm->attach(_utcb, 0, 0, true, _virt_utcb, 0); } + catch (...) { assert(0); } + } + /* let thread participate in CPU scheduling */ + _software_tlb = Kernel::start_thread(this, ip, sp, cpu_no); + return _software_tlb ? 0 : -1; +} + + +void Platform_thread::pager(Pager_object * const pager) +{ + /* announce pager thread to kernel */ + Kernel::set_pager(pager->cap().dst(), _id); + + /* get RM client from pager pointer */ + _rm_client = dynamic_cast(pager); + assert(_rm_client); +} + + +Genode::Pager_object * Platform_thread::pager() const +{ + assert(_rm_client) + return static_cast(_rm_client); +} + diff --git a/base-hw/src/core/ram_session_support.cc b/base-hw/src/core/ram_session_support.cc new file mode 100644 index 0000000000..1219b15563 --- /dev/null +++ b/base-hw/src/core/ram_session_support.cc @@ -0,0 +1,31 @@ +/* + * \brief Export RAM dataspace as shared memory object (dummy) + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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) +{ memset((void *)ds->phys_addr(), 0, ds->size()); } + diff --git a/base-hw/src/core/rm_session_support.cc b/base-hw/src/core/rm_session_support.cc new file mode 100644 index 0000000000..9ae6e89c48 --- /dev/null +++ b/base-hw/src/core/rm_session_support.cc @@ -0,0 +1,85 @@ +/* + * \brief RM- and pager implementations specific for base-hw and core + * \author Martin Stein + * \date 2012-02-12 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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 +#include + +using namespace Genode; + + +/*************** + ** Rm_client ** + ***************/ + + +void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size) +{ + /* get software TLB of the thread that we serve */ + Platform_thread * const pt = Kernel::get_thread(badge()); + assert(pt); + Software_tlb * const tlb = pt->software_tlb(); + assert(tlb); + + /* update all translation caches */ + tlb->remove_region(virt_base, size); + Kernel::update_pd(pt->pd_id()); + + /* try to regain administrative memory that has been freed by unmap */ + size_t s; + void * base; + while (tlb->regain_memory(base, s)) platform()->ram_alloc()->free(base, s); +} + + +/*************** + ** Ipc_pager ** + ***************/ + +void Ipc_pager::resolve_and_wait_for_fault() +{ + /* valid mapping? */ + assert(_mapping.valid()); + + /* do we need extra space to resolve pagefault? */ + Software_tlb * const tlb = _pagefault.software_tlb; + enum Mapping_attributes { X = 1, K = 0, G = 0 }; + unsigned sl2 = tlb->insert_translation(_mapping.virt_address, + _mapping.phys_address, _mapping.size_log2, + _mapping.writable, X, K, G); + if (sl2) + { + /* try to get some natural aligned space */ + void * space; + assert(platform()->ram_alloc()->alloc_aligned(1<insert_translation(_mapping.virt_address, + _mapping.phys_address, + _mapping.size_log2, + _mapping.writable, X, K, G, space); + assert(!sl2); + } + /* try to wake up faulter */ + assert(!Kernel::resume_thread(_pagefault.thread_id)); + + /* wait for next page fault */ + wait_for_fault(); +} + diff --git a/base-hw/src/core/signal_session_component.cc b/base-hw/src/core/signal_session_component.cc new file mode 100644 index 0000000000..0add0145db --- /dev/null +++ b/base-hw/src/core/signal_session_component.cc @@ -0,0 +1,79 @@ +/* + * \brief Implementation of the SIGNAL service on the HW-core + * \author Martin Stein + * \date 2012-05-05 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + +enum { + RECEIVER_SLAB_CHUNK_SIZE = 32, + CONTEXT_SLAB_CHUNK_SIZE = 32, +}; + + +Signal_session_component::Signal_session_component(Allocator * const md, + size_t const ram_quota) : + _md_alloc(md, ram_quota), + _receivers_slab(Kernel::signal_receiver_size(), + RECEIVER_SLAB_CHUNK_SIZE * Kernel::signal_receiver_size(), + 0, &_md_alloc), + + _contexts_slab(Kernel::signal_context_size(), + CONTEXT_SLAB_CHUNK_SIZE * Kernel::signal_context_size(), + 0, &_md_alloc) +{ } + + +Signal_session_component::~Signal_session_component() +{ + PERR("%s: Not implemented", __PRETTY_FUNCTION__); + while (1) ; +} + + +Signal_receiver_capability Signal_session_component::alloc_receiver() +{ + /* create receiver kernel-object */ + size_t const s = Kernel::signal_receiver_size(); + void * p; + if (!_receivers_slab.alloc(s, &p)) throw Out_of_metadata(); + unsigned long const id = Kernel::new_signal_receiver(p); + if (!id) throw Out_of_metadata(); + + /* return reference to the new kernel-object */ + Native_capability c(id, 0); + return reinterpret_cap_cast(c); +} + + +Signal_context_capability +Signal_session_component::alloc_context(Signal_receiver_capability r, + unsigned long imprint) +{ + /* create context kernel-object */ + size_t const s = Kernel::signal_context_size(); + void * p; + if (!_contexts_slab.alloc(s, &p)) throw Out_of_metadata(); + unsigned long const id = Kernel::new_signal_context(p, r.dst(), imprint); + if (!id) throw Out_of_metadata(); + + /* return reference to the new kernel-object */ + Native_capability c(id, 0); + return reinterpret_cap_cast(c); +} + diff --git a/base-hw/src/core/target.mk b/base-hw/src/core/target.mk new file mode 100644 index 0000000000..d6ec95498c --- /dev/null +++ b/base-hw/src/core/target.mk @@ -0,0 +1,49 @@ +# +# \brief The core of Genode +# \author Martin Stein +# \date 2011-12-16 +# + +# set program name +TARGET = core + +# use core specific startup library +STARTUP_LIB = startup_core + +# add library dependencies +LIBS += cxx raw_ipc heap process pager lock console signal raw_server \ + syscall startup_core core_support + +# add include paths +GEN_CORE_DIR = $(BASE_DIR)/src/core +INC_DIR += $(REP_DIR)/src/core/include $(REP_DIR)/include \ + $(REP_DIR)/src/platform $(GEN_CORE_DIR)/include \ + $(BASE_DIR)/src/platform $(BASE_DIR)/src/core/include \ + $(BASE_DIR)/include + +# add C++ sources +SRC_CC += main.cc _main.cc ram_session_component.cc \ + ram_session_support.cc rom_session_component.cc \ + pd_session_component.cc io_mem_session_component.cc \ + io_mem_session_support.cc thread.cc platform_pd.cc platform.cc \ + platform_thread.cc dataspace_component.cc rm_session_component.cc \ + io_port_session_component.cc \ + irq_session_component.cc signal_session_component.cc \ + dump_alloc.cc cpu_session_component.cc \ + cpu_session_support.cc console.cc + +# declare file locations +vpath _main.cc $(BASE_DIR)/src/platform +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 dataspace_component.cc $(GEN_CORE_DIR) +vpath dump_alloc.cc $(GEN_CORE_DIR) +vpath console.cc $(REP_DIR)/src/base +vpath % $(REP_DIR)/src/core + diff --git a/base-hw/src/core/thread.cc b/base-hw/src/core/thread.cc new file mode 100644 index 0000000000..16e76a5df0 --- /dev/null +++ b/base-hw/src/core/thread.cc @@ -0,0 +1,92 @@ +/* + * \brief Implementation of Thread API interface for core + * \author Martin Stein + * \date 2012-01-25 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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 + +using namespace Genode; + +extern Genode::Native_utcb * _main_utcb; + +namespace Kernel { unsigned core_id(); } + + +Native_utcb * Thread_base::utcb() +{ + /* this is the main thread */ + if (!this) { return _main_utcb; } + + /* this isn't the main thread */ + return _tid->phys_utcb(); +} + + +/** + * Returns 0 if this is the main thread or the thread base pointer otherwise + */ +Thread_base * Thread_base::myself() +{ + /* get our platform thread wich holds our thread base or 0 */ + Platform_thread * const pt = Kernel::get_thread(); + if (pt) return pt->thread_base(); + + /* we are core main, the only thread beside idle with no platform thread */ + else return 0; +} + + +static void thread_entry() +{ + /* this is never called by a main thread */ + Thread_base::myself()->entry(); +} + + +Thread_base::Thread_base(const char *name, size_t stack_size) +: _list_element(this), _tid(0) +{ + _tid = new (platform()->core_mem_alloc()) + Platform_thread(name, this, stack_size, Kernel::core_id()); +} + + +Thread_base::~Thread_base() +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + + +void Thread_base::start() +{ + size_t const stack_size = _tid->stack_size()/sizeof(unsigned long) + 1; + void * const stack_base = new (platform()->core_mem_alloc()) + unsigned long [stack_size]; + void * sp = (void *)((addr_t)stack_base + _tid->stack_size()); + void * ip = (void *)&thread_entry; + if (_tid->start(ip, sp)) PERR("Couldn't start thread"); +} + + +void Thread_base::cancel_blocking() +{ + kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n"; + while (1) ; +} + diff --git a/base-hw/src/core/vea9x4/kernel_support.h b/base-hw/src/core/vea9x4/kernel_support.h new file mode 100644 index 0000000000..e81e70cd0f --- /dev/null +++ b/base-hw/src/core/vea9x4/kernel_support.h @@ -0,0 +1,21 @@ +/* + * \brief Kernel support specific for the Versatile VEA9X4 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * 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__VEA9X4__KERNEL_SUPPORT_H_ +#define _SRC__CORE__VEA9X4__KERNEL_SUPPORT_H_ + +/* local includes */ +#include + +#endif /* _SRC__CORE__VEA9X4__KERNEL_SUPPORT_H_ */ + diff --git a/base-hw/src/core/vea9x4/platform_support.cc b/base-hw/src/core/vea9x4/platform_support.cc new file mode 100644 index 0000000000..956403e198 --- /dev/null +++ b/base-hw/src/core/vea9x4/platform_support.cc @@ -0,0 +1,83 @@ +/* + * \brief Platform implementations specific for base-hw and VEA9X4 + * \author Martin Stein + * \date 2012-04-27 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file 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; + + +Native_region * Platform::_ram_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { Vea9x4::LOCAL_DDR2_BASE, Vea9x4::LOCAL_DDR2_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_irq_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { 0, Pl390_base::MAX_INTERRUPT_ID + 1 } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_core_only_irq_regions(unsigned const i) +{ + static Native_region _regions[] = + { + /* Core timer */ + { Cortex_a9::PRIVATE_TIMER_IRQ, 1 }, + + /* Core UART */ + { Vea9x4::PL011_0_IRQ, 1 } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_mmio_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { Vea9x4::SMB_CS7_BASE, Vea9x4::SMB_CS7_SIZE }, + { Vea9x4::SMB_CS0_TO_CS6_BASE, Vea9x4::SMB_CS0_TO_CS6_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_core_only_mmio_regions(unsigned const i) +{ + static Native_region _regions[] = + { + /* Core timer and PIC */ + { Vea9x4::CORTEX_A9_PRIVATE_MEM_BASE, + Vea9x4::CORTEX_A9_PRIVATE_MEM_SIZE }, + + /* Core UART */ + { Vea9x4::PL011_0_MMIO_BASE, Vea9x4::PL011_0_MMIO_SIZE } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + diff --git a/base-hw/src/core/vea9x4/software_tlb.h b/base-hw/src/core/vea9x4/software_tlb.h new file mode 100644 index 0000000000..870cfb5bdd --- /dev/null +++ b/base-hw/src/core/vea9x4/software_tlb.h @@ -0,0 +1,26 @@ +/* + * \brief Software TLB controls specific for the Versatile VEA9X4 + * \author Martin Stein + * \date 2012-04-23 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * 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__VEA9X4__SOFTWARE_TLB_H_ +#define _SRC__CORE__VEA9X4__SOFTWARE_TLB_H_ + +/* Genode includes */ +#include + +/** + * Software TLB controls + */ +class Software_tlb : public Genode::Section_table { }; + +#endif /* _SRC__CORE__VEA9X4__SOFTWARE_TLB_H_ */ + diff --git a/base-hw/src/platform/_main_helper.h b/base-hw/src/platform/_main_helper.h new file mode 100644 index 0000000000..8ab6ba5f1d --- /dev/null +++ b/base-hw/src/platform/_main_helper.h @@ -0,0 +1,20 @@ +/* + * \brief Platform-specific helper functions for the _main() function + * \author Martin Stein + * \date 2010-09-13 + */ + +/* + * Copyright (C) 2010-2012 Genode Labs GmbH + * + * 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_ + +static void main_thread_bootstrap() { } + +#endif /* _SRC__PLATFORM__MAIN_HELPER_H_ */ + diff --git a/base-hw/src/platform/crt0.s b/base-hw/src/platform/crt0.s new file mode 100644 index 0000000000..41760d4fdc --- /dev/null +++ b/base-hw/src/platform/crt0.s @@ -0,0 +1,61 @@ +/** + * \brief Startup code for Genode programs on Cortex A9 + * \author Martin Stein + * \date 2011-10-01 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +.section .text + + /* ELF entry symbol */ + .global _start + _start: + + /* zero-fill BSS segment but don't pollute thread-entry arguments */ + .extern _bss_start + ldr r4, =_bss_start + .extern _bss_end + ldr r3, =_bss_end + mov r2, #0 + sub r3, r3, #4 + 1: + str r2, [r4] + add r4, r4, #4 + cmp r4, r3 + bne 1b + + /* fetch thread-entry arguments to their destinations in BSS */ + ldr r1, =_main_utcb + str r0, [r1] + + /* call _main routine */ + ldr sp, =_main_stack_high + .extern _main + bl _main + 1: + b 1b + + /* dynamic symbol object handle */ + .align 3 + .global __dso_handle + __dso_handle: .long 0 + +.section .bss + + /* main-thread stack */ + .align 3 + .space 64*1024 + .global _main_stack_high + _main_stack_high: + + /* main-thread UTCB-pointer for the Genode thread-API */ + .align 3 + .global _main_utcb + _main_utcb: .long 0 + diff --git a/base-hw/src/platform/genode.ld b/base-hw/src/platform/genode.ld new file mode 100644 index 0000000000..02a32dab7a --- /dev/null +++ b/base-hw/src/platform/genode.ld @@ -0,0 +1,123 @@ +/* + * \brief Linker script for Genode programs + * \author Christian Helmuth + * \date 2006-04-12 + */ + +/* + * Copyright (C) 2006-2012 Genode Labs 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 : { + _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 + + .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) } + + /* .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_start = .; + *(.bss .bss.* .gnu.linkonce.b.* COMMON) + } + _bss_end = .; + _prog_img_end = .; + + /DISCARD/ : { + *(.note) + *(.note.ABI-tag) + *(.comment) + } +} diff --git a/base/include/drivers/board/panda_a2.h b/base/include/drivers/board/panda_a2.h index be83c9e092..9771140b7d 100644 --- a/base/include/drivers/board/panda_a2.h +++ b/base/include/drivers/board/panda_a2.h @@ -23,9 +23,11 @@ namespace Genode { enum { - /* buses */ + /* interconnect domains */ L4_PER_BASE = 0x48000000, L4_PER_SIZE = 0x01000000, + L4_CFG_BASE = 0x4a000000, + L4_CFG_SIZE = 0x01000000, /* clocks */ MPU_DPLL_CLOCK = 200*1000*1000,