diff --git a/repos/os/src/drivers/acpi/acpi.cc b/repos/os/src/drivers/acpi/acpi.cc index f5b07d40c..7608a0350 100644 --- a/repos/os/src/drivers/acpi/acpi.cc +++ b/repos/os/src/drivers/acpi/acpi.cc @@ -19,7 +19,9 @@ #include #include #include + #include "acpi.h" +#include "memory.h" using namespace Genode; @@ -192,6 +194,13 @@ class Pci_config_space : public List::Element }; +static Acpi::Memory & acpi_memory() +{ + static Acpi::Memory _memory; + return _memory; +} + + /** * ACPI table wrapper that for mapping tables to this address space */ @@ -200,35 +209,9 @@ class Table_wrapper private: addr_t _base; /* table base address */ - Io_mem_connection *_io_mem; /* mapping connection */ Generic *_table; /* pointer to table header */ char _name[5]; /* table name */ - /** - * Cleanup dynamically allocated memory - */ - void _cleanup() - { - if (_table) - env()->rm_session()->detach((uint8_t *)_table - _offset()); - - if (_io_mem) - destroy(env()->heap(), _io_mem); - } - - /** - * Map table of 'size' - */ - void _map(size_t size) - { - _io_mem = new (env()->heap()) Io_mem_connection(_base - _offset(), size + _offset()); - Io_mem_dataspace_capability io_ds = _io_mem->dataspace(); - if (!io_ds.valid()) - throw -1; - - _table = (Generic *)((uint8_t *)env()->rm_session()->attach(io_ds, size + _offset()) + _offset()); - } - /* return offset of '_base' to page boundary */ addr_t _offset() const { return (_base & 0xfff); } @@ -245,17 +228,12 @@ class Table_wrapper char const *name() const { return _name; } /** - * Copy table data to 'ptr' + * Determine maximal number of potential elements */ template - T * copy_entries(T &count) + addr_t entry_count(T *) { - addr_t size = _table->size - sizeof (Generic); - count = size / sizeof(T); - - T * entries = new (env()->heap()) T [count]; - memcpy(entries, _table + 1, size); - return entries; + return (_table->size - sizeof (Generic)) / sizeof(T); } /** @@ -344,20 +322,17 @@ class Table_wrapper dmar->base, dmar->limit); } - Table_wrapper(addr_t base) : _base(base), _io_mem(0), _table(0) + Table_wrapper(addr_t base) : _base(base), _table(0) { - /* - * Try to map one page only, if table is on page boundary, map two pages - */ - size_t map_size = 0x1000 - _offset(); - _map(map_size < 8 ? 0x1000 : map_size); + /* if table is on page boundary, map two pages, otherwise one page */ + size_t const map_size = 0x1000UL - _offset() < 8 ? 0x1000UL : 1UL; - /* remap if table size is larger than current size */ - if (_offset() + _table->size > 0x1000) { - size_t size = _table->size; - _cleanup(); - _map(size); - } + /* make table header accessible */ + _table = reinterpret_cast(acpi_memory().phys_to_virt(base, map_size)); + + /* table size is known now - make it complete accessible */ + if (_offset() + _table->size > 0x1000UL) + acpi_memory().phys_to_virt(base, _table->size); memset(_name, 0, 5); memcpy(_name, _table->signature, 4); @@ -370,8 +345,6 @@ class Table_wrapper throw -1; } } - - ~Table_wrapper() { _cleanup(); } }; @@ -844,12 +817,6 @@ class Element : public List::Element } } - virtual ~Element() - { - if (_name) - env()->heap()->free(_name, _name_len); - } - bool is_device() { return _type == SUB_DEVICE; } bool is_device_name() { return _type == DEVICE_NAME; } @@ -870,6 +837,12 @@ class Element : public List::Element public: + virtual ~Element() + { + if (_name) + env()->heap()->free(_name, _name_len); + } + /** * Accessors */ @@ -897,6 +870,30 @@ class Element : public List::Element return &_list; } + static void clean_list() + { + unsigned long freed_up = 0; + + Element * element = list()->first(); + while (element) { + if (element->is_device() || (element->_name_len == 4 && + !memcmp(element->_name, "_PIC", 4))) { + element = element->next(); + continue; + } + + freed_up += sizeof(*element) + element->_name ? element->_name_len : 0; + + Element * next = element->next(); + Element::list()->remove(element); + destroy(env()->heap(), element); + element = next; + } + + if (verbose) + PDBG("Freeing up memory of elements - %lu bytes", freed_up); + } + /** * Return list of PCI information for this element */ @@ -1188,32 +1185,21 @@ class Acpi_table if (xsdt && sizeof(addr_t) != sizeof(uint32_t)) { /* running 64bit and xsdt is valid */ - addr_t entries_count; - addr_t * entries; - { - Table_wrapper table(xsdt); - entries = table.copy_entries(entries_count); - } - - _parse_tables(entries, entries_count); - - if (entries) - env()->heap()->free(entries, 0); + Table_wrapper table(xsdt); + uint64_t * entries = reinterpret_cast(table.table() + 1); + _parse_tables(entries, table.entry_count(entries)); } else { /* running (32bit) or (64bit and xsdt isn't valid) */ - uint32_t entries_count; - uint32_t * entries; - - { - Table_wrapper table(rsdt); - entries = table.copy_entries(entries_count); - } - - _parse_tables(entries, entries_count); - - if (entries) - env()->heap()->free(entries, 0); + Table_wrapper table(rsdt); + uint32_t * entries = reinterpret_cast(table.table() + 1); + _parse_tables(entries, table.entry_count(entries)); } + + /* free up memory of elements not of any use */ + Element::clean_list(); + + /* free up io memory */ + acpi_memory().free_io_memory(); } }; diff --git a/repos/os/src/drivers/acpi/acpi.h b/repos/os/src/drivers/acpi/acpi.h index 31805a67a..dfd0fd16d 100644 --- a/repos/os/src/drivers/acpi/acpi.h +++ b/repos/os/src/drivers/acpi/acpi.h @@ -14,33 +14,27 @@ #ifndef _ACPI_H_ #define _ACPI_H_ -#include #include -struct Acpi +namespace Acpi { - /** - * Constructor - */ - Acpi(); - /** * Generate config file for pci_drv containing pointers to the * extended PCI config space (since PCI Express) */ - static void create_pci_config_file(char * config_space, + void create_pci_config_file(char * config_space, Genode::size_t config_space_max); /** * Rewrite PCI-config space with GSIs found in ACPI tables. */ - static void configure_pci_devices(Pci::Session_capability &session); + void configure_pci_devices(Pci::Session_capability &session); /** * Return override GSI for IRQ */ - static unsigned override(unsigned irq, unsigned *mode); -}; + unsigned override(unsigned irq, unsigned *mode); +} #endif /* _ACPI_H_ */ diff --git a/repos/os/src/drivers/acpi/memory.h b/repos/os/src/drivers/acpi/memory.h new file mode 100644 index 000000000..3503b9bc6 --- /dev/null +++ b/repos/os/src/drivers/acpi/memory.h @@ -0,0 +1,102 @@ +/* + * \brief Internal acpi io memory management + * \author Alexander Boettcher + * \date 2015-02-16 + */ + + /* + * 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 _MEMORY_H_ +#define _MEMORY_H_ + +#include +#include + +namespace Acpi { class Memory; } + +class Acpi::Memory +{ + private: + + class Io_mem : public Genode::List::Element + { + private: + Genode::Io_mem_connection _io_mem; + + public: + Io_mem(Genode::addr_t phys) : _io_mem(phys, 0x1000UL) { } + + Genode::Io_mem_dataspace_capability dataspace() + { + return _io_mem.dataspace(); + } + }; + + Genode::addr_t const ACPI_REGION_SIZE_LOG2; + Genode::Rm_connection _rm_acpi; + Genode::addr_t const _acpi_base; + Genode::Allocator_avl _range; + Genode::List _io_mem_list; + + public: + + Memory() + : + /* 1 GB range */ + ACPI_REGION_SIZE_LOG2(30), + _rm_acpi(~0UL, 1UL << ACPI_REGION_SIZE_LOG2), + _acpi_base(Genode::env()->rm_session()->attach(_rm_acpi.dataspace())), + _range(Genode::env()->heap()) + { + _range.add_range(0, 1UL << ACPI_REGION_SIZE_LOG2); + } + + Genode::addr_t phys_to_virt(Genode::addr_t const phys, Genode::addr_t const p_size) + { + using namespace Genode; + + /* 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) { + PERR("acpi table out of range - 0x%lx not in [0x%lx,0x%lx)", + phys, high, high + (1UL << ACPI_REGION_SIZE_LOG2) - 1); + throw -1; + } + + addr_t const phys_aligned = phys & _align_mask(12); + addr_t const size_aligned = align_addr(p_size + (phys & _align_offset(12)), 12); + + 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).is_ok()) + continue; + + /* allocate acpi page as io memory */ + Io_mem *mem = new (Genode::env()->heap()) Io_mem(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); + } + + return _acpi_base + (phys & _align_offset(ACPI_REGION_SIZE_LOG2)); + } + + void free_io_memory() + { + while (Io_mem * io_mem = _io_mem_list.first()) { + _io_mem_list.remove(io_mem); + destroy(Genode::env()->heap(), io_mem); + } + } +}; + +#endif /* _MEMORY_H_ */