/* * \brief Fiasco platform interface implementation * \author Christian Helmuth * \date 2006-04-11 */ /* * 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. */ /* Genode includes */ #include #include #include #include /* base-internal includes */ #include #include #include #include #include /* core includes */ #include #include #include #include #include /* Fiasco includes */ namespace Fiasco { #include #include #include #include #include #include } using namespace Genode; /*********************************** ** Core address space management ** ***********************************/ static Synced_range_allocator &_core_address_ranges() { static Synced_range_allocator _core_address_ranges(nullptr); return _core_address_ranges; } enum { PAGER_STACK_ELEMENTS = 1024 }; static unsigned long _core_pager_stack[PAGER_STACK_ELEMENTS]; static unsigned _core_pager_arg; /** * Core pager "service loop" */ static void _core_pager_loop() { unsigned pd_id = _core_pager_arg; using namespace Fiasco; l4_threadid_t t; l4_umword_t dw0, dw1; l4_msgdope_t r; bool send_reply = false; while (1) { if (send_reply) /* unblock faulter and wait for next pagefault */ l4_ipc_reply_and_wait(t, L4_IPC_SHORT_MSG, 0, 0, &t, L4_IPC_SHORT_MSG, &dw0, &dw1, L4_IPC_NEVER, &r); else l4_ipc_wait(&t, L4_IPC_SHORT_MSG, &dw0, &dw1, L4_IPC_NEVER, &r); /* ignore messages from non-core pds */ if (t.id.task != pd_id) break; /* detect local map request */ if (dw1 == 0) { l4_msgdope_t ipc_result; l4_ipc_send(t, L4_IPC_SHORT_FPAGE, 0, dw0, L4_IPC_SEND_TIMEOUT_0, &ipc_result); send_reply = false; continue; } bool rw = dw0 & 2; addr_t pfa = dw0 & ~2; if (pfa < L4_PAGESIZE) { /* NULL pointer access */ error("possible null pointer ", rw ? "WRITE" : "READ", " " "in ", (int)t.id.task, ".", (int)t.id.lthread, " " "at ", Hex(pfa), " IP ", Hex(dw1)); /* do not unblock faulter */ send_reply = false; continue; } else if (!_core_address_ranges().valid_addr(pfa)) { /* page-fault address is not in RAM */ error(rw ? "WRITE" : "READ", " access outside of RAM " "in ", (int)t.id.task, ".", (int)t.id.lthread, " " "at ", Hex(pfa), " IP ", Hex(dw1)); /* do not unblock faulter */ send_reply = false; continue; } /* my pf handler is sigma0 - just touch the appropriate page */ if (rw) touch_rw((void *)pfa, 1); else touch_ro((void *)pfa, 1); send_reply = true; } } Platform::Sigma0::Sigma0() : Pager_object(Cpu_session_capability(), Thread_capability(), 0, Affinity::Location(), Session_label(), Cpu_session::Name("sigma0")) { cap(Capability_space::import(Fiasco::sigma0_threadid, Rpc_obj_key())); } Platform::Sigma0 &Platform::sigma0() { static Sigma0 _sigma0; return _sigma0; } Platform::Core_pager::Core_pager(Platform_pd &core_pd) : Platform_thread("core.pager"), Pager_object(Cpu_session_capability(), Thread_capability(), 0, Affinity::Location(), Session_label(), Cpu_session::Name(name())) { Platform_thread::pager(sigma0()); core_pd.bind_thread(*this); cap(Capability_space::import(native_thread_id(), Rpc_obj_key())); /* pager needs to know core's pd ID */ _core_pager_arg = core_pd.pd_id(); /* stack begins at the top end of the '_core_pager_stack' array */ void *sp = (void *)&_core_pager_stack[PAGER_STACK_ELEMENTS - 1]; start((void *)_core_pager_loop, sp); using namespace Fiasco; /* pager0 receives pagefaults from me - for NULL pointer detection */ l4_umword_t d; l4_threadid_t preempter = L4_INVALID_ID; l4_threadid_t pager = native_thread_id(); l4_thread_ex_regs(l4_myself(), ~0UL, ~0UL, &preempter, &pager, &d, &d, &d); } Platform::Core_pager &Platform::core_pager() { static Core_pager _core_pager(core_pd()); return _core_pager; } /*********************************** ** Helper for L4 region handling ** ***********************************/ struct Region { addr_t start; addr_t end; Region() : start(0), end(0) { } Region(addr_t s, addr_t e) : start(s), end(e) { } /** * Returns true if the specified range intersects with the region */ bool intersects(addr_t base, size_t size) const { return (((base + size) > start) && (base < end)); } }; /** * Add region to allocator */ static inline void add_region(Region r, Range_allocator &alloc) { /* adjust region */ addr_t start = trunc_page(r.start); addr_t end = round_page(r.end); alloc.add_range(start, end - start); } /** * Remove region from allocator */ static inline void remove_region(Region r, Range_allocator &alloc) { /* adjust region */ addr_t start = trunc_page(r.start); addr_t end = round_page(r.end); alloc.remove_range(start, end - start); } /** * Request any RAM page from Sigma0 */ static inline int sigma0_req_region(addr_t *addr, unsigned log2size) { using namespace Fiasco; /* XXX sigma0 always maps pages RW */ l4_umword_t req_fpage = l4_fpage(0, log2size, 0, 0).fpage; void* rcv_window = L4_IPC_MAPMSG(0, L4_WHOLE_ADDRESS_SPACE); addr_t base; l4_fpage_t rcv_fpage; l4_msgdope_t result; l4_msgtag_t tag; int err = l4_ipc_call_tag(Fiasco::sigma0_threadid, L4_IPC_SHORT_MSG, SIGMA0_REQ_FPAGE_ANY, req_fpage, l4_msgtag(L4_MSGTAG_SIGMA0, 0, 0, 0), rcv_window, &base, (l4_umword_t *)&rcv_fpage, L4_IPC_NEVER, &result, &tag); int ret = (err || !l4_ipc_fpage_received(result)); if (!ret) touch_rw((void *)addr, 1); *addr = base; return ret; } void Platform::_setup_mem_alloc() { /* * Completely map program image by touching all pages read-only to * prevent sigma0 from handing out those page as anonymous memory. */ volatile const char *beg, *end; beg = (const char *)(((unsigned)&_prog_img_beg) & L4_PAGEMASK); end = (const char *)&_prog_img_end; for ( ; beg < end; beg += L4_PAGESIZE) (void)(*beg); /* request pages of known page size starting with largest */ size_t log2_sizes[] = { L4_LOG2_SUPERPAGESIZE, L4_LOG2_PAGESIZE }; for (unsigned i = 0; i < sizeof(log2_sizes)/sizeof(*log2_sizes); ++i) { size_t log2_size = log2_sizes[i]; size_t size = 1 << log2_size; int err = 0; addr_t addr; Region region; /* request any page of current size from sigma0 */ do { err = sigma0_req_region(&addr, log2_size); if (!err) { /* XXX do not allocate page0 */ if (addr == 0) { Fiasco::l4_fpage_unmap(Fiasco::l4_fpage(0, log2_size, 0, 0), L4_FP_FLUSH_PAGE | L4_FP_ALL_SPACES); continue; } region.start = addr; region.end = addr + size; if (!region.intersects(stack_area_virtual_base(), stack_area_virtual_size())) { add_region(region, _ram_alloc); add_region(region, _core_address_ranges()); } remove_region(region, _io_mem_alloc); remove_region(region, _region_alloc); } } while (!err); } } void Platform::_setup_irq_alloc() { _irq_alloc.add_range(0, 0x10); } static Fiasco::l4_kernel_info_t *get_kip() { using namespace Fiasco; static l4_kernel_info_t *kip = nullptr; if (kip) return kip; int err; /* region allocator is not setup yet */ /* map KIP one-to-one */ void *fpage = L4_IPC_MAPMSG(0, L4_WHOLE_ADDRESS_SPACE); l4_umword_t dw0, dw1; l4_msgdope_t r; l4_msgtag_t tag; err = l4_ipc_call_tag(Fiasco::sigma0_threadid, L4_IPC_SHORT_MSG, SIGMA0_REQ_KIP, 0, l4_msgtag(L4_MSGTAG_SIGMA0, 0, 0, 0), fpage, &dw0, &dw1, L4_IPC_NEVER, &r, &tag); bool amok = false; if (err) { raw("IPC error ", err, " while accessing the KIP"); amok = true; } if (!l4_ipc_fpage_received(r)) { warning("No fpage received"); amok = true; } if (amok) panic("kip mapping failed"); /* store mapping base from received mapping */ kip = (l4_kernel_info_t *)dw0; if (kip->magic != L4_KERNEL_INFO_MAGIC) panic("Sigma0 mapped something but not the KIP"); return kip; } void Platform::_setup_basics() { using namespace Fiasco; l4_kernel_info_t * kip = get_kip(); /* add KIP as ROM module */ _rom_fs.insert(&_kip_rom); /* parse memory descriptors - look for virtual memory configuration */ /* XXX we support only one VM region (here and also inside RM) */ using L4::Kip::Mem_desc; _vm_start = 0; _vm_size = 0; Mem_desc *desc = Mem_desc::first(kip); for (unsigned i = 0; i < Mem_desc::count(kip); ++i) if (desc[i].is_virtual()) { _vm_start = round_page(desc[i].start()); _vm_size = trunc_page(desc[i].end() - _vm_start + 1); break; } if (_vm_size == 0) panic("Virtual memory configuration not found"); /* configure applicable address space but never use page0 */ _vm_size = _vm_start == 0 ? _vm_size - L4_PAGESIZE : _vm_size; _vm_start = _vm_start == 0 ? L4_PAGESIZE : _vm_start; _region_alloc.add_range(_vm_start, _vm_size); /* preserve stack area in core's virtual address space */ _region_alloc.remove_range(stack_area_virtual_base(), stack_area_virtual_size()); /* I/O memory could be the whole user address space */ /* FIXME if the kernel helps to find out max address - use info here */ _io_mem_alloc.add_range(0, ~0); /* remove KIP area from region and IO_MEM allocator */ remove_region(Region((addr_t)kip, (addr_t)kip + L4_PAGESIZE), _region_alloc); remove_region(Region((addr_t)kip, (addr_t)kip + L4_PAGESIZE), _io_mem_alloc); /* remove core program image memory from region and IO_MEM allocator */ addr_t img_start = (addr_t) &_prog_img_beg; addr_t img_end = (addr_t) &_prog_img_end; remove_region(Region(img_start, img_end), _region_alloc); remove_region(Region(img_start, img_end), _io_mem_alloc); /* image is accessible by core */ add_region(Region(img_start, img_end), _core_address_ranges()); } Platform::Platform() : _ram_alloc(nullptr), _io_mem_alloc(&core_mem_alloc()), _io_port_alloc(&core_mem_alloc()), _irq_alloc(&core_mem_alloc()), _region_alloc(&core_mem_alloc()), _kip_rom((addr_t)get_kip(), L4_PAGESIZE, "l4v2_kip") { /* * We must be single-threaded at this stage and so this is safe. */ static bool initialized = 0; if (initialized) panic("Platform constructed twice!"); initialized = true; _setup_basics(); _setup_mem_alloc(); _setup_io_port_alloc(); _setup_irq_alloc(); _init_rom_modules(); log(_rom_fs); Fiasco::l4_threadid_t myself = Fiasco::l4_myself(); Platform_pd::init(); /* setup pd object for core pd */ _core_label[0] = 0; _core_pd = new (core_mem_alloc()) Platform_pd(_core_label, myself.id.task); /* * We setup the thread object for thread0 in core pd using a special * interface that allows us to specify the lthread number. */ Platform_thread &core_thread = *new (core_mem_alloc()) Platform_thread("core.main"); core_thread.pager(sigma0()); _core_pd->bind_thread(core_thread); /* we never call _core_thread.start(), so set name directly */ Fiasco::fiasco_register_thread_name(core_thread.native_thread_id(), core_thread.name().string()); /* core log as ROM module */ { void * phys_ptr = nullptr; unsigned const pages = 1; size_t const log_size = pages << get_page_size_log2(); ram_alloc().alloc_aligned(log_size, &phys_ptr, get_page_size_log2()); addr_t const phys_addr = reinterpret_cast(phys_ptr); void * const core_local_ptr = phys_ptr; addr_t const core_local_addr = phys_addr; /* let one page free after the log buffer */ region_alloc().remove_range(core_local_addr, log_size + get_page_size()); memset(core_local_ptr, 0, log_size); _rom_fs.insert(new (core_mem_alloc()) Rom_module(phys_addr, log_size, "core_log")); init_core_log(Core_log_range { core_local_addr, log_size } ); } } /******************************** ** Generic platform interface ** ********************************/ void Platform::wait_for_exit() { /* * On Fiasco, Core never exits. So let us sleep forever. */ sleep_forever(); }