/*
 * \brief  Stack layout and organization
 * \author Norman Feske
 * \date   2006-04-28
 *
 * For storing thread-specific data such as the stack and thread-local data,
 * there is a dedicated portion of the virtual address space. This portion is
 * called stack area. Within this area, each thread has
 * a fixed-sized slot. The layout of each slot looks as follows
 *
 * ; lower address
 * ;   ...
 * ;   ============================ <- aligned at the slot size
 * ;
 * ;             empty
 * ;
 * ;   ----------------------------
 * ;
 * ;             stack
 * ;             (top)              <- initial stack pointer
 * ;   ---------------------------- <- address of 'Stack' object
 * ;       thread-specific data
 * ;   ----------------------------
 * ;              UTCB
 * ;   ============================ <- aligned at the slot size
 * ;   ...
 * ; higher address
 *
 * On some platforms, a user-level thread-control block (UTCB) contains
 * data shared between the user-level thread and the kernel. It is typically
 * used for transferring IPC message payload or for system-call arguments.
 * The additional stack members are a reference to the corresponding
 * 'Thread' object and the name of the thread.
 *
 * The stack area is a virtual memory area, initially not backed by real
 * memory. When a new thread is created, an empty slot gets assigned to the new
 * thread and populated with memory pages for the stack and thread-specific
 * data. Note that this memory is allocated from the RAM session of the
 * component environment and not accounted for when using the 'sizeof()'
 * operand on a 'Thread' object.
 *
 * A thread may be associated with more than one stack. Additional secondary
 * stacks can be associated with a thread, and used for user level scheduling.
 */

/*
 * Copyright (C) 2006-2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

#ifndef _INCLUDE__BASE__INTERNAL__STACK_H_
#define _INCLUDE__BASE__INTERNAL__STACK_H_

/* Genode includes */
#include <base/thread.h>
#include <cpu/consts.h>
#include <cpu_session/cpu_session.h>  /* for 'Cpu_session::Name' type */

/* base-internal includes */
#include <base/internal/native_utcb.h>
#include <base/internal/native_thread.h>

namespace Genode { class Stack; }


/**
 * Stack located within the stack area
 *
 * The end of a stack is placed virtual size aligned.
 */
class Genode::Stack
{
	public:

		using Name        = Cpu_session::Name;
		using Size_result = Thread::Stack_size_result;
		using Error       = Thread::Stack_error;

		struct Mappings
		{
			struct Entry
			{
				/**
				 * Virtual address of the start of the stack
				 *
				 * This address is pointing to the begin of the dataspace used
				 * for backing the stack except for the UTCB (which is managed
				 * by the kernel).
				 */
				addr_t base;

				/**
				 * Dataspace containing the backing store for the stack
				 *
				 * We keep the dataspace capability to be able to release the
				 * backing store on thread destruction.
				 */
				Ram_dataspace_capability ds_cap;
			};

			static constexpr unsigned MAX = 4;

			Entry _entries[MAX] { };

			void for_each(auto const &fn)
			{
				for (Entry &e : _entries)
					if (e.base) fn(e.base, e.ds_cap);
			}

			auto alloc(auto const &fn, auto const &exhausted_fn) -> decltype(exhausted_fn())
			{
				for (Entry &e : _entries)
					if (!e.base)
						return fn(e);

				return exhausted_fn();
			}
		};

	private:

		/**
		 * Top of the stack is accessible via stack_top()
		 *
		 * Context provides the first word of the stack to prevent the
		 * overlapping of stack top and the 'stack_base' member.
		 */
		addr_t _stack[1];

		/*
		 * TLS pointer for libc pthreads
		 */
		addr_t _libc_tls_pointer { };

		/**
		 * Thread name, used for debugging
		 */
		Name const _name;

		/**
		 * Pointer to corresponding 'Thread' object
		 */
		Thread &_thread;

		Mappings _mappings { };

		addr_t _base;

		/**
		 * Kernel-specific thread meta data
		 */
		Native_thread _native_thread { };

		/*
		 * <- end of regular memory area
		 *
		 * The following part of the stack is backed by kernel-managed memory.
		 * No member variables are allowed beyond this point.
		 */

		/**
		 * Kernel-specific user-level thread control block
		 */
		Native_utcb _utcb { };

	public:

		/**
		 * Constructor
		 */
		Stack(Name const &name, Thread &thread, Mappings::Entry mapping)
		:
			_name(name), _thread(thread), _base(mapping.base)
		{
			_mappings._entries[0] = mapping;
		}

		/**
		 * Top of stack
		 *
		 * The alignment constrains are enforced by the CPU-specific ABI.
		 */
		addr_t top() const { return Abi::stack_align(addr_t(_stack)); }

		/**
		 * Return base (the "end") of stack
		 */
		addr_t base() const { return _base; }

		/**
		 * Return libc TLS pointer offset relative to end of stack
		 */
		addr_t libc_tls_pointer_offset() const
		{
			return addr_t(this) + sizeof(Stack) - addr_t(&_libc_tls_pointer);
		}

		/**
		 * Ensure that the stack has a given minimum size
		 *
		 * \param size  minimum stack size
		 * \return      actually available stack size
		 */
		Size_result size(size_t const size);

		/**
		 * Return kernel-specific thread meta data
		 */
		Native_thread &native_thread() { return _native_thread; }

		/**
		 * Return UTCB of the stack's thread
		 */
		Native_utcb &utcb() { return _utcb; }

		/**
		 * Return thread name
		 */
		Name name() const { return _name; }

		/**
		 * Return 'Thread' object of the stack's thread
		 */
		Thread &thread() { return _thread; }

		/**
		 * Return RAM mappings used as the stack's backing storage
		 */
		Mappings mappings() { return _mappings; }
};

#endif /* _INCLUDE__BASE__INTERNAL__STACK_H_ */
