From 944be1b4e6dc535fae91bb9f5bf208634e4f5077 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 20 Dec 2013 10:41:37 +0100 Subject: [PATCH] nova: throw exception if ram_ds setup fails Fail hard if no large enough virtual memory area can be found where to map the memory from the kernel to core. Additionally clear dataspaces in junks if it can't be done in one large junk. Fixes #1011 --- base-nova/src/core/ram_session_support.cc | 126 ++++++++++++++++------ 1 file changed, 93 insertions(+), 33 deletions(-) diff --git a/base-nova/src/core/ram_session_support.cc b/base-nova/src/core/ram_session_support.cc index b1ebc7298..c19afc177 100644 --- a/base-nova/src/core/ram_session_support.cc +++ b/base-nova/src/core/ram_session_support.cc @@ -28,8 +28,6 @@ enum { verbose_ram_ds = false }; using namespace Genode; -void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { } - void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { @@ -50,55 +48,117 @@ void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) } -void Ram_session_component::_clear_ds(Dataspace_component *ds) +/** + * Map dataspace core-locally + */ +static inline void * alloc_region(Dataspace_component *ds, const size_t size) { - /* - * Map dataspace core-locally and clear its content - */ - - size_t page_rounded_size = (ds->size() + get_page_size() - 1) & get_page_mask(); - /* * Allocate range in core's virtual address space * * Start with trying to use natural alignment. If this does not work, * successively weaken the alignment constraint until we hit the page size. */ - void *virt_addr; - bool virt_alloc_succeeded = false; + void *virt_addr = 0; size_t align_log2 = log2(ds->size()); for (; align_log2 >= get_page_size_log2(); align_log2--) { - if (platform()->region_alloc()->alloc_aligned(page_rounded_size, - &virt_addr, align_log2).is_ok()) { - virt_alloc_succeeded = true; + if (platform()->region_alloc()->alloc_aligned(size, + &virt_addr, align_log2).is_ok()) break; - } } - if (!virt_alloc_succeeded) { - PERR("Could not allocate virtual address range in core of size %zd\n", - page_rounded_size); - return; + return virt_addr; +} + + +void Ram_session_component::_clear_ds(Dataspace_component *ds) +{ + const size_t page_rounded_size = (ds->size() + get_page_size() - 1) & get_page_mask(); + size_t pages = page_rounded_size >> get_page_size_log2(); + size_t virt_size = page_rounded_size; + + void * virt_ptr = alloc_region(ds, virt_size); + + /* if it's not possible to get it in one piece, try to get a smaller one */ + while (pages && !virt_ptr) { + pages >>= 1; + virt_size = pages << get_page_size_log2(); + virt_ptr = alloc_region(ds, virt_size); } + /* no free virtual region available ? */ + if (!virt_ptr) return; + + Nova::Utcb * const utcb = reinterpret_cast(Thread_base::myself()->utcb()); + const Nova::Rights rights(true, ds->writable(), true); + + addr_t const virt_addr = reinterpret_cast(virt_ptr); + addr_t phys = ds->phys_addr(); + if (verbose_ram_ds) - printf("-- map - ram ds size=0x%8zx phys 0x%8lx has core-local addr 0x%8p - thread 0x%8p\n", - page_rounded_size, ds->phys_addr(), virt_addr, Thread_base::myself()->utcb()); + printf("-- map - ram ds to be cleared phys 0x%8lx+0x%8zx\n", + ds->phys_addr(), page_rounded_size); - /* map the dataspace's physical pages to local addresses */ - const Nova::Rights rights(true, ds->writable(), true); - int res = map_local((Nova::Utcb *)Thread_base::myself()->utcb(), - ds->phys_addr(), (addr_t)virt_addr, - page_rounded_size >> get_page_size_log2(), rights, - true); + while (phys < ds->phys_addr() + page_rounded_size) { - if (res) { - PERR("map failed - ram ds size=0x%8zx phys 0x%8lx, core-local 0x%8p\n", - page_rounded_size, ds->phys_addr(), virt_addr); - return; + if (verbose_ram_ds) + printf("-- map - clear phys 0x%8lx+0x%8zx\n", phys, virt_size); + + /* map the dataspace's physical pages to core local addresses */ + if (map_local(utcb, phys, virt_addr, virt_size >> get_page_size_log2(), + rights, true)) + { + PERR("map failed - ram ds size=0x%8zx phys 0x%8lx, core-local 0x%8p", + virt_size, phys, virt_ptr); + + break; + } + + memset(virt_ptr, 0, virt_size); + + unmap_local(utcb, virt_addr, virt_size >> get_page_size_log2()); + + phys += virt_size; + + if (page_rounded_size - virt_size < phys - ds->phys_addr()) + virt_size = ds->phys_addr() + page_rounded_size - phys; } - memset(virt_addr, 0, page_rounded_size); + /* free virtual region - don't use 'virt_size' - use 'pages' */ + platform()->region_alloc()->free(virt_ptr, pages << get_page_size_log2()); - ds->assign_core_local_addr(virt_addr); + /* signal that we cleared the memory successfully */ + ds->assign_core_local_addr(0); +} + + +void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { + + const size_t page_rounded_size = (ds->size() + get_page_size() - 1) & get_page_mask(); + + /* if clearing the pages failed don't give the pages out */ + if (!ds || ds->core_local_addr()) + throw Out_of_metadata(); + + /* allocate the virtual region contiguous for the dataspace */ + void * virt_ptr = alloc_region(ds, page_rounded_size); + if (!virt_ptr) + throw Out_of_metadata(); + + /* map it */ + Nova::Utcb * const utcb = reinterpret_cast(Thread_base::myself()->utcb()); + const Nova::Rights rights(true, ds->writable(), true); + + if (map_local(utcb, ds->phys_addr(), reinterpret_cast(virt_ptr), + page_rounded_size >> get_page_size_log2(), rights, true)) { + platform()->region_alloc()->free(virt_ptr, page_rounded_size); + throw Out_of_metadata(); + } + + /* we succeeded, so assign the virtual address to the dataspace */ + ds->assign_core_local_addr(virt_ptr); + + if (verbose_ram_ds) + printf("-- map - ram ds size=0x%8zx phys 0x%8lx has core-local addr 0x%8lx\n", + page_rounded_size, ds->phys_addr(), ds->core_local_addr()); }