diff --git a/os/src/drivers/acpi/README b/os/src/drivers/acpi/README
new file mode 100644
index 0000000000..c9fc7ab91c
--- /dev/null
+++ b/os/src/drivers/acpi/README
@@ -0,0 +1,35 @@
+This directory contains an implementation of a simple ACPI parser
+
+Behavior
+--------
+
+This server should be used when using a kernel (like Fiasco.OC or Nova) that
+takes advantage of x86's APIC. The server traverses the ACPI tables and sets the
+interrupt line of devices within the PCI config space to the GSIs found in the
+ACPI tables.
+
+Usage
+-----
+
+Start the 'acpi_drv' in your Genode environment. Do not start the 'pci_drv'
+since this will be used as a slave of the 'acpi_drv'. You still must load the
+'pci_drv' in your boot loader.
+
+Configuration snipped:
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+Limitations and known issues
+----------------------------
+
+Currently there is no interface to set the interrupt mode for Irq_sessions
+(e.g., level or edge triggered). This is required by Fiasco.OCs kernel
+interface. We regard this as future work.
diff --git a/os/src/drivers/acpi/acpi.cc b/os/src/drivers/acpi/acpi.cc
new file mode 100644
index 0000000000..47052f03c9
--- /dev/null
+++ b/os/src/drivers/acpi/acpi.cc
@@ -0,0 +1,1073 @@
+/*
+ * \brief ACPI parsing and PCI rewriting code
+ * \author Sebastian Sumpf
+ * \date 2012-02-25
+ *
+ * This code parses the DSDT and SSDT-ACPI tables and extracts the PCI-bridge
+ * to GSI interrupt mappings as described by "ATARE: ACPI Tables and Regular
+ * Expressions, Bernhard Kauer, TU Dresden technical report TUD-FI09-09,
+ * Dresden, Germany, August 2009".
+ */
+
+ /*
+ * Copyright (C) 2009-2012 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.
+ */
+
+#include
+#include
+#include
+#include "acpi.h"
+
+using namespace Genode;
+
+/* Enable debugging output */
+static const bool verbose = false;
+
+/* ACPI spec 5.2.6 */
+struct Generic
+{
+ uint8_t signature[4];
+ uint32_t size;
+ uint8_t rev;
+ uint8_t checksum;
+ uint8_t oemid[6];
+ uint8_t oemtabid[8];
+ uint32_t oemrev;
+ uint8_t creator[4];
+ uint32_t creator_rev;
+
+ uint8_t const *data() { return reinterpret_cast(this); }
+};
+
+
+/**
+ * ACPI table wrapper that for mapping tables to this address space
+ */
+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, 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 */
+ uint32_t _offset() const { return (_base & 0xfff); }
+
+ /* compare table name with 'name' */
+ bool _cmp(char const *name) const { return !memcmp(_table->signature, name, 4); }
+
+ public:
+
+ /**
+ * Accessors
+ */
+ Generic* operator -> () { return _table; }
+ Generic* table() { return _table; }
+ char const *name() const { return _name; }
+
+ /**
+ * Copy table data to 'ptr'
+ */
+ void copy_entries(uint32_t *ptr)
+ {
+ memcpy(ptr, _table + 1, _table->size - sizeof(Generic));
+ }
+
+ /**
+ * Create ACPI checksum (is zero if valid)
+ */
+ static uint8_t checksum(uint8_t *table, uint32_t count)
+ {
+ uint8_t sum = 0;
+ while (count--)
+ sum += table[count];
+ return sum;
+ }
+
+ /**
+ * Is this the FACP table
+ */
+ bool is_facp() const { return _cmp("FACP");}
+
+ /**
+ * Look for DSDT and SSDT tables
+ */
+ bool is_searched() const { return _cmp("DSDT") || _cmp("SSDT"); }
+
+ Table_wrapper(addr_t base)
+ : _base(base), _io_mem(0), _table(0)
+ {
+ _map(0x1000);
+
+ /* remap if table size is larger than current size */
+ if (_offset() + _table->size > 0x1000) {
+ size_t size = _table->size;
+ _cleanup();
+ _map(size);
+ }
+
+ memset(_name, 0, 5);
+ memcpy(_name, _table->signature, 4);
+
+ if (verbose)
+ PDBG("Table mapped '%s' at %p (from %lx) size %x", _name, _table, _base, _table->size);
+
+ if (checksum((uint8_t *)_table, _table->size)) {
+ PERR("Checksum mismatch for %s", _name);
+ throw -1;
+ }
+ }
+
+ ~Table_wrapper() { _cleanup(); }
+};
+
+
+/**
+ * PCI routing information
+ */
+class Pci_routing : public List::Element
+{
+ private:
+
+ uint32_t _adr; /* address (6.1.1) */
+ uint32_t _pin; /* IRQ pin */
+ uint32_t _gsi; /* global system interrupt */
+
+ public:
+
+ Pci_routing(uint32_t adr, uint32_t pin, uint32_t gsi)
+ : _adr(adr), _pin(pin), _gsi(gsi) { }
+
+ /**
+ * Compare BDF of this object to given bdf
+ */
+ bool match_bdf(uint32_t bdf) const { return (_adr >> 16) == ((bdf >> 3) & 0x1f); }
+
+ /**
+ * Accessors
+ */
+ uint32_t pin() const { return _pin; }
+ uint32_t gsi() const { return _gsi; }
+
+ /* debug */
+ void dump() { if (verbose) PDBG("Pci: adr %x pin %x gsi: %u", _adr, _pin, _gsi); }
+};
+
+
+/**
+ * A table element (method, device, scope or name)
+ */
+class Element : public List::Element
+{
+ private:
+
+ uint8_t _type; /* the type of this element */
+ uint32_t _size; /* size in bytes */
+ uint32_t _size_len; /* length of size in bytes */
+ char *_name; /* name of element */
+ uint32_t _name_len; /* length of name in bytes */
+ uint32_t _bdf; /* bus device function */
+ uint8_t const *_data; /* pointer to the data section */
+ bool _valid; /* true if this is a valid element */
+ bool _routed; /* has the PCI information been read */
+ List *_pci; /* list of PCI routing elements for this element */
+
+ /* packages we are looking for */
+ enum { DEVICE = 0x5b, SUB_DEVICE = 0x82, DEVICE_NAME = 0x8, SCOPE = 0x10, METHOD = 0x14, PACKAGE_OP = 0x12 };
+
+ /* name prefixes */
+ enum { ROOT_PREFIX = 0x5c, PARENT_PREFIX = 0x5e, DUAL_NAME_PREFIX = 0x2e, MULTI_NAME_PREFIX = 0x2f, };
+
+ /* default signature length of ACPI elements */
+ enum { NAME_LEN = 4 };
+
+ /* return address of 'name' in Element */
+ uint8_t const *_name_addr() const { return _data + _size_len + 1; }
+
+ /**
+ * See ACPI spec 5.4
+ */
+ uint32_t _read_size_encoding()
+ {
+ /*
+ * Most sig. 2 bits set number of bytes (1-4), next two bits are only used
+ * in one byte encoding, if bits are set in all two areas it is no valid
+ * size
+ */
+ uint32_t encoding = _data[1];
+ return ((encoding & 0xc0) && (encoding & 0x30)) ? 0 : 1 + (encoding >> 6);
+ }
+
+ /**
+ * See ACPI spec. 5.4
+ */
+ void _read_size()
+ {
+ _size = _data[1] & 0x3f;
+ for (uint32_t i = 1; i < _read_size_encoding(); i++)
+ _size += _data[i + 1] << (8 * i - 4);
+ }
+
+ /**
+ * Return the length of the name prefix
+ */
+ uint32_t _prefix_len(uint8_t const *name)
+ {
+ uint8_t const *n = name;
+ if (*n == ROOT_PREFIX)
+ n++;
+ else while (*n == PARENT_PREFIX)
+ n++;
+
+ if (*n == DUAL_NAME_PREFIX)
+ n++;
+ else if (*n == MULTI_NAME_PREFIX)
+ n += 2;
+
+ return n - name;
+ }
+
+ /**
+ * Return length of a given name
+ */
+ uint32_t _read_name_len(uint8_t const *name = 0)
+ {
+ uint8_t const *name_addr = name ? name : _name_addr();
+ uint8_t const *n = name_addr;
+
+ /* skip prefixes (ACPI 18.2.1) */
+ if (*n == ROOT_PREFIX)
+ n++;
+ else while (*n == PARENT_PREFIX)
+ n++;
+
+ /* two names follow */
+ if (*n == DUAL_NAME_PREFIX) {
+ /* check first + second name */
+ if (_check_name_segment(n + 1) && _check_name_segment(n + NAME_LEN + 1))
+
+ /* prefixes + dual prefixe + 2xname */
+ return n - name_addr + 1 + 2 * NAME_LEN;
+ }
+
+ /* multiple name segments ('MultiNamePrefix SegCount NameSeg(SegCount)') */
+ else if (*n == MULTI_NAME_PREFIX) {
+ uint32_t i;
+ for (i = 0; i < n[1]; i++)
+
+ /* check segment */
+ if (!_check_name_segment(n + 2 + NAME_LEN * i))
+ return 0;
+
+ if (i)
+
+ /* prefix + multi prefix + seg. count + name length * seg. count */
+ return n - name_addr + 2 + NAME_LEN * i;
+ }
+
+ /* single name segment */
+ else if (_check_name_segment(n))
+
+ /* prefix + name */
+ return n - name_addr + NAME_LEN;
+
+ return n - name_addr;
+ }
+
+ /**
+ * Check if name is a valid ASL name (18.2.1)
+ */
+ bool _check_name_segment(uint8_t const *name)
+ {
+ for (uint32_t i = 0; i < NAME_LEN; i++) {
+ uint8_t c = name[i];
+ if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' ||
+ (i > 0 && c >= '0' && c <= '9')))
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Return parent of this element
+ */
+ Element *_parent()
+ {
+ Element *parent = list()->first();
+
+ /* set length of previous element */
+ if (parent && !parent->size())
+ parent->size(_data - parent->data());
+
+ /* find parent */
+ for (; parent; parent = parent->next())
+
+ /* parent surrounds child */
+ if ((parent->data() < _data) && ((parent->data() + parent->size()) > _data))
+ break;
+
+ return parent;
+ }
+
+ /**
+ * Set the name of this object
+ */
+ void _set_name()
+ {
+ uint8_t const *name = _name_addr();
+ Element *parent = _parent();
+ uint32_t prefix_len = _prefix_len(name);
+
+ if (_name_len <= prefix_len) {
+ _name_len = 0;
+ return;
+ }
+
+ _name_len -= prefix_len;
+
+ /* is absolute name */
+ if (*name == ROOT_PREFIX || !parent) {
+ _name = (char *)env()->heap()->alloc(_name_len);
+ memcpy(_name, name + prefix_len, _name_len);
+ }
+ else {
+ /* skip parts */
+ int parent_len = parent->_name_len;
+ parent_len = (parent_len < 0) ? 0 : parent_len;
+
+ /* skip parent prefix */
+ for (uint32_t p = 0; name[p] == PARENT_PREFIX; p++, parent_len -= NAME_LEN) ;
+ _name = (char *)env()->heap()->alloc(_name_len + parent_len);
+
+ memcpy(_name, parent->_name, parent_len);
+ memcpy(_name + parent_len, name + prefix_len, _name_len);
+
+ _name_len += parent_len;
+ }
+ }
+
+ /**
+ * Compare 'sub_string' with '_name'
+ */
+ Element *_compare(char const *sub_string, uint32_t skip = 0)
+ {
+ size_t sub_len = strlen(sub_string);
+
+ Element *other = list()->first();
+ while (other) {
+
+ if ((other->_name_len == _name_len + sub_len - skip) &&
+
+ /* compare our part */
+ !(memcmp(other->_name, _name, _name_len - skip)) &&
+
+ /* compare other part */
+ !memcmp(other->_name + _name_len - skip, sub_string, sub_len))
+ return other;
+
+ other = other->next();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Read value of element that matches 'sub_string'
+ */
+ uint32_t _value(char const *sub_string)
+ {
+ Element *other = _compare(sub_string);
+
+ if (!other || !other->is_device_name())
+ return 0;
+
+ uint32_t data_len;
+ uint32_t data = other->_read(other->_data + other->_read_name_len() + 1, data_len);
+
+ return data_len ? data : 0;
+ }
+
+ /**
+ * Read data if return length of data read
+ */
+ uint32_t _read(uint8_t const *data, uint32_t &length)
+ {
+ switch (data[0]) {
+ case 0: length = 1; return 0;
+ case 1: length = 1; return 1;
+ case 0xff: length = 1; return 0xffffffff;
+ case 0x0a: length = 2; return data[1];
+ case 0x0b: length = 3; return data[1] | (data[2] << 8);
+ case 0x0c: length = 5; return data[1] | (data[2] << 8) | (data[3] << 16) | (data[4] << 24);
+ default: length = 0; return 0;
+ }
+ }
+
+ /**
+ * Try to find an element containing four values of data
+ */
+ Element _packet(uint8_t const *table, long len)
+ {
+ for (uint8_t const *data = table; data < table + len; data++) {
+ Element e(data, true);
+ if (e.valid())
+ return Element(data, true);
+ }
+ return Element();
+ }
+
+ /**
+ * Try to locate _PRT table and its GSI values for device
+ * (data has to be located within the device data)
+ */
+ void _direct_prt(Element *dev)
+ {
+ uint32_t len = 0;
+
+ for (uint32_t offset = 0; offset < size(); offset += len) {
+ len = 1;
+
+ /* search for four value packet */
+ Element e = _packet(_data + offset, size() - offset);
+
+ if (!e.valid())
+ continue;
+
+ /* read values */
+ uint32_t val[4];
+ uint32_t read_offset = 0;
+ uint32_t i;
+ for (i = 0; i < 4; i++) {
+ val[i] = e._read(e.data() + e.size_len() + 2 + read_offset, len);
+ if (!len) break;
+ read_offset += len;
+ }
+
+ if (i == 4) {
+ Pci_routing * r = new (env()->heap()) Pci_routing(val[0], val[1], val[3]);
+
+ /* set _ADR, _PIN, _GSI */
+ dev->pci_list()->insert(r);
+ dev->pci_list()->first()->dump();
+ }
+
+ len = len ? (e.data() - (_data + offset)) + e.size() : 1;
+ }
+ }
+
+ /**
+ * Search for _PRT outside of device
+ */
+ void _indirect_prt(Element *dev)
+ {
+ uint32_t name_len;
+ uint32_t found = 0;
+ for (uint32_t offset = size_len(); offset < size(); offset += name_len) {
+ name_len = _read_name_len(_data + offset);
+ if (name_len) {
+ if (!found++)
+ continue;
+
+ char name[name_len + 1];
+ memcpy(name, _data + offset, name_len);
+ name[name_len] = 0;
+
+ char name_dev[dev->_name_len + 1];
+ memcpy(name_dev, dev->_name, dev->_name_len);
+ name_dev[dev->_name_len] = 0;
+
+ if (verbose)
+ PDBG("Indirect %s %s", name, name_dev);
+
+ for (uint32_t skip = 0; skip <= dev->_name_len / NAME_LEN; skip++) {
+ Element *e = dev->_compare(name, skip * NAME_LEN);
+ if (e)
+ e->_direct_prt(dev);
+ }
+ }
+ else
+ name_len = 1;
+ }
+ }
+
+ Element(uint8_t const *data, bool package_op4 = false)
+ :
+ _type(0), _size(0), _size_len(0), _name(0), _name_len(0), _bdf(0), _data(data),
+ _valid(false), _routed(false), _pci(0)
+ {
+ /* special handle for four value packet */
+ if (package_op4) {
+ /* scan for data package with four entries */
+ if (data[0] != PACKAGE_OP)
+ return;
+
+ /* area there four entries */
+ if (!(_size_len = _read_size_encoding()) || _data[1 + _size_len] != 0x04)
+ return;
+
+ _read_size();
+ _valid = true;
+ return;
+ }
+
+ switch (data[0]) {
+ case DEVICE:
+
+ data++; _data++;
+
+ if (data[0] != SUB_DEVICE)
+ return;
+
+ case SCOPE:
+ case METHOD:
+
+ if (!(_size_len = _read_size_encoding()))
+ return;
+
+ _read_size();
+
+ if (_size) {
+ /* check if element is larger than parent */
+ Element *p = _parent();
+ for (; p; p = p->_parent())
+ if (p->_size && p->_size < _size)
+ return;
+ }
+
+ case DEVICE_NAME:
+
+ if (!(_name_len = _read_name_len()))
+ return;
+
+ _valid = true;
+
+ /* set absolute name of this element */
+ _set_name();
+ _type = data[0];
+
+ dump();
+
+ default:
+
+ return;
+ }
+ }
+
+ /**
+ * Default constructor
+ */
+ Element() : _valid(false) {}
+
+ /**
+ * Copy constructor
+ */
+ Element(Element const &other)
+ {
+ _type = other._type;
+ _size = other._size;
+ _size_len = other._size_len;
+ _name_len = other._name_len;
+ _bdf = other._bdf;
+ _data = other._data;
+ _valid = other._valid;
+ _routed = other._routed;
+ _pci = other._pci;
+
+ if (other._name) {
+ _name = (char *)env()->heap()->alloc(other._name_len);
+ memcpy(_name, other._name, _name_len);
+ }
+ }
+
+ 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; }
+
+ /**
+ * Debugging
+ */
+ void dump()
+ {
+ if (!verbose)
+ return;
+
+ char n[_name_len + 1];
+ memcpy(n, _name, _name_len);
+ n[_name_len] = 0;
+ PDBG("Found package %x size %u name_len %u name: %s", _data[0], _size, _name_len, n);
+ }
+
+ public:
+
+ /**
+ * Accessors
+ */
+ uint32_t size() const { return _size; }
+ void size(uint32_t size) { _size = size; }
+ uint32_t size_len() const { return _size_len; }
+ uint8_t const *data() const { return _data; }
+ bool valid() const { return _valid; }
+
+ static bool supported_acpi_format()
+ {
+ /* check if _PIC method is present */
+ for (Element *e = list()->first(); e; e = e->next())
+ if (e->_name_len == 4 && !memcmp(e->_name, "_PIC", 4))
+ return true;
+ return false;
+ }
+
+ /**
+ * Return list of elements
+ */
+ static List *list()
+ {
+ static List _list;
+ return &_list;
+ }
+
+ /**
+ * Return list of PCI information for this element
+ */
+ List *pci_list()
+ {
+ if (!_pci)
+ _pci = new (env()->heap()) List();
+ return _pci;
+ }
+
+ /**
+ * Parse elements of table
+ */
+ static void parse(Generic *table)
+ {
+ for (uint8_t const *data = table->data(); data < table->data() + table->size; data++) {
+ Element e(data);
+
+ if (!e.valid() || !e._name_len)
+ continue;
+
+ if (data + e.size() > table->data() + table->size)
+ break;
+
+ Element *i = new (env()->heap()) Element(e);
+ list()->insert(i);
+
+ /* skip header */
+ data += e.size_len();
+ }
+
+ parse_bdf();
+ }
+
+ /**
+ * Parse BDF and GSI information
+ */
+ static void parse_bdf()
+ {
+ for (Element *e = list()->first(); e; e = e->next()) {
+
+ if (!e->is_device() || e->_routed)
+ continue;
+
+ /* address (high word = device, low word = function) (6.1.1) */
+ uint32_t adr = e->_value("_ADR");
+
+ /* Base bus number (6.5.5) */
+ uint32_t bbn = e->_value("_BBN");
+
+ /* Segment object located under host bridge (6.5.6) */
+ uint32_t seg = e->_value("_SEG");
+
+ /* build BDF */
+ e->_bdf = (seg << 16) | (bbn << 8) | (adr >> 16) << 3 | (adr & 0xffff);
+
+ /* add routing */
+ Element *prt = e->_compare("_PRT");
+ if (prt) prt->dump();
+
+ if (prt) {
+ if (verbose)
+ PDBG("Scanning device %x", e->_bdf);
+
+ prt->_direct_prt(e);
+ prt->_indirect_prt(e);
+ }
+
+ e->_routed = true;
+ }
+ }
+
+ /**
+ * Search for GSI of given device, bridge, and pin
+ */
+ static uint32_t search_gsi(uint32_t device_bdf, uint32_t bridge_bdf, uint32_t pin)
+ {
+ Element *e = list()->first();
+
+ for (; e; e = e->next()) {
+ if (!e->is_device() || e->_bdf != bridge_bdf)
+ continue;
+
+ Pci_routing *r = e->pci_list()->first();
+ for (; r; r = r->next()) {
+ if (r->match_bdf(device_bdf) && r->pin() == pin) {
+ if (verbose) PDBG("Found GSI: %u device : %x pin %u", r->gsi(), device_bdf, pin);
+ return r->gsi();
+ }
+ }
+ }
+ throw -1;
+ }
+};
+
+
+/**
+ * Locate and parse PCI tables we are looking for
+ */
+class Acpi_table
+{
+ private:
+
+ /* BIOS range to scan for RSDP */
+ enum { BIOS_BASE = 0xe0000, BIOS_SIZE = 0x20000 };
+
+ /**
+ * Map table and return address and session cap
+ */
+ uint8_t *_map_io(addr_t base, size_t size, Io_mem_session_capability &cap)
+ {
+ Io_mem_connection io_mem(base, size);
+ io_mem.on_destruction(Io_mem_connection::KEEP_OPEN);
+ Io_mem_dataspace_capability io_ds = io_mem.dataspace();
+ if (!io_ds.valid())
+ throw -1;
+
+ uint8_t *ret = env()->rm_session()->attach(io_ds, size);
+ cap = io_mem.cap();
+ return ret;
+ }
+
+ /**
+ * Search for RSDP pointer signature in area
+ */
+ uint8_t *_search_rsdp(uint8_t *area)
+ {
+ for (addr_t addr = 0; area && addr < BIOS_SIZE; addr += 16)
+ if (!memcmp(area + addr, "RSD PTR ", 8) && !Table_wrapper::checksum(area + addr, 20))
+ return area + addr;
+
+ throw -2;
+ }
+
+ /**
+ * Return 'Root System Descriptor Pointer' (ACPI spec 5.2.5.1)
+ */
+ uint8_t *_rsdp(Io_mem_session_capability &cap)
+ {
+ uint8_t *area = 0;
+
+ /* try BIOS area (0xe0000 - 0xfffffh)*/
+ try {
+ area = _search_rsdp(_map_io(BIOS_BASE, BIOS_SIZE, cap));
+ return area;
+ } catch (...) { env()->parent()->close(cap); }
+
+ /* search EBDA (BIOS addr + 0x40e) */
+ try {
+ area = _map_io(0x0, 0x1000, cap);
+ if (area) {
+ unsigned short base = (*reinterpret_cast(area + 0x40e)) << 4;
+ env()->parent()->close(cap);
+ area = _map_io(base, 1024, cap);
+ area = _search_rsdp(area);
+ }
+ return area;
+ } catch (...) { env()->parent()->close(cap); }
+
+ return 0;
+ }
+
+ public:
+
+ Acpi_table()
+ {
+ Io_mem_session_capability io_mem;
+ uint8_t *rsdp = _rsdp(io_mem);
+
+ if (verbose)
+ PDBG("RSDP %p", rsdp);
+
+ if (!rsdp)
+ return;
+
+ uint32_t rsdt_entries[36];
+ memset(rsdt_entries, 0, sizeof(uint32_t)*36);
+
+ {
+ /* table pointer at 16 byte offset in RSDP structure (5.2.5.3) */
+ Table_wrapper rsdt(*reinterpret_cast(rsdp + 0x10));
+ rsdt.copy_entries(rsdt_entries);
+ }
+
+ env()->parent()->close(io_mem);
+
+ /* search for SSDT and DSDT tables */
+ for (int i = 0; rsdt_entries[i]; i++) {
+ uint32_t dsdt = 0;
+ {
+ Table_wrapper table(rsdt_entries[i]);
+ if (table.is_facp())
+ dsdt = *reinterpret_cast(table->signature + 40);
+
+ if (table.is_searched()) {
+
+ if (verbose)
+ PDBG("Found %s", table.name());
+
+ Element::parse(table.table());
+ }
+ }
+
+ if (dsdt) {
+ Table_wrapper table(dsdt);
+ if (table.is_searched()) {
+ if (verbose)
+ PDBG("Found dsdt %s", table.name());
+
+ Element::parse(table.table());
+ }
+ }
+ }
+ }
+};
+
+
+/**
+ * Pci::Device_client extensions identifies bridges and adds IRQ line re-write
+ */
+class Pci_client : public ::Pci::Device_client
+{
+ public:
+
+ Pci_client(Pci::Device_capability &cap) : ::Pci::Device_client(cap) { }
+
+ /**
+ * Return true if this is a PCI-PCI bridge
+ */
+ bool is_bridge()
+ {
+ enum { BRIDGE_CLASS = 0x6 };
+ if ((class_code() >> 16) != BRIDGE_CLASS)
+ return false;
+
+ /* see PCI bridge spec (3.2) */
+ enum { BRIDGE = 0x1 };
+ uint16_t header = config_read(0xe, ::Pci::Device::ACCESS_16BIT);
+
+ /* skip multi function flag 0x80) */
+ return ((header & 0x3f) != BRIDGE) ? false : true;
+ }
+
+ /**
+ * Return bus-device function of this device
+ */
+ uint32_t bdf()
+ {
+ uint8_t bus, dev, func;
+ bus_address(&bus, &dev, &func);
+ return (bus << 8) | ((dev & 0x1f) << 3) | (func & 0x7);
+ }
+
+ /**
+ * Return IRQ pin
+ */
+ uint32_t irq_pin()
+ {
+ return ((config_read(0x3c, ::Pci::Device::ACCESS_32BIT) >> 8) & 0xf);
+ }
+
+ /**
+ * Return IRQ line
+ */
+ uint8_t irq_line()
+ {
+ return (config_read(0x3c, ::Pci::Device::ACCESS_8BIT));
+ }
+
+ /**
+ * Write line to config space
+ */
+ void irq_line(uint32_t gsi)
+ {
+ config_write(0x3c, gsi, ::Pci::Device::ACCESS_8BIT);
+ }
+};
+
+
+/**
+ * List of PCI-bridge devices
+ */
+class Pci_bridge : public List::Element
+{
+ private:
+
+ /* PCI config space fields of bridge */
+ uint32_t _bdf;
+ uint32_t _secondary_bus;
+ uint32_t _subordinate_bus;
+
+ Pci_bridge(Pci_client &client) : _bdf(client.bdf())
+ {
+ /* PCI bridge spec 3.2.5.3, 3.2.5.4 */
+ uint32_t bus = client.config_read(0x18, ::Pci::Device::ACCESS_32BIT);
+ _secondary_bus = (bus >> 8) & 0xff;
+ _subordinate_bus = (bus >> 16) & 0xff;
+
+ if (verbose)
+ PDBG("New bridge: bdf %x se: %u su: %u", _bdf, _secondary_bus, _subordinate_bus);
+ }
+
+ static List *_list()
+ {
+ static List list;
+ return &list;
+ }
+
+ public:
+
+ /**
+ * Scan PCI bus for bridges
+ */
+ Pci_bridge(Pci::Session_capability &session)
+ {
+ Pci::Session_client pci(session);
+ Pci::Device_capability device_cap = pci.first_device(), prev_device_cap;
+
+ /* search for bridges */
+ while (device_cap.valid()) {
+ prev_device_cap = device_cap;
+ Pci_client device(device_cap);
+
+ if (device.is_bridge())
+ _list()->insert(new (env()->heap()) Pci_bridge(device));
+
+ device_cap = pci.next_device(device_cap);
+ pci.release_device(prev_device_cap);
+ }
+ }
+
+ /**
+ * Locate BDF of bridge belonging to given bdf
+ */
+ static uint32_t bridge_bdf(uint32_t bdf)
+ {
+ Pci_bridge *bridge = _list()->first();
+ uint32_t bus = bdf >> 8;
+
+ for (; bridge; bridge = bridge->next())
+ if (bridge->_secondary_bus <= bus && bridge->_subordinate_bus >= bus)
+ return bridge->_bdf;
+
+ return 0;
+ }
+
+};
+
+
+/**
+ * Debugging
+ */
+static void dump_bdf(uint32_t a, uint32_t b, uint32_t pin)
+{
+ if (verbose)
+ PDBG("Device bdf %02x:%02x.%u (%x) bridge %02x:%02x.%u (%x) Pin: %u",
+ (a >> 8), (a >> 3) & 0x1f, (a & 0x7), a,
+ (b >> 8), (b >> 3) & 0x1f, (b & 0x7), b, pin);
+}
+
+static void dump_rewrite(uint32_t bdf, uint8_t line, uint8_t gsi)
+{
+ PINF("Rewriting %02x:%02x.%u IRQ: %02u -> GSI: %02u",
+ (bdf >> 8), (bdf >> 3) & 0x1f, (bdf & 0x7), line, gsi);
+}
+
+
+/**
+ * Rewrite GSIs of PCI config space
+ */
+void Acpi::rewrite_irq(Pci::Session_capability &session)
+{
+ static Acpi_table table;
+ static Pci_bridge bridge(session);
+
+ /* if no _PIC method could be found return */
+ if (Element::supported_acpi_format())
+ PINF("ACPI table format is supported by this driver");
+ else {
+ PERR("ACPI table format not supported (is too old) by this driver");
+ return;
+ }
+
+ Pci::Session_client pci(session);
+ Pci::Device_capability device_cap = pci.first_device(), prev_device_cap;
+
+ while (device_cap.valid()) {
+ prev_device_cap = device_cap;
+ Pci_client device(device_cap);
+
+ if (!device.is_bridge()) {
+ uint32_t device_bdf = device.bdf();
+ uint32_t bridge_bdf = Pci_bridge::bridge_bdf(device_bdf);
+ uint32_t irq_pin = device.irq_pin();
+ if (irq_pin) {
+ dump_bdf(device_bdf, bridge_bdf, irq_pin - 1);
+ try {
+ uint8_t gsi = Element::search_gsi(device_bdf, bridge_bdf, irq_pin - 1);
+ dump_rewrite(device_bdf, device.irq_line(), gsi);
+ device.irq_line(gsi);
+ } catch (...) { }
+ }
+ }
+
+ device_cap = pci.next_device(device_cap);
+ pci.release_device(prev_device_cap);
+ }
+}
+
diff --git a/os/src/drivers/acpi/acpi.h b/os/src/drivers/acpi/acpi.h
new file mode 100644
index 0000000000..b0b6661e3b
--- /dev/null
+++ b/os/src/drivers/acpi/acpi.h
@@ -0,0 +1,31 @@
+/*
+ * \brief Interface to ACPI
+ * \author Sebastian Sumpf
+ * \date 2012-02-25
+ */
+
+ /*
+ * Copyright (C) 2009-2012 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 _ACPI_H_
+#define _ACPI_H_
+
+#include
+#include
+
+class Acpi
+{
+ public:
+
+ /**
+ * Rewrite PCI-config space with GSIs found in ACPI tables
+ */
+ static void rewrite_irq(Pci::Session_capability &session);
+};
+
+#endif /* _ACPI_H_ */
+
diff --git a/os/src/drivers/acpi/main.cc b/os/src/drivers/acpi/main.cc
new file mode 100644
index 0000000000..6f0dbbffdb
--- /dev/null
+++ b/os/src/drivers/acpi/main.cc
@@ -0,0 +1,155 @@
+/*
+ * \brief Service and session interface
+ * \author Sebastian Sumpf
+ * \date 2012-02-25
+ *
+ * The 'acpi_drv' provides the 'PCI' after rewriting the IRQ information of PCI
+ * devices. For this it uses the 'pci_drv' as a client and forwards the session
+ * capability of the 'pci_drv' afterwards
+ */
+
+ /*
+ * Copyright (C) 2009-2012 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.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "acpi.h"
+
+namespace Pci {
+
+ struct Provider
+ {
+ bool ready_to_use() { return root().valid(); }
+
+ virtual Genode::Root_capability root() = 0;
+ };
+
+ /**
+ * Root interface of PCI service
+ */
+ class Root : public Genode::Rpc_object >
+ {
+ private:
+
+ Provider &_pci_provider;
+
+ public:
+
+ Genode::Session_capability session(Session_args const &args)
+ {
+ if (!args.is_valid_string()) throw Invalid_args();
+
+ if (!_pci_provider.ready_to_use())
+ throw Unavailable();
+
+ try {
+ return Genode::Root_client(_pci_provider.root()).session(args.string());
+ } catch (...) {
+ throw Unavailable();
+ }
+ }
+
+ void upgrade(Genode::Session_capability, Upgrade_args const &) { }
+
+ void close(Genode::Session_capability session)
+ {
+ Genode::Root_client(_pci_provider.root()).close(session);
+ }
+
+ Root(Provider &pci_provider) : _pci_provider(pci_provider) { }
+ };
+}
+
+typedef Genode::Capability > Service_capability;
+
+class Pci_policy : public Genode::Slave_policy, public Pci::Provider
+{
+ private:
+
+ Genode::Root_capability _cap;
+ Genode::Rpc_entrypoint &_ep;
+
+ protected:
+
+ char const **_permitted_services() const
+ {
+ static char const *permitted_services[] = {
+ "CAP", "RM", "LOG", "IO_PORT", 0 };
+
+ return permitted_services;
+ };
+
+ /**
+ * Parse ACPI tables and announce slave PCI service
+ */
+ void _acpi_session()
+ {
+ Pci::Session_capability session;
+ const char *args = "ram_quota=4K";
+
+ try {
+ using namespace Genode;
+ session = static_cap_cast(Root_client(_cap).session(args));
+ } catch (...) { return; }
+
+ Acpi::rewrite_irq(session);
+
+ /* announce service PCI to parent */
+ static Pci::Root pci_root(*this);
+ Genode::env()->parent()->announce(_ep.manage(&pci_root));
+
+ Genode::Root_client(_cap).close(session);
+ }
+
+ public:
+
+ Pci_policy(Genode::Rpc_entrypoint &slave_ep, Genode::Rpc_entrypoint &ep)
+ : Slave_policy("pci_drv", slave_ep), _ep(ep)
+ { }
+
+ bool announce_service(const char *service_name,
+ Genode::Root_capability root,
+ Genode::Allocator *alloc)
+ {
+ /* wait for 'pci_drv' to announce the PCI service */
+ if (Genode::strcmp(service_name, "PCI"))
+ return false;
+
+ _cap = root;
+
+ /* connect session and start ACPI parsing */
+ _acpi_session();
+
+ return true;
+ }
+
+ Genode::Root_capability root() { return _cap; }
+};
+
+
+int main(int argc, char **argv)
+{
+ using namespace Genode;
+
+ enum { STACK_SIZE = 2*4096 };
+ static Cap_connection cap;
+ static Rpc_entrypoint ep(&cap, STACK_SIZE, "acpi_ep");
+
+ /* use 'pci_drv' as slave service */
+ static Rpc_entrypoint pci_ep(&cap, STACK_SIZE, "pci_slave");
+ static Pci_policy pci_policy(pci_ep, ep);
+ static Genode::Slave pci_slave(pci_ep, pci_policy, 512 * 1024);
+
+ Genode::sleep_forever();
+ return 0;
+}
+
diff --git a/os/src/drivers/acpi/x86/target.mk b/os/src/drivers/acpi/x86/target.mk
new file mode 100644
index 0000000000..8b0f346998
--- /dev/null
+++ b/os/src/drivers/acpi/x86/target.mk
@@ -0,0 +1,9 @@
+TARGET = acpi_drv
+REQUIRES = x86
+SRC_CC = main.cc acpi.cc
+LIBS = cxx env server process
+
+INC_DIR = $(PRG_DIR)/..
+
+vpath main.cc $(PRG_DIR)/..
+vpath acpi.cc $(PRG_DIR)/..