From e6ad346e24d527d2c19c9bf7743fec7b5d77081f Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 5 May 2015 00:15:50 +0200 Subject: [PATCH] sel4: management of core's virtual memory --- repos/base-sel4/src/core/context_area.cc | 149 +++++++++++ repos/base-sel4/src/core/include/cnode.h | 32 +-- .../base-sel4/src/core/include/core_cspace.h | 53 ++++ repos/base-sel4/src/core/include/map_local.h | 8 +- .../src/core/include/page_table_registry.h | 177 +++++++++++++ repos/base-sel4/src/core/include/platform.h | 62 +++++ .../src/core/include/untyped_address.h | 6 +- .../src/core/include/untyped_memory.h | 127 +++++++++ repos/base-sel4/src/core/include/util.h | 5 + repos/base-sel4/src/core/include/vm_space.h | 215 +++++++++++++++ repos/base-sel4/src/core/platform.cc | 246 +++++++++--------- .../base-sel4/src/core/ram_session_support.cc | 11 +- repos/base-sel4/src/core/target.inc | 1 - 13 files changed, 943 insertions(+), 149 deletions(-) create mode 100644 repos/base-sel4/src/core/context_area.cc create mode 100644 repos/base-sel4/src/core/include/core_cspace.h create mode 100644 repos/base-sel4/src/core/include/page_table_registry.h create mode 100644 repos/base-sel4/src/core/include/untyped_memory.h create mode 100644 repos/base-sel4/src/core/include/vm_space.h diff --git a/repos/base-sel4/src/core/context_area.cc b/repos/base-sel4/src/core/context_area.cc new file mode 100644 index 000000000..7818412e9 --- /dev/null +++ b/repos/base-sel4/src/core/context_area.cc @@ -0,0 +1,149 @@ +/* + * \brief Support code for the thread API + * \author Norman Feske + * \author Stefan Kalkowski + * \date 2010-01-13 + */ + +/* + * Copyright (C) 2010-2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include + +using namespace Genode; + + +/** + * Region-manager session for allocating thread contexts + * + * This class corresponds to the managed dataspace that is normally + * used for organizing thread contexts with the thread context area. + * In contrast to the ordinary implementation, core's version does + * not split between allocation of memory and virtual memory management. + * Due to the missing availability of "real" dataspaces and capabilities + * refering to it without having an entrypoint in place, the allocation + * of a dataspace has no effect, but the attachment of the thereby "empty" + * dataspace is doing both: allocation and attachment. + */ +class Context_area_rm_session : public Rm_session +{ + private: + + using Ds_slab = Synchronized_allocator >; + + Ds_slab _ds_slab { platform()->core_mem_alloc() }; + + enum { verbose = false }; + + public: + + /** + * Allocate and attach on-the-fly backing store to thread-context area + */ + Local_addr attach(Dataspace_capability ds_cap, /* ignored capability */ + size_t size, off_t offset, + bool use_local_addr, Local_addr local_addr, + bool executable) + { + size = round_page(size); + + /* allocate physical memory */ + Untyped_address const untyped_addr = + Untyped_memory::alloc(*platform_specific()->ram_alloc(), size); + + Untyped_memory::convert_to_page_frames(untyped_addr.phys(), + size >> get_page_size_log2()); + + Dataspace_component *ds = new (&_ds_slab) + Dataspace_component(size, 0, untyped_addr.phys(), CACHED, true, 0); + if (!ds) { + PERR("dataspace for core context does not exist"); + return (addr_t)0; + } + + addr_t const core_local_addr = + Native_config::context_area_virtual_base() + (addr_t)local_addr; + + if (verbose) + PDBG("core_local_addr = %lx, phys_addr = %lx, size = 0x%zx", + core_local_addr, ds->phys_addr(), ds->size()); + + if (!map_local(ds->phys_addr(), core_local_addr, + ds->size() >> get_page_size_log2())) { + PERR("could not map phys %lx at local %lx", + ds->phys_addr(), core_local_addr); + return (addr_t)0; + } + + ds->assign_core_local_addr((void*)core_local_addr); + + return local_addr; + } + + void detach(Local_addr local_addr) { PWRN("Not implemented!"); } + + Pager_capability add_client(Thread_capability) { + return Pager_capability(); } + + void remove_client(Pager_capability) { } + + void fault_handler(Signal_context_capability) { } + + State state() { return State(); } + + Dataspace_capability dataspace() { return Dataspace_capability(); } +}; + + +class Context_area_ram_session : public Ram_session +{ + public: + + Ram_dataspace_capability alloc(size_t size, Cache_attribute cached) { + return reinterpret_cap_cast(Native_capability()); } + + void free(Ram_dataspace_capability ds) { + PWRN("Not implemented!"); } + + int ref_account(Ram_session_capability ram_session) { return 0; } + + int transfer_quota(Ram_session_capability ram_session, size_t amount) { + return 0; } + + size_t quota() { return 0; } + + size_t used() { return 0; } +}; + + +/** + * Return single instance of the context-area RM and RAM session + */ +namespace Genode { + + Rm_session *env_context_area_rm_session() + { + static Context_area_rm_session inst; + return &inst; + } + + Ram_session *env_context_area_ram_session() + { + static Context_area_ram_session inst; + return &inst; + } +} diff --git a/repos/base-sel4/src/core/include/cnode.h b/repos/base-sel4/src/core/include/cnode.h index 550570e24..204d34570 100644 --- a/repos/base-sel4/src/core/include/cnode.h +++ b/repos/base-sel4/src/core/include/cnode.h @@ -15,11 +15,12 @@ #define _CORE__INCLUDE__CNODE_H_ /* Genode includes */ +#include #include #include /* core includes */ -#include +#include namespace Genode { @@ -100,17 +101,17 @@ class Genode::Cnode_base }; -class Genode::Cnode : public Cnode_base +class Genode::Cnode : public Cnode_base, Noncopyable { public: - class Phys_alloc_failed : Exception { }; class Untyped_lookup_failed : Exception { }; class Retype_untyped_failed : Exception { }; /** * Constructor * + * \param parent selector of CNode where to place 'dst_sel' * \param dst_sel designated selector referring to the created * CNode * \param size_log2 number of entries in CNode @@ -120,34 +121,19 @@ class Genode::Cnode : public Cnode_base * \throw Phys_alloc_failed * \throw Untyped_address::Lookup_failed */ - Cnode(unsigned dst_sel, size_t size_log2, Range_allocator &phys_alloc) + Cnode(unsigned parent_sel, unsigned dst_sel, size_t size_log2, + Range_allocator &phys_alloc) : Cnode_base(dst_sel, size_log2) { - /* - * Allocate natually-aligned physical memory for cnode - * - * The natual alignment is needed to ensure that the backing store is - * contained in a single untyped memory region. - */ - void *out_ptr = nullptr; - size_t const mem_size = 1UL << mem_size_log2(); - Range_allocator::Alloc_return alloc_ret = - phys_alloc.alloc_aligned(mem_size, &out_ptr, mem_size_log2()); - addr_t const phys_addr = (addr_t)out_ptr; - - if (alloc_ret.is_error()) { - PERR("%s: allocation of backing store for cnode failed", __FUNCTION__); - throw Phys_alloc_failed(); - } - - Untyped_address const untyped_addr(phys_addr, mem_size); + Untyped_address const untyped_addr = + Untyped_memory::alloc_log2(phys_alloc, mem_size_log2()); seL4_Untyped const service = untyped_addr.sel(); int const type = seL4_CapTableObject; int const offset = untyped_addr.offset(); int const size_bits = size_log2; - seL4_CNode const root = seL4_CapInitThreadCNode; + seL4_CNode const root = parent_sel; int const node_index = 0; int const node_depth = 0; int const node_offset = dst_sel; diff --git a/repos/base-sel4/src/core/include/core_cspace.h b/repos/base-sel4/src/core/include/core_cspace.h new file mode 100644 index 000000000..c2b2558c4 --- /dev/null +++ b/repos/base-sel4/src/core/include/core_cspace.h @@ -0,0 +1,53 @@ +/* + * \brief Core's CSpace layout definition + * \author Norman Feske + * \date 2015-05-06 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CORE__INCLUDE__CORE_CSPACE_H_ +#define _CORE__INCLUDE__CORE_CSPACE_H_ + +namespace Genode { class Core_cspace; } + + +class Genode::Core_cspace +{ + public: + + /* CNode dimensions */ + enum { + NUM_TOP_SEL_LOG2 = 12UL, + NUM_CORE_SEL_LOG2 = 14UL, + NUM_PHYS_SEL_LOG2 = 20UL, + + NUM_CORE_PAD_SEL_LOG2 = 32UL - NUM_TOP_SEL_LOG2 - NUM_CORE_SEL_LOG2, + }; + + /* selectors for statically created CNodes */ + enum Static_cnode_sel { + TOP_CNODE_SEL = 0x200, + CORE_PAD_CNODE_SEL = 0x201, + CORE_CNODE_SEL = 0x202, + PHYS_CNODE_SEL = 0x203, + CORE_VM_PAD_CNODE_SEL = 0x204, + CORE_VM_CNODE_SEL = 0x205, + }; + + /* indices within top-level CNode */ + enum Top_cnode_idx { + TOP_CNODE_CORE_IDX = 0, + TOP_CNODE_PHYS_IDX = 0xfff + }; + + enum { CORE_VM_ID = 1 }; +}; + + +#endif /* _CORE__INCLUDE__CORE_CSPACE_H_ */ diff --git a/repos/base-sel4/src/core/include/map_local.h b/repos/base-sel4/src/core/include/map_local.h index c5f6b1e0c..9597cb935 100644 --- a/repos/base-sel4/src/core/include/map_local.h +++ b/repos/base-sel4/src/core/include/map_local.h @@ -19,6 +19,7 @@ /* core includes */ #include +#include namespace Genode { @@ -34,9 +35,12 @@ namespace Genode { */ inline bool map_local(addr_t from_phys, addr_t to_virt, size_t num_pages) { - PDBG("not implemented"); + PDBG("map_local from_phys=0x%lx, to_virt=0x%lx, num_pages=%zd", + from_phys, to_virt, num_pages); - return false; + platform_specific()->core_vm_space().map(from_phys, to_virt, num_pages); + + return true; } diff --git a/repos/base-sel4/src/core/include/page_table_registry.h b/repos/base-sel4/src/core/include/page_table_registry.h new file mode 100644 index 000000000..52c6937d3 --- /dev/null +++ b/repos/base-sel4/src/core/include/page_table_registry.h @@ -0,0 +1,177 @@ +/* + * \brief Associate page-table and frame selectors with virtual addresses + * \author Norman Feske + * \date 2015-05-04 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * 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__PAGE_TABLE_REGISTRY_H_ +#define _CORE__INCLUDE__PAGE_TABLE_REGISTRY_H_ + +/* Genode includes */ +#include +#include +#include + +/* core includes */ +#include + +namespace Genode { class Page_table_registry; } + + +class Genode::Page_table_registry +{ + public: + + class Lookup_failed : Exception { }; + + private: + + /* + * XXX use AVL tree (with virtual address as key) instead of list + */ + + class Page_table : public List::Element + { + public: + + struct Entry : List::Element + { + addr_t const addr; + unsigned const sel; + + Entry(addr_t addr, unsigned sel) : addr(addr), sel(sel) { } + }; + + addr_t const addr; + + private: + + List _entries; + + static addr_t _page_frame_base(addr_t addr) + { + return addr & get_page_mask(); + } + + bool _entry_exists(addr_t addr) const + { + for (Entry const *e = _entries.first(); e; e = e->next()) { + if (_page_frame_base(e->addr) == _page_frame_base(addr)) + return true; + } + return false; + } + + public: + + Page_table(addr_t addr) : addr(addr) { } + + void insert_entry(Allocator &entry_slab, addr_t addr, unsigned sel) + { + if (_entry_exists(addr)) { + PWRN("trying to insert page frame for 0x%lx twice", addr); + return; + } + + _entries.insert(new (entry_slab) Entry(addr, sel)); + } + }; + + class Slab_block : public Genode::Slab_block { long _data[4*1024]; }; + + template + struct Slab : Genode::Tslab + { + Slab_block _initial_block; + + Slab(Allocator &md_alloc) + : + Tslab(&md_alloc, &_initial_block) + { } + }; + + Slab _page_table_slab; + Slab _page_table_entry_slab; + + List _page_tables; + + static addr_t _page_table_base(addr_t addr) + { + return addr & ~(4*1024*1024 - 1); + } + + bool _page_table_exists(addr_t addr) const + { + for (Page_table const *pt = _page_tables.first(); pt; pt = pt->next()) { + if (_page_table_base(pt->addr) == _page_table_base(addr)) + return true; + } + return false; + } + + Page_table &_lookup(addr_t addr) + { + for (Page_table *pt = _page_tables.first(); pt; pt = pt->next()) { + if (_page_table_base(pt->addr) == _page_table_base(addr)) + return *pt; + } + PDBG("page-table lookup failed"); + throw Lookup_failed(); + } + + public: + + /** + * Constructor + * + * \param md_alloc backing store allocator for metadata + */ + Page_table_registry(Allocator &md_alloc) + : + _page_table_slab(md_alloc), + _page_table_entry_slab(md_alloc) + { } + + /** + * Register page table + * + * \param addr virtual address + * \param sel page-table selector + */ + void insert_page_table(addr_t addr, unsigned sel) + { + if (_page_table_exists(addr)) { + PWRN("trying to insert page table for 0x%lx twice", addr); + return; + } + + _page_tables.insert(new (_page_table_slab) Page_table(addr)); + } + + bool has_page_table_at(addr_t addr) const + { + return _page_table_exists(addr); + } + + /** + * Register page table entry + * + * \param addr virtual address + * \param sel page frame selector + * + * \throw Lookup_failed no page table for given address + */ + void insert_page_table_entry(addr_t addr, unsigned sel) + { + _lookup(addr).insert_entry(_page_table_entry_slab, addr, sel); + } +}; + +#endif /* _CORE__INCLUDE__PAGE_TABLE_REGISTRY_H_ */ diff --git a/repos/base-sel4/src/core/include/platform.h b/repos/base-sel4/src/core/include/platform.h index 6a9e6e02a..ea652736e 100644 --- a/repos/base-sel4/src/core/include/platform.h +++ b/repos/base-sel4/src/core/include/platform.h @@ -20,6 +20,8 @@ /* local includes */ #include #include +#include +#include namespace Genode { class Platform; } @@ -36,12 +38,67 @@ class Genode::Platform : public Platform_generic Phys_allocator _irq_alloc; /* IRQ allocator */ Rom_fs _rom_fs; /* ROM file system */ + /** + * Shortcut for physical memory allocator + */ + Range_allocator &_phys_alloc = *_core_mem_alloc.phys_alloc(); + /** * Virtual address range usable by non-core processes */ addr_t _vm_base; size_t _vm_size; + /** + * Initialize core allocators + */ + void _init_allocators(); + bool _init_allocators_done; + + /* + * Until this point, no interaction with the seL4 kernel was needed. + * However, the next steps involve the invokation of system calls and + * the use of kernel services. To use the kernel bindings, we first + * need to initialize the TLS mechanism that is used to find the IPC + * buffer for the calling thread. + */ + bool _init_sel4_ipc_buffer_done; + + /* allocate 1st-level CNode */ + Cnode _top_cnode { seL4_CapInitThreadCNode, Core_cspace::TOP_CNODE_SEL, + Core_cspace::NUM_TOP_SEL_LOG2, _phys_alloc }; + + /* allocate 2nd-level CNode to align core's CNode with the LSB of the CSpace*/ + Cnode _core_pad_cnode { seL4_CapInitThreadCNode, Core_cspace::CORE_PAD_CNODE_SEL, + Core_cspace::NUM_CORE_PAD_SEL_LOG2, + _phys_alloc }; + + /* allocate 3rd-level CNode for core's objects */ + Cnode _core_cnode { seL4_CapInitThreadCNode, Core_cspace::CORE_CNODE_SEL, + Core_cspace::NUM_CORE_SEL_LOG2, _phys_alloc }; + + /* allocate 2nd-level CNode for storing page-frame cap selectors */ + Cnode _phys_cnode { seL4_CapInitThreadCNode, Core_cspace::PHYS_CNODE_SEL, + Core_cspace::NUM_PHYS_SEL_LOG2, _phys_alloc }; + + /** + * Replace initial CSpace with custom CSpace layout + */ + void _switch_to_core_cspace(); + bool _switch_to_core_cspace_done; + + Page_table_registry _core_page_table_registry; + + /** + * Pre-populate core's '_page_table_registry' with the information + * about the initial page tables and page frames as set up by the + * kernel + */ + void _init_core_page_table_registry(); + bool _init_core_page_table_registry_done; + + Vm_space _core_vm_space; + int _init_rom_fs(); public: @@ -66,6 +123,11 @@ class Genode::Platform : public Platform_generic size_t vm_size() const { return _vm_size; } Rom_fs *rom_fs() { return &_rom_fs; } + Cnode &phys_cnode() { return _phys_cnode; } + Cnode &top_cnode() { return _top_cnode; } + + Vm_space &core_vm_space() { return _core_vm_space; } + void wait_for_exit(); }; diff --git a/repos/base-sel4/src/core/include/untyped_address.h b/repos/base-sel4/src/core/include/untyped_address.h index d31ad5069..05bc76488 100644 --- a/repos/base-sel4/src/core/include/untyped_address.h +++ b/repos/base-sel4/src/core/include/untyped_address.h @@ -15,7 +15,6 @@ #define _CORE__INCLUDE__UNTYPED_ADDRESS_H_ /* seL4 includes */ -#include #include namespace Genode { struct Untyped_address; } @@ -44,11 +43,11 @@ class Genode::Untyped_address seL4_Untyped _sel = 0; addr_t _offset = 0; + addr_t _phys = 0; void _init(seL4_BootInfo const &bi, addr_t phys_addr, size_t size, unsigned const start_idx, unsigned const num_idx) { - for (unsigned i = start_idx; i < start_idx + num_idx; i++) { /* index into 'untypedPaddrList' and 'untypedSizeBitsList' */ @@ -78,6 +77,8 @@ class Genode::Untyped_address */ Untyped_address(addr_t phys_addr, size_t size) { + _phys = phys_addr; + seL4_BootInfo const &bi = sel4_boot_info(); _init(bi, phys_addr, size, bi.untyped.start, bi.untyped.end - bi.untyped.start); @@ -92,6 +93,7 @@ class Genode::Untyped_address unsigned sel() const { return _sel; } addr_t offset() const { return _offset; } + addr_t phys() const { return _phys; } }; diff --git a/repos/base-sel4/src/core/include/untyped_memory.h b/repos/base-sel4/src/core/include/untyped_memory.h new file mode 100644 index 000000000..11ab03b64 --- /dev/null +++ b/repos/base-sel4/src/core/include/untyped_memory.h @@ -0,0 +1,127 @@ +/* + * \brief Utilities for dealing with untyped memory + * \author Norman Feske + * \date 2015-05-06 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * 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__UNTYPED_MEMORY_H_ +#define _CORE__INCLUDE__UNTYPED_MEMORY_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include + +/* seL4 includes */ +#include + +namespace Genode { struct Untyped_memory; } + +struct Genode::Untyped_memory +{ + class Phys_alloc_failed : Exception { }; + + + /** + * Allocate natually-aligned physical memory for seL4 kernel object + * + * \throw Phys_alloc_failed + */ + static inline Untyped_address alloc_log2(Range_allocator &phys_alloc, + size_t const size_log2) + { + /* + * The natual alignment is needed to ensure that the backing store is + * contained in a single untyped memory region. + */ + void *out_ptr = nullptr; + size_t const size = 1UL << size_log2; + Range_allocator::Alloc_return alloc_ret = + phys_alloc.alloc_aligned(size, &out_ptr, size_log2); + addr_t const phys_addr = (addr_t)out_ptr; + + if (alloc_ret.is_error()) { + PERR("%s: allocation of untyped memory failed", __FUNCTION__); + throw Phys_alloc_failed(); + } + + return Untyped_address(phys_addr, size); + } + + + /** + * Allocate natually aligned physical memory + * + * \param size size in bytes + */ + static inline Untyped_address alloc(Range_allocator &phys_alloc, + size_t const size) + { + if (size == 0) { + PERR("%s: invalid size of 0x%zd", __FUNCTION__, size); + throw Phys_alloc_failed(); + } + + /* calculate next power-of-two size that fits the allocation size */ + size_t const size_log2 = log2(size - 1) + 1; + + return alloc_log2(phys_alloc, size_log2); + } + + + /** + * Create page frames from untyped memory + */ + static inline void convert_to_page_frames(addr_t phys_addr, + size_t num_pages) + { + size_t const size = num_pages << get_page_size_log2(); + + /* align allocation offset to page boundary */ + Untyped_address const untyped_address(align_addr(phys_addr, 12), size); + + seL4_Untyped const service = untyped_address.sel(); + int const type = seL4_IA32_4K; + int const offset = untyped_address.offset(); + int const size_bits = 0; + seL4_CNode const root = Core_cspace::TOP_CNODE_SEL; + int const node_index = Core_cspace::TOP_CNODE_PHYS_IDX; + int const node_depth = Core_cspace::NUM_TOP_SEL_LOG2; + int const node_offset = phys_addr >> get_page_size_log2(); + int const num_objects = num_pages; + + PDBG("create frame idx %x", node_offset); + + int const ret = seL4_Untyped_RetypeAtOffset(service, + type, + offset, + size_bits, + root, + node_index, + node_depth, + node_offset, + num_objects); + + if (ret != 0) { + PERR("%s: seL4_Untyped_RetypeAtOffset (IA32_4K) returned %d", + __FUNCTION__, ret); + } + } + + static inline unsigned frame_sel(addr_t phys_addr) + { + return (Core_cspace::TOP_CNODE_PHYS_IDX << Core_cspace::NUM_PHYS_SEL_LOG2) + | (phys_addr >> get_page_size_log2()); + } +}; + +#endif /* _CORE__INCLUDE__UNTYPED_MEMORY_H_ */ diff --git a/repos/base-sel4/src/core/include/util.h b/repos/base-sel4/src/core/include/util.h index 66c61b0e6..9c7f52a30 100644 --- a/repos/base-sel4/src/core/include/util.h +++ b/repos/base-sel4/src/core/include/util.h @@ -18,6 +18,10 @@ #include #include +/* core includes */ +#include + + namespace Genode { constexpr size_t get_page_size_log2() { return 12; } @@ -29,6 +33,7 @@ namespace Genode { inline addr_t map_src_addr(addr_t core_local, addr_t phys) { return phys; } inline size_t constrain_map_size_log2(size_t size_log2) { return get_page_size_log2(); } + inline void print_page_fault(const char *msg, addr_t pf_addr, addr_t pf_ip, Rm_session::Fault_type pf_type, unsigned long faulter_badge) diff --git a/repos/base-sel4/src/core/include/vm_space.h b/repos/base-sel4/src/core/include/vm_space.h new file mode 100644 index 000000000..984da72b5 --- /dev/null +++ b/repos/base-sel4/src/core/include/vm_space.h @@ -0,0 +1,215 @@ +/* + * \brief Virtual-memory space + * \author Norman Feske + * \date 2015-05-04 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * 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__VM_SPACE_H_ +#define _CORE__INCLUDE__VM_SPACE_H_ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include + +namespace Genode { class Vm_space; } + + +class Genode::Vm_space +{ + private: + + Page_table_registry &_page_table_registry; + + unsigned const _id; + + Range_allocator &_phys_alloc; + + /** + * Maximum number of page tables and page frames for the VM space + */ + enum { NUM_VM_SEL_LOG2 = 13 }; + + Cnode &_top_level_cnode; + Cnode &_phys_cnode; + + /** + * 2nd-level CNode for aligning '_vm_cnode' with the LSB of the CSpace + */ + Cnode _vm_pad_cnode; + + /** + * 3rd-level CNode for storing page-table and page-frame capabilities + */ + Cnode _vm_cnode; + + /** + * Allocator for the selectors within '_vm_cnode' + */ + Bit_allocator<1UL << NUM_VM_SEL_LOG2> _sel_alloc; + + /** + * Return selector for a capability slot within '_vm_cnode' + */ + unsigned _idx_to_sel(unsigned idx) const { return (_id << 20) | idx; } + + void _map_page(addr_t from_phys, addr_t to_virt) + { + /* allocate page-table entry selector */ + unsigned pte_idx = _sel_alloc.alloc(); + + /* + * Copy page-frame selector to pte_sel + * + * This is needed because each page-frame selector can be + * inserted into only a single page table. + */ + _vm_cnode.copy(_phys_cnode, from_phys >> get_page_size_log2(), pte_idx); + + /* remember relationship between pte_sel and the virtual address */ + _page_table_registry.insert_page_table_entry(to_virt, pte_idx); + + /* + * Insert copy of page-frame selector into page table + */ + { + seL4_IA32_Page const service = _idx_to_sel(pte_idx); + seL4_IA32_PageDirectory const pd = seL4_CapInitThreadPD; + seL4_Word const vaddr = to_virt; + seL4_CapRights const rights = seL4_AllRights; + seL4_IA32_VMAttributes const attr = seL4_IA32_Default_VMAttributes; + + int const ret = seL4_IA32_Page_Map(service, pd, vaddr, rights, attr); + + if (ret != 0) + PERR("seL4_IA32_Page_Map to 0x%lx returned %d", + (unsigned long)vaddr, ret); + } + } + + void _map_page_table(unsigned pt_sel, addr_t to_virt) + { + seL4_IA32_PageTable const service = pt_sel; + seL4_IA32_PageDirectory const pd = seL4_CapInitThreadPD; + seL4_Word const vaddr = to_virt; + seL4_IA32_VMAttributes const attr = seL4_IA32_Default_VMAttributes; + + int const ret = seL4_IA32_PageTable_Map(service, pd, vaddr, attr); + + if (ret != 0) + PDBG("seL4_IA32_PageTable_Map returned %d", ret); + } + + class Alloc_page_table_failed : Exception { }; + + /** + * Allocate and install page table at given virtual address + * + * \throw Alloc_page_table_failed + */ + void _alloc_and_map_page_table(addr_t to_virt) + { + /* allocate page-table selector */ + unsigned const pt_idx = _sel_alloc.alloc(); + + /* XXX account the consumed backing store */ + + /* allocate backing store for page table */ + size_t const pt_mem_size_log2 = 12; + Untyped_address const untyped_addr = + Untyped_memory::alloc_log2(_phys_alloc, pt_mem_size_log2); + + seL4_Untyped const service = untyped_addr.sel(); + int const type = seL4_IA32_PageTableObject; + int const offset = untyped_addr.offset(); + int const size_bits = pt_mem_size_log2; + seL4_CNode const root = _vm_cnode.sel(); + int const node_index = 0; + int const node_depth = 0; + int const node_offset = pt_idx; + int const num_objects = 1; + + int const ret = seL4_Untyped_RetypeAtOffset(service, + type, + offset, + size_bits, + root, + node_index, + node_depth, + node_offset, + num_objects); + + if (ret != 0) { + PDBG("seL4_Untyped_RetypeAtOffset (page table) returned %d", ret); + throw Alloc_page_table_failed(); + } + + unsigned const pt_sel = _idx_to_sel(pt_idx); + + _page_table_registry.insert_page_table(to_virt, pt_sel); + + _map_page_table(pt_sel, to_virt); + } + + public: + + /** + * Constructor + * + * \param vm_pad_cnode_sel selector for the (2nd-level) VM pad CNode + * \param vm_cnode_sel selector for the (3rd-level) VM CNode + * \param phys_alloc backing store for the CNodes + * \param top_level_cnode top-level CNode to insert 'vm_pad_cnode_sel' + * \param id ID used as index in 'top_level_cnode' + * \param page_table_registry association of VM CNode selectors with + * with virtual addresses + */ + Vm_space(unsigned vm_pad_cnode_sel, + unsigned vm_cnode_sel, + Range_allocator &phys_alloc, + Cnode &top_level_cnode, + Cnode &core_cnode, + Cnode &phys_cnode, + unsigned id, + Page_table_registry &page_table_registry) + : + _page_table_registry(page_table_registry), _id(id), + _phys_alloc(phys_alloc), + _top_level_cnode(top_level_cnode), + _phys_cnode(phys_cnode), + _vm_pad_cnode(core_cnode.sel(), vm_pad_cnode_sel, + 32 - 12 - NUM_VM_SEL_LOG2, phys_alloc), + _vm_cnode(core_cnode.sel(), vm_cnode_sel, NUM_VM_SEL_LOG2, phys_alloc) + { + Cnode_base const cspace(seL4_CapInitThreadCNode, 32); + + /* insert 3rd-level VM CNode into 2nd-level VM-pad CNode */ + _vm_pad_cnode.copy(cspace, vm_cnode_sel, 0); + + /* insert 2nd-level VM-pad CNode into 1st-level CNode */ + _top_level_cnode.copy(cspace, vm_pad_cnode_sel, id); + } + + void map(addr_t from_phys, addr_t to_virt, size_t num_pages) + { + /* check if we need to add a page table to core's VM space */ + if (!_page_table_registry.has_page_table_at(to_virt)) + _alloc_and_map_page_table(to_virt); + + for (size_t i = 0; i < num_pages; i++) { + off_t const offset = i << get_page_size_log2(); + _map_page(from_phys + offset, to_virt + offset); + } + } +}; + +#endif /* _CORE__INCLUDE__VM_SPACE_H_ */ diff --git a/repos/base-sel4/src/core/platform.cc b/repos/base-sel4/src/core/platform.cc index 50dca3153..a32adac17 100644 --- a/repos/base-sel4/src/core/platform.cc +++ b/repos/base-sel4/src/core/platform.cc @@ -43,7 +43,11 @@ bool Core_mem_allocator::Mapped_mem_allocator::_map_local(addr_t virt_addr, addr_t phys_addr, unsigned size) { - return map_local(phys_addr, virt_addr, size / get_page_size()); + size_t const num_pages = size / get_page_size(); + + Untyped_memory::convert_to_page_frames(phys_addr, num_pages); + + return map_local(phys_addr, virt_addr, num_pages); } @@ -83,116 +87,8 @@ static inline void init_sel4_ipc_buffer() } -/* CNode dimensions */ -enum { - NUM_TOP_SEL_LOG2 = 12UL, - NUM_CORE_SEL_LOG2 = 14UL, - NUM_PHYS_SEL_LOG2 = 20UL, -}; - - -/* selectors for statically created CNodes */ -enum Static_cnode_sel { - TOP_CNODE_SEL = 0x200, - CORE_PAD_CNODE_SEL = 0x201, - CORE_CNODE_SEL = 0x202, - PHYS_CNODE_SEL = 0x203 -}; - - -/* indices within top-level CNode */ -enum Top_cnode_idx { - TOP_CNODE_CORE_IDX = 0, - TOP_CNODE_PHYS_IDX = 0xfff -}; - - -/** - * Replace initial CSpace with custom CSpace layout - */ -static void switch_to_core_cspace(Range_allocator &phys_alloc) +void Platform::_init_allocators() { - Cnode_base const initial_cspace(seL4_CapInitThreadCNode, 32); - - /* allocate 1st-level CNode */ - static Cnode top_cnode(TOP_CNODE_SEL, NUM_TOP_SEL_LOG2, phys_alloc); - - /* allocate 2nd-level CNode to align core's CNode with the LSB of the CSpace*/ - static Cnode core_pad_cnode(CORE_PAD_CNODE_SEL, - 32UL - NUM_TOP_SEL_LOG2 - NUM_CORE_SEL_LOG2, - phys_alloc); - - /* allocate 3rd-level CNode for core's objects */ - static Cnode core_cnode(CORE_CNODE_SEL, NUM_CORE_SEL_LOG2, phys_alloc); - - /* copy initial selectors to core's CNode */ - core_cnode.copy(initial_cspace, seL4_CapInitThreadTCB); - core_cnode.copy(initial_cspace, seL4_CapInitThreadCNode); - core_cnode.copy(initial_cspace, seL4_CapInitThreadPD); - core_cnode.move(initial_cspace, seL4_CapIRQControl); /* cannot be copied */ - core_cnode.copy(initial_cspace, seL4_CapIOPort); - core_cnode.copy(initial_cspace, seL4_CapBootInfoFrame); - core_cnode.copy(initial_cspace, seL4_CapArchBootInfoFrame); - core_cnode.copy(initial_cspace, seL4_CapInitThreadIPCBuffer); - core_cnode.copy(initial_cspace, seL4_CapIPI); - core_cnode.copy(initial_cspace, seL4_CapDomain); - - /* copy untyped memory selectors to core's CNode */ - seL4_BootInfo const &bi = sel4_boot_info(); - - for (unsigned sel = bi.untyped.start; sel < bi.untyped.end; sel++) - core_cnode.copy(initial_cspace, sel); - - for (unsigned sel = bi.deviceUntyped.start; sel < bi.deviceUntyped.end; sel++) - core_cnode.copy(initial_cspace, sel); - - /* copy statically created CNode selectors to core's CNode */ - core_cnode.copy(initial_cspace, TOP_CNODE_SEL); - core_cnode.copy(initial_cspace, CORE_PAD_CNODE_SEL); - core_cnode.copy(initial_cspace, CORE_CNODE_SEL); - - /* - * Construct CNode hierarchy of core's CSpace - */ - - /* insert 3rd-level core CNode into 2nd-level core-pad CNode */ - core_pad_cnode.copy(initial_cspace, CORE_CNODE_SEL, 0); - - /* insert 2nd-level core-pad CNode into 1st-level CNode */ - top_cnode.copy(initial_cspace, CORE_PAD_CNODE_SEL, TOP_CNODE_CORE_IDX); - - /* allocate 2nd-level CNode for storing page-frame cap selectors */ - static Cnode phys_cnode(PHYS_CNODE_SEL, NUM_PHYS_SEL_LOG2, phys_alloc); - - /* insert 2nd-level phys-mem CNode into 1st-level CNode */ - top_cnode.copy(initial_cspace, PHYS_CNODE_SEL, TOP_CNODE_PHYS_IDX); - - /* activate core's CSpace */ - { - seL4_CapData_t null_data = { { 0 } }; - - int const ret = seL4_TCB_SetSpace(seL4_CapInitThreadTCB, - seL4_CapNull, /* fault_ep */ - TOP_CNODE_SEL, null_data, - seL4_CapInitThreadPD, null_data); - - if (ret != 0) { - PERR("%s: seL4_TCB_SetSpace returned %d", __FUNCTION__, ret); - } - } -} - - -Platform::Platform() -: - _io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()), - _irq_alloc(core_mem_alloc()), - _vm_base(0x1000), - _vm_size(2*1024*1024*1024UL - _vm_base) /* use the lower 2GiB */ -{ - /* - * Initialize core allocators - */ seL4_BootInfo const &bi = sel4_boot_info(); /* interrupt allocator */ @@ -210,8 +106,16 @@ Platform::Platform() _core_mem_alloc.virt_alloc()->add_range(_vm_base, _vm_size); /* remove core image from core's virtual address allocator */ + + /* + * XXX Why do we need to skip a few KiB after the end of core? + * When allocating a PTE immediately after _prog_img_end, the + * kernel would complain "Mapping already present" on the + * attempt to map a page frame. + */ addr_t const core_virt_beg = trunc_page((addr_t)&_prog_img_beg), - core_virt_end = round_page((addr_t)&_prog_img_end); + core_virt_end = round_page((addr_t)&_prog_img_end) + + 64*1024; size_t const core_size = core_virt_end - core_virt_beg; _core_mem_alloc.virt_alloc()->remove_range(core_virt_beg, core_size); @@ -225,18 +129,121 @@ Platform::Platform() /* preserve context area in core's virtual address space */ _core_mem_alloc.virt_alloc()->remove_range(Native_config::context_area_virtual_base(), Native_config::context_area_virtual_size()); +} + + +void Platform::_switch_to_core_cspace() +{ + Cnode_base const initial_cspace(seL4_CapInitThreadCNode, 32); + + /* copy initial selectors to core's CNode */ + _core_cnode.copy(initial_cspace, seL4_CapInitThreadTCB); + _core_cnode.copy(initial_cspace, seL4_CapInitThreadPD); + _core_cnode.move(initial_cspace, seL4_CapIRQControl); /* cannot be copied */ + _core_cnode.copy(initial_cspace, seL4_CapIOPort); + _core_cnode.copy(initial_cspace, seL4_CapBootInfoFrame); + _core_cnode.copy(initial_cspace, seL4_CapArchBootInfoFrame); + _core_cnode.copy(initial_cspace, seL4_CapInitThreadIPCBuffer); + _core_cnode.copy(initial_cspace, seL4_CapIPI); + _core_cnode.copy(initial_cspace, seL4_CapDomain); + + /* replace seL4_CapInitThreadCNode with new top-level CNode */ + _core_cnode.copy(initial_cspace, Core_cspace::TOP_CNODE_SEL, seL4_CapInitThreadCNode); + + /* copy untyped memory selectors to core's CNode */ + seL4_BootInfo const &bi = sel4_boot_info(); + + for (unsigned sel = bi.untyped.start; sel < bi.untyped.end; sel++) + _core_cnode.copy(initial_cspace, sel); + + for (unsigned sel = bi.deviceUntyped.start; sel < bi.deviceUntyped.end; sel++) + _core_cnode.copy(initial_cspace, sel); + + /* copy statically created CNode selectors to core's CNode */ + _core_cnode.copy(initial_cspace, Core_cspace::TOP_CNODE_SEL); + _core_cnode.copy(initial_cspace, Core_cspace::CORE_PAD_CNODE_SEL); + _core_cnode.copy(initial_cspace, Core_cspace::CORE_CNODE_SEL); + _core_cnode.copy(initial_cspace, Core_cspace::PHYS_CNODE_SEL); /* - * Until this point, no interaction with the seL4 kernel was needed. - * However, the next steps involve the invokation of system calls and - * the use of kernel services. To use the kernel bindings, we first - * need to initialize the TLS mechanism that is used to find the IPC - * buffer for the calling thread. + * Construct CNode hierarchy of core's CSpace */ - init_sel4_ipc_buffer(); - /* initialize core's capability space */ - switch_to_core_cspace(*_core_mem_alloc.phys_alloc()); + /* insert 3rd-level core CNode into 2nd-level core-pad CNode */ + _core_pad_cnode.copy(initial_cspace, Core_cspace::CORE_CNODE_SEL, 0); + + /* insert 2nd-level core-pad CNode into 1st-level CNode */ + _top_cnode.copy(initial_cspace, Core_cspace::CORE_PAD_CNODE_SEL, + Core_cspace::TOP_CNODE_CORE_IDX); + + /* insert 2nd-level phys-mem CNode into 1st-level CNode */ + _top_cnode.copy(initial_cspace, Core_cspace::PHYS_CNODE_SEL, + Core_cspace::TOP_CNODE_PHYS_IDX); + + /* activate core's CSpace */ + { + seL4_CapData_t null_data = { { 0 } }; + + int const ret = seL4_TCB_SetSpace(seL4_CapInitThreadTCB, + seL4_CapNull, /* fault_ep */ + Core_cspace::TOP_CNODE_SEL, null_data, + seL4_CapInitThreadPD, null_data); + + if (ret != 0) { + PERR("%s: seL4_TCB_SetSpace returned %d", __FUNCTION__, ret); + } + } +} + + +void Platform::_init_core_page_table_registry() +{ + seL4_BootInfo const &bi = sel4_boot_info(); + + /* + * Register initial page tables + */ + addr_t virt_addr = (addr_t)(&_prog_img_beg); + for (unsigned sel = bi.userImagePTs.start; sel < bi.userImagePTs.end; sel++) { + + _core_page_table_registry.insert_page_table(virt_addr, sel); + + /* one page table has 1024 entries */ + virt_addr += 1024*get_page_size(); + } + + /* + * Register initial page frames + */ + virt_addr = (addr_t)(&_prog_img_beg); + for (unsigned sel = bi.userImageFrames.start; sel < bi.userImageFrames.end; sel++) { + + _core_page_table_registry.insert_page_table_entry(virt_addr, sel); + + virt_addr += get_page_size(); + } +} + + +Platform::Platform() +: + _io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()), + _irq_alloc(core_mem_alloc()), + _vm_base(0x1000), + _vm_size(2*1024*1024*1024UL - _vm_base), /* use the lower 2GiB */ + _init_allocators_done((_init_allocators(), true)), + _init_sel4_ipc_buffer_done((init_sel4_ipc_buffer(), true)), + _switch_to_core_cspace_done((_switch_to_core_cspace(), true)), + _core_page_table_registry(*core_mem_alloc()), + _init_core_page_table_registry_done((_init_core_page_table_registry(), true)), + _core_vm_space(Core_cspace::CORE_VM_PAD_CNODE_SEL, Core_cspace::CORE_VM_CNODE_SEL, + _phys_alloc, + _top_cnode, + _core_cnode, + _phys_cnode, + Core_cspace::CORE_VM_ID, + _core_page_table_registry) +{ /* add boot modules to ROM fs */ @@ -250,6 +257,7 @@ Platform::Platform() printf(":virt_alloc: "); _core_mem_alloc.virt_alloc()->raw()->dump_addr_tree(); printf(":io_mem_alloc: "); _io_mem_alloc.raw()->dump_addr_tree(); } + } diff --git a/repos/base-sel4/src/core/ram_session_support.cc b/repos/base-sel4/src/core/ram_session_support.cc index cda7a35ea..7300e412d 100644 --- a/repos/base-sel4/src/core/ram_session_support.cc +++ b/repos/base-sel4/src/core/ram_session_support.cc @@ -22,8 +22,15 @@ 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::_export_ram_ds(Dataspace_component *ds) +{ + PDBG("not implemented"); +} + +void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) +{ + PDBG("not implemented"); +} void Ram_session_component::_clear_ds (Dataspace_component *ds) diff --git a/repos/base-sel4/src/core/target.inc b/repos/base-sel4/src/core/target.inc index a0c5a3043..b41cdcebc 100644 --- a/repos/base-sel4/src/core/target.inc +++ b/repos/base-sel4/src/core/target.inc @@ -54,5 +54,4 @@ vpath trace_session_component.cc $(GEN_CORE_DIR) vpath dataspace_component.cc $(GEN_CORE_DIR) vpath core_mem_alloc.cc $(GEN_CORE_DIR) vpath dump_alloc.cc $(GEN_CORE_DIR) -vpath context_area.cc $(GEN_CORE_DIR) vpath %.cc $(REP_DIR)/src/core