/* * \brief Protection-domain facility * \author Martin Stein * \date 2010-09-13 */ /* * Copyright (C) 2010-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _SRC__CORE__INCLUDE__PLATFORM_PD_H_ #define _SRC__CORE__INCLUDE__PLATFORM_PD_H_ /* core includes */ #include #include #include namespace Genode { class Platform_pd; typedef Id_allocator Pid_allocator; Pid_allocator *pid_allocator(); class Platform_thread; class Platform_pd { public: typedef unsigned Context_id; typedef Thread_base::Context Context; private: addr_t context_area_base() { return Native_config::context_area_virtual_base(); } addr_t context_area_size() { return Native_config::context_area_virtual_size(); } addr_t context_area_top() { return context_area_base() + context_area_size(); } addr_t context_size() { return Native_config::context_virtual_size(); } addr_t context_base_mask() { return ~(Native_config::context_virtual_size() - 1); } addr_t context_offset_mask() { return ~context_base_mask(); } enum { CONTEXT_SIZE = Native_config::CONTEXT_VIRTUAL_SIZE, CONTEXT_AREA_SIZE = Native_config::CONTEXT_AREA_VIRTUAL_SIZE, MAX_CONTEXT_ID = CONTEXT_AREA_SIZE / CONTEXT_SIZE - 1 }; Native_process_id _pid; Native_thread_id owner_tid_by_context_id[MAX_CONTEXT_ID + 1]; void _free_context(Native_thread_id const & t) { for (Context_id cid = 0; cid <= MAX_CONTEXT_ID; cid++) { if (owner_tid_by_context_id[cid] == t) { owner_tid_by_context_id[cid] = 0; } } } public: /** * Constructors */ Platform_pd(signed pid = 0, bool create = true) : _pid(pid) { static bool const verbose = false; if ((unsigned)User::MAX_THREAD_ID>(unsigned)MAX_CONTEXT_ID) { PERR("More threads allowed than context areas available"); return; } if (!_pid) _pid=pid_allocator()->allocate(this); if (!_pid) { PERR("Allocating new Process ID failed"); return; } if (verbose) PDBG("Create protection domain %i", (unsigned int)_pid); } /** * Destructor */ ~Platform_pd() { } enum Context_part{ NO_CONTEXT_PART = 0, MISC_AREA = 1, UTCB_AREA = 2, STACK_AREA = 3 }; bool cid_if_context_address(addr_t a, Context_id* cid) { if (a < context_area_base() || a >= context_area_top()) return false; addr_t context_base = a & context_base_mask(); *cid = (Context_id)((context_base-context_area_base()) / context_size()); return true; } Context *context(Context_id i) { return (Context*)(context_area_base()+(i+1)*context_size()-sizeof(Context)); } Context *context_by_tid(Native_thread_id tid) { for (unsigned cid = 0; cid <= MAX_CONTEXT_ID; cid++) if (owner_tid_by_context_id[cid] == tid) return context(cid); return 0; } bool metadata_if_context_address(addr_t a, Native_thread_id *context_owner_tid, Context_part *cp, unsigned *stack_offset) { Context_id cid; if (!cid_if_context_address(a, &cid)) return false; if (cid > MAX_CONTEXT_ID) { PERR("Context ID %i out of range", (unsigned int)cid); return false; } *context_owner_tid = owner_tid_by_context_id[cid]; if (!*context_owner_tid) { if (_pid == Roottask::PROTECTION_ID) PERR("Context %p is not in use", (void*)a); return false; } addr_t offset = a & context_offset_mask(); Context *context = (Context *)(context_size() - sizeof(Context)); if ((void*)offset >= &context->utcb) { *cp = UTCB_AREA; } else if ((void*)offset < &context->stack) { *cp = STACK_AREA; *stack_offset = (((unsigned)&(context->stack)) - (unsigned)offset); } else { *cp = MISC_AREA; } return true; } bool allocate_context(Native_thread_id tid, Context_id cid) { static bool const verbose = false; if (cid > MAX_CONTEXT_ID) return 0; if (owner_tid_by_context_id[cid]){ PERR("Context is already in use"); return false; } owner_tid_by_context_id[cid] = tid; if (verbose) PDBG("Thread %i owns Context %i (0x%p) of PD %i", tid, cid, context(cid), _pid); return true; } Context *allocate_context(Native_thread_id tid) { static bool const verbose = false; /* * First thread is assumed to be the main thread and gets last * context-area by convention */ if (!owner_tid_by_context_id[MAX_CONTEXT_ID]){ owner_tid_by_context_id[MAX_CONTEXT_ID] = tid; if (verbose) PDBG("Thread %i owns Context %i (0x%p) of Protection Domain %i", tid, MAX_CONTEXT_ID, context(MAX_CONTEXT_ID), _pid); return context(MAX_CONTEXT_ID); } for (unsigned i = 0; i <= MAX_CONTEXT_ID - 1; i++) { if (!owner_tid_by_context_id[i]) { owner_tid_by_context_id[i] = tid; if (verbose) PDBG("Thread %i owns Context %i (0x%p) of Protection Domain %i", tid, MAX_CONTEXT_ID, context(MAX_CONTEXT_ID), _pid); return context(i); } } return 0; } /** * Bind thread to protection domain * * \return 0 on success */ inline int bind_thread(Platform_thread* pt) { Context *context = allocate_context(pt->tid()); if (!context) { PERR("Context allocation failed"); return -1; } Native_utcb *utcb = &context->utcb; pt->_assign_physical_thread(_pid, utcb, this); return 0; } /** * Unbind thread from protection domain * * Free the thread's slot and update thread object. */ inline void unbind_thread(Platform_thread *pt) { _free_context(pt->tid()); } /** * Free a context so it is allocatable again * \param c PD wide unique context ID */ void free_context(Context_id const & c) { if (c > MAX_CONTEXT_ID) { return; } owner_tid_by_context_id[c] = Kernel::INVALID_THREAD_ID; } /** * Assign parent interface to protection domain */ inline int assign_parent(Native_capability parent) { return 0; } }; } #endif /* _SRC__CORE__INCLUDE__PLATFORM_PD_H */