From babe1d1026e2af514c1bd1f1c9f7290437a3d334 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Sun, 15 Feb 2015 23:31:56 +0100 Subject: [PATCH] acpi: amend handling MMIO regions for ACPI tables The handling of MMIO regions now supports more pathological cases with weird cross references. Also, MMIO regions are releases after the parsing is done. Fixes #998 --- repos/os/src/drivers/acpi/acpi.cc | 140 +++++++++++++---------------- repos/os/src/drivers/acpi/acpi.h | 16 ++-- repos/os/src/drivers/acpi/memory.h | 102 +++++++++++++++++++++ 3 files changed, 170 insertions(+), 88 deletions(-) create mode 100644 repos/os/src/drivers/acpi/memory.h diff --git a/repos/os/src/drivers/acpi/acpi.cc b/repos/os/src/drivers/acpi/acpi.cc index f5b07d40ca..7608a0350e 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 31805a67a4..dfd0fd16d1 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 0000000000..3503b9bc61 --- /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_ */