diff --git a/repos/os/src/drivers/acpi/acpi.cc b/repos/os/src/drivers/acpi/acpi.cc index 3387be2ba..d620c1d20 100644 --- a/repos/os/src/drivers/acpi/acpi.cc +++ b/repos/os/src/drivers/acpi/acpi.cc @@ -469,15 +469,11 @@ class Table_wrapper Table_wrapper(Acpi::Memory &memory, addr_t base) : _base(base), _table(0) { - /* if table is on page boundary, map two pages, otherwise one page */ - size_t const map_size = 0x1000UL - _offset() < 8 ? 0x1000UL : 1UL; - /* make table header accessible */ - _table = reinterpret_cast(memory.phys_to_virt(base, map_size)); + _table = reinterpret_cast(memory.map_region(base, 8)); - /* table size is known now - make it complete accessible */ - if (_offset() + _table->size > 0x1000UL) - memory.phys_to_virt(base, _table->size); + /* table size is known now - make it completely accessible (in place) */ + memory.map_region(base, _table->size); memset(_name, 0, 5); memcpy(_name, _table->signature, 4); diff --git a/repos/os/src/drivers/acpi/memory.h b/repos/os/src/drivers/acpi/memory.h index 28efb10ad..e43403793 100644 --- a/repos/os/src/drivers/acpi/memory.h +++ b/repos/os/src/drivers/acpi/memory.h @@ -20,97 +20,183 @@ #include #include -namespace Acpi { class Memory; } +namespace Acpi { + using namespace Genode; + + class Memory; +} class Acpi::Memory { + public: + + struct Unsupported_range { }; + private: - class Io_mem : public Genode::List::Element + /* + * We wrap the connection into Constructible to prevent a "accessible + * non-virtual destructor" compiler error with Allocator_avl_base::Block. + */ + struct Io_mem { - private: - Genode::Io_mem_connection _io_mem; + struct Region + { + addr_t _base; + size_t _size; - public: - Io_mem(Genode::Env &env, Genode::addr_t phys) - : _io_mem(env, phys, 0x1000UL) { } - - Genode::Io_mem_dataspace_capability dataspace() + static addr_t _base_align(addr_t base) { - return _io_mem.dataspace(); + return base & ~0xfffUL; } + + static addr_t _size_align(addr_t base, size_t size) + { + return align_addr(base + (size - 1) - _base_align(base), 12); + } + + Region(addr_t base, size_t size) + : + _base(_base_align(base)), + _size(_size_align(base, size)) + { } + + addr_t base() const { return _base; } + addr_t last() const { return _base + (_size - 1); } + size_t size() const { return _size; } + + bool contains(Region const &o) const + { + return o.base() >= base() && o.last() <= last(); + } + + void print(Output &o) const + { + Genode::print(o, Hex_range(_base, _size)); + } + } region; + + Constructible connection { }; + + Io_mem(Env &env, Region region) : region(region) + { + connection.construct(env, region.base(), region.size()); + } }; - Genode::Env &_env; - Genode::addr_t const ACPI_REGION_SIZE_LOG2; - Genode::Rm_connection _rm; - Genode::Region_map_client _rm_acpi; - Genode::addr_t const _acpi_base; - Genode::Allocator &_heap; - Genode::Allocator_avl _range; - Genode::List _io_mem_list { }; + static constexpr unsigned long + ACPI_REGION_SIZE_LOG2 = 30, /* 1 GiB range */ + ACPI_REGION_SIZE = 1UL << ACPI_REGION_SIZE_LOG2; + + Env &_env; + Allocator &_heap; + + Rm_connection _rm { _env }; + Region_map_client _acpi_window { _rm.create(ACPI_REGION_SIZE) }; + addr_t const _acpi_base { _env.rm().attach(_acpi_window.dataspace()) }; + + Constructible _io_region { }; + + addr_t _acpi_ptr(addr_t base) const + { + /* virtual address inside the mapped ACPI window */ + return _acpi_base + (base - _io_region->base()); + } + + Allocator_avl_tpl _range { &_heap }; public: - Memory(Genode::Env &env, Genode::Allocator &heap) - : - _env(env), - /* 1 GB range */ - ACPI_REGION_SIZE_LOG2(30), - _rm(env), - _rm_acpi(_rm.create(1UL << ACPI_REGION_SIZE_LOG2)), - _acpi_base(env.rm().attach(_rm_acpi.dataspace())), - _heap(heap), - _range(&_heap) + Memory(Env &env, Allocator &heap) : _env(env), _heap(heap) { - _range.add_range(0, 1UL << ACPI_REGION_SIZE_LOG2); + _range.add_range(0, ~0UL); } - Genode::addr_t phys_to_virt(Genode::addr_t const phys, Genode::addr_t const p_size) + addr_t map_region(addr_t const req_base, addr_t const req_size) { - using namespace Genode; + /* + * The first caller sets the upper physical bits of addresses and, + * thereby, determines the valid range of addresses. + */ - /* the first caller sets the upper physical bits of addresses */ - static addr_t const high = phys & _align_mask(ACPI_REGION_SIZE_LOG2); - - /* sanity check that physical address is in range we support */ - if ((phys & _align_mask(ACPI_REGION_SIZE_LOG2)) != high) { - addr_t const end = high + (1UL << ACPI_REGION_SIZE_LOG2) - 1; - error("acpi table out of range - ", Hex(phys), " " - "not in ", Hex_range(high, end - high)); - throw -1; + if (!_io_region.constructed()) { + _io_region.construct(req_base & _align_mask(ACPI_REGION_SIZE_LOG2), + ACPI_REGION_SIZE); } - addr_t const phys_aligned = phys & _align_mask(12); - addr_t const size_aligned = align_addr(p_size + (phys & _align_offset(12)), 12); + /* requested region of I/O memory */ + Io_mem::Region loop_region { req_base, req_size }; - for (addr_t size = 0; size < size_aligned; size += 0x1000UL) { - addr_t const low = (phys_aligned + size) & - _align_offset(ACPI_REGION_SIZE_LOG2); - if (!_range.alloc_addr(0x1000UL, low).ok()) - continue; - - /* allocate acpi page as io memory */ - Io_mem *mem = new (_heap) Io_mem(_env, phys_aligned + size); - /* attach acpi page to this process */ - _rm_acpi.attach_at(mem->dataspace(), low, 0x1000UL); - /* add to list to free when parsing acpi table is done */ - _io_mem_list.insert(mem); + /* check that physical region fits into supported range */ + if (!_io_region->contains(loop_region)) { + error("acpi table out of range - ", loop_region, " not in ", *_io_region); + throw Unsupported_range(); } - return _acpi_base + (phys & _align_offset(ACPI_REGION_SIZE_LOG2)); + /* early return if the region is already mapped */ + if (Io_mem *m = _range.metadata((void *)req_base)) { + if (m->region.contains(loop_region)) { + return _acpi_ptr(req_base); + } + } + + /* + * We iterate over the requested region looking for collisions with + * existing mappings. On a collision, we extend the requested range + * to comprise also the existing mapping and destroy the mapping. + * Finally, we request the compound region as on I/O memory + * mapping. + * + * Note, this approach unfortunately does not merge consecutive + * regions. + */ + + addr_t loop_offset = 0; + while (loop_offset < loop_region.size()) { + void * const addr = (void *)(loop_region.base() + loop_offset); + + if (Io_mem *m = _range.metadata(addr)) { + addr_t const region_base = m->region.base(); + addr_t const region_size = m->region.size(); + addr_t const compound_base = min(loop_region.base(), region_base); + addr_t const compound_end = max(loop_region.base() + loop_region.size(), + region_base + region_size); + + m->~Io_mem(); + _range.free((void *)region_base); + + /* now start over */ + loop_region = Io_mem::Region(compound_base, compound_end - compound_base); + loop_offset = 0; + } + + loop_offset += 0x1000; + } + + /* allocate ACPI range as I/O memory */ + _range.alloc_addr(loop_region.size(), loop_region.base()); + _range.construct_metadata((void *)loop_region.base(), _env, loop_region); + + /* + * We attach the I/O memory dataspace into a virtual-memory window, + * which starts at _io_region.base(). Therefore, the attachment + * address is the offset of loop_region.base() from + * _io_region.base(). + */ + _acpi_window.attach_at( + _range.metadata((void *)loop_region.base())->connection->dataspace(), + loop_region.base() - _io_region->base(), loop_region.size()); + + return _acpi_ptr(req_base); } void free_io_memory() { - while (Io_mem * io_mem = _io_mem_list.first()) { - _io_mem_list.remove(io_mem); - destroy(_heap, io_mem); - } - - Genode::addr_t out_addr; - while (_range.any_block_addr(&out_addr)) + addr_t out_addr = 0; + while (_range.any_block_addr(&out_addr)) { + _range.metadata((void *)out_addr)->~Io_mem(); _range.free((void *)out_addr); + } } };