genode/repos/os/src/drivers/acpi/acpi.cc

1365 lines
32 KiB
C++

/*
* \brief ACPI parsing and PCI rewriting code
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \author Alexander Boettcher
* \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-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.
*/
/* base includes */
#include <io_mem_session/connection.h>
#include <util/mmio.h>
/* os includes */
#include <os/reporter.h>
#include <os/attached_rom_dataspace.h>
#include "acpi.h"
#include "memory.h"
using namespace Genode;
/* enable debugging output */
static const bool verbose = false;
/**
* Generic Apic structure
*/
struct Apic_struct
{
enum Types { SRC_OVERRIDE = 2 };
uint8_t type;
uint8_t length;
bool is_override() { return type == SRC_OVERRIDE; }
Apic_struct *next() { return reinterpret_cast<Apic_struct *>((uint8_t *)this + length); }
} __attribute__((packed));
struct Mcfg_struct
{
uint64_t base;
uint16_t pci_seg;
uint8_t pci_bus_start;
uint8_t pci_bus_end;
uint32_t reserved;
Mcfg_struct *next() {
return reinterpret_cast<Mcfg_struct *>((uint8_t *)this + sizeof(*this)); }
} __attribute__((packed));
/* ACPI spec 5.2.12.5 */
struct Apic_override : Apic_struct
{
uint8_t bus;
uint8_t irq;
uint32_t gsi;
uint16_t flags;
} __attribute__((packed));
struct Dmar_struct_header;
/* 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<uint8_t *>(this); }
/* MADT ACPI structure */
Apic_struct *apic_struct() { return reinterpret_cast<Apic_struct *>(&creator_rev + 3); }
Apic_struct *end() { return reinterpret_cast<Apic_struct *>(signature + size); }
/* MCFG ACPI structure */
Mcfg_struct *mcfg_struct() { return reinterpret_cast<Mcfg_struct *>(&creator_rev + 3); }
Mcfg_struct *mcfg_end() { return reinterpret_cast<Mcfg_struct *>(signature + size); }
Dmar_struct_header *dmar_header() { return reinterpret_cast<Dmar_struct_header *>(this); }
} __attribute__((packed));
struct Dmar_common : Genode::Mmio
{
struct Type : Register<0x0, 16> {
enum { DRHD= 0U, RMRR = 0x1U, ATSR = 0x2U, RHSA = 0x3U }; };
struct Length : Register<0x2, 16> { };
Dmar_common(addr_t mmio) : Genode::Mmio(mmio) { }
};
/* DMA Remapping Reporting Structure - Intel VT-d IO Spec - 8.1. */
struct Dmar_struct_header : Generic
{
enum { INTR_REMAP_MASK = 0x1U };
uint8_t width;
uint8_t flags;
uint8_t reserved[10];
/* DMAR Intel VT-d structures */
addr_t dmar_entry_start() { return reinterpret_cast<addr_t>(&creator_rev + 4); }
addr_t dmar_entry_end() { return reinterpret_cast<addr_t>(signature + size); }
template <typename FUNC>
void apply(FUNC const &func = [] () { } )
{
addr_t addr = dmar_entry_start();
do {
Dmar_common dmar(addr);
func(dmar);
addr = dmar.base + dmar.read<Dmar_common::Length>();
} while (addr < dmar_entry_end());
}
struct Dmar_struct_header * clone()
{
size_t const size = dmar_entry_end() - reinterpret_cast<addr_t>(this);
char * clone = new (env()->heap()) char[size];
memcpy(clone, this, size);
return reinterpret_cast<Dmar_struct_header *>(clone);
}
} __attribute__((packed));
/* Intel VT-d IO Spec - 8.3.1. */
struct Device_scope : Genode::Mmio
{
Device_scope(addr_t a) : Genode::Mmio(a) { }
struct Type : Register<0x0, 8> { enum { PCI_END_POINT = 0x1 }; };
struct Length : Register<0x1, 8> { };
struct Bus : Register<0x5, 8> { };
struct Path : Register_array<0x6, 8, 1, 16> {
struct Dev : Bitfield<0,8> { };
struct Func : Bitfield<8,8> { };
};
unsigned count() const { return (read<Length>() - 6) / 2; }
};
/* DMA Remapping Reporting structure - Intel VT-d IO Spec - 8.3. */
struct Dmar_rmrr : Genode::Mmio
{
struct Length : Register<0x02, 16> { };
struct Base : Register<0x08, 64> { };
struct Limit : Register<0x10, 64> { };
Dmar_rmrr(addr_t a) : Genode::Mmio(a) { }
template <typename FUNC>
void apply(FUNC const &func = [] () { } )
{
addr_t addr = base + 24;
do {
Device_scope scope(addr);
func(scope);
addr = scope.base + scope.read<Device_scope::Length>();
} while (addr < base + read<Length>());
}
};
class Dmar_entry : public List<Dmar_entry>::Element
{
private:
Dmar_struct_header *_header;
public:
Dmar_entry(Dmar_struct_header * h) : _header(h) { }
template <typename FUNC>
void apply(FUNC const &func = [] () { } ) { _header->apply(func); }
static List<Dmar_entry> *list()
{
static List<Dmar_entry> _list;
return &_list;
}
};
/**
* List that holds interrupt override information
*/
class Irq_override : public List<Irq_override>::Element
{
private:
uint32_t _irq; /* source IRQ */
uint32_t _gsi; /* target GSI */
uint32_t _flags; /* interrupt flags */
public:
Irq_override(uint32_t irq, uint32_t gsi, uint32_t flags)
: _irq(irq), _gsi(gsi), _flags(flags) { }
static List<Irq_override> *list()
{
static List<Irq_override> _list;
return &_list;
}
uint32_t irq() const { return _irq; }
uint32_t gsi() const { return _gsi; }
uint32_t flags() const { return _flags; }
};
/**
* List that holds the result of the mcfg table parsing which are pointers
* to the extended pci config space - 4k for each device.
*/
class Pci_config_space : public List<Pci_config_space>::Element
{
public:
uint32_t _bdf_start;
uint32_t _func_count;
addr_t _base;
Pci_config_space(uint32_t bdf_start, uint32_t func_count, addr_t base)
:
_bdf_start(bdf_start), _func_count(func_count), _base(base) { }
static List<Pci_config_space> *list()
{
static List<Pci_config_space> _list;
return &_list;
}
};
static Acpi::Memory & acpi_memory()
{
static Acpi::Memory _memory;
return _memory;
}
/**
* ACPI table wrapper that for mapping tables to this address space
*/
class Table_wrapper
{
private:
addr_t _base; /* table base address */
Generic *_table; /* pointer to table header */
char _name[5]; /* table name */
/* return offset of '_base' to page boundary */
addr_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; }
/**
* Determine maximal number of potential elements
*/
template <typename T>
addr_t entry_count(T *)
{
return (_table->size - sizeof (Generic)) / sizeof(T);
}
/**
* 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");}
/**
* Is this a MADT table
*/
bool is_madt() { return _cmp("APIC"); }
/**
* Is this a MCFG table
*/
bool is_mcfg() { return _cmp("MCFG"); }
/**
* Look for DSDT and SSDT tables
*/
bool is_searched() const { return _cmp("DSDT") || _cmp("SSDT"); }
/**
* Is this a DMAR table
*/
bool is_dmar() { return _cmp("DMAR"); }
/**
* Parse override structures
*/
void parse_madt()
{
Apic_struct *apic = _table->apic_struct();
for (; apic < _table->end(); apic = apic->next()) {
if (!apic->is_override())
continue;
Apic_override *o = static_cast<Apic_override *>(apic);
PINF("MADT IRQ %u -> GSI %u flags: %x", o->irq, o->gsi, o->flags);
Irq_override::list()->insert(new (env()->heap())
Irq_override(o->irq, o->gsi, o->flags));
}
}
void parse_mcfg() const
{
Mcfg_struct *mcfg = _table->mcfg_struct();
for (; mcfg < _table->mcfg_end(); mcfg = mcfg->next()) {
PINF("MCFG BASE 0x%llx seg %02x bus %02x-%02x", mcfg->base,
mcfg->pci_seg, mcfg->pci_bus_start, mcfg->pci_bus_end);
/* bus_count * up to 32 devices * 8 function per device * 4k */
uint32_t bus_count = mcfg->pci_bus_end - mcfg->pci_bus_start + 1;
uint32_t func_count = bus_count * 32 * 8;
uint32_t bus_start = mcfg->pci_bus_start * 32 * 8;
Pci_config_space::list()->insert(
new (env()->heap()) Pci_config_space(bus_start, func_count, mcfg->base));
}
}
void parse_dmar() const
{
Dmar_struct_header *head = _table->dmar_header();
PLOG("%u bit DMA physical addressable%s\n", head->width + 1,
head->flags & Dmar_struct_header::INTR_REMAP_MASK ?
" , IRQ remapping supported" : "");
head->apply([&] (Dmar_common const &dmar) {
PLOG("DMA remapping structure type=%u", dmar.read<Dmar_common::Type>());
});
Dmar_entry::list()->insert(new (env()->heap()) Dmar_entry(head->clone()));
}
Table_wrapper(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<Generic *>(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);
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;
}
}
};
/**
* PCI routing information
*/
class Pci_routing : public List<Pci_routing>::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; }
uint32_t device() const { return _adr >> 16; }
/* 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>::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 */
uint32_t _para_len; /* parameters to be skipped */
bool _valid; /* true if this is a valid element */
bool _routed; /* has the PCI information been read */
List<Pci_routing> *_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 };
/* ComputationalData - ACPI 19.2.3 */
enum { BYTE_PREFIX = 0xa, WORD_PREFIX = 0xb, DWORD_PREFIX = 0xc, QWORD_PREFIX=0xe };
/* 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(bool update_size = false)
{
Element *parent = list()->first();
/* set length of previous element */
if (update_size && 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(true);
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;
if (verbose)
PDBG("Indirect %s", name);
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 = 0, bool package_op4 = false)
:
_type(0), _size(0), _size_len(0), _name(0), _name_len(0), _bdf(0),
_data(data), _para_len(0), _valid(false), _routed(false), _pci(0)
{
if (!data)
return;
/* 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:
/* ACPI 19.2.5.1 - NameOp NameString DataRefObject */
if (!(_name_len = _read_name_len()))
return;
_valid = true;
/* ACPI 19.2.3 DataRefObject */
switch (data[_name_len + 1]) {
case QWORD_PREFIX: _para_len += 4;
case DWORD_PREFIX: _para_len += 2;
case WORD_PREFIX: _para_len += 1;
case BYTE_PREFIX: _para_len += 1;
default: _para_len += 1;
}
/* set absolute name of this element */
_set_name();
_type = data[0];
dump();
default:
return;
}
}
/**
* 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;
_para_len = other._para_len;
if (other._name) {
_name = (char *)env()->heap()->alloc(other._name_len);
memcpy(_name, other._name, _name_len);
}
}
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:
virtual ~Element()
{
if (_name)
env()->heap()->free(_name, _name_len);
}
/**
* 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; }
uint32_t bdf() const { return _bdf; }
bool is_device() const { return _type == SUB_DEVICE; }
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<Element> *list()
{
static List<Element> _list;
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
*/
List<Pci_routing> *pci_list()
{
if (!_pci)
_pci = new (env()->heap()) List<Pci_routing>();
return _pci;
}
/**
* Parse elements of table
*/
static void parse(Generic *table)
{
uint8_t const *data = table->data();
for (; 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();
/* skip name */
data += NAME_LEN;
/* skip rest of structure if known */
if (e.is_device_name())
data += e._para_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<unsigned short *>(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;
}
template <typename T>
void _parse_tables(T * entries, uint32_t count)
{
/* search for SSDT and DSDT tables */
for (uint32_t i = 0; i < count; i++) {
uint32_t dsdt = 0;
{
Table_wrapper table(entries[i]);
if (table.is_facp())
dsdt = *reinterpret_cast<uint32_t *>(table->signature + 40);
if (table.is_searched()) {
if (verbose)
PDBG("Found %s", table.name());
Element::parse(table.table());
}
if (table.is_madt()) {
PDBG("Found MADT");
table.parse_madt();
}
if (table.is_mcfg()) {
PDBG("Found MCFG");
table.parse_mcfg();
}
if (table.is_dmar()) {
PDBG("Found DMAR");
table.parse_dmar();
}
}
if (dsdt) {
Table_wrapper table(dsdt);
if (table.is_searched()) {
if (verbose)
PDBG("Found dsdt %s", table.name());
Element::parse(table.table());
}
}
}
}
public:
Acpi_table()
{
Io_mem_session_capability io_mem;
uint8_t * ptr_rsdp = _rsdp(io_mem);
struct rsdp {
char signature[8];
uint8_t checksum;
char oemid[6];
uint8_t revision;
/* table pointer at 16 byte offset in RSDP structure (5.2.5.3) */
uint32_t rsdt;
/* With ACPI 2.0 */
uint32_t len;
uint64_t xsdt;
uint8_t checksum_extended;
uint8_t reserved[3];
} __attribute__((packed));
struct rsdp * rsdp = reinterpret_cast<struct rsdp *>(ptr_rsdp);
if (!rsdp) {
if (verbose)
PDBG("No rsdp structure found");
return;
}
if (verbose) {
uint8_t oem[7];
memcpy(oem, rsdp->oemid, 6);
oem[6] = 0;
PDBG("ACPI revision %u of OEM '%s', rsdt:0x%x xsdt:0x%llx",
rsdp->revision, oem, rsdp->rsdt, rsdp->xsdt);
}
addr_t const rsdt = rsdp->rsdt;
addr_t const xsdt = rsdp->xsdt;
uint8_t const acpi_revision = rsdp->revision;
/* drop rsdp io_mem mapping since rsdt/xsdt may overlap */
env()->parent()->close(io_mem);
if (acpi_revision != 0 && xsdt && sizeof(addr_t) != sizeof(uint32_t)) {
/* running 64bit and xsdt is valid */
Table_wrapper table(xsdt);
uint64_t * entries = reinterpret_cast<uint64_t *>(table.table() + 1);
_parse_tables(entries, table.entry_count(entries));
} else {
/* running (32bit) or (64bit and xsdt isn't valid) */
Table_wrapper table(rsdt);
uint32_t * entries = reinterpret_cast<uint32_t *>(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();
}
};
/**
* Parse acpi table
*/
static void init_acpi_table()
{
static Acpi_table table;
}
void Acpi::generate_report()
{
init_acpi_table();
enum { REPORT_SIZE = 4 * 4096 };
static Reporter acpi("acpi", REPORT_SIZE);
acpi.enabled(true);
Genode::Reporter::Xml_generator xml(acpi, [&] () {
for (Pci_config_space *e = Pci_config_space::list()->first(); e;
e = e->next())
{
xml.node("bdf", [&] () {
char number[20];
Genode::snprintf(number, sizeof(number), "%u", e->_bdf_start);
xml.attribute("start", number);
Genode::snprintf(number, sizeof(number), "%u", e->_func_count);
xml.attribute("count", number);
Genode::snprintf(number, sizeof(number), "0x%lx", e->_base);
xml.attribute("base", number);
});
}
for (Irq_override *i = Irq_override::list()->first(); i; i = i->next())
{
xml.node("irq_override", [&] () {
char number[8];
Genode::snprintf(number, sizeof(number), "%u", i->irq());
xml.attribute("irq", number);
Genode::snprintf(number, sizeof(number), "%u", i->gsi());
xml.attribute("gsi", number);
Genode::snprintf(number, sizeof(number), "0x%x", i->flags());
xml.attribute("flags", number);
});
}
/* lambda definition for scope evaluation in rmrr */
auto func_scope = [&] (Device_scope const &scope)
{
xml.node("scope", [&] () {
char number[8];
Genode::snprintf(number, sizeof(number), "%u",
scope.read<Device_scope::Bus>());
xml.attribute("bus_start", number);
for (unsigned j = 0 ; j < scope.count(); j++) {
xml.node("path", [&] () {
char number[8];
Genode::snprintf(number, sizeof(number), "0x%x",
scope.read<Device_scope::Path::Dev>(j));
xml.attribute("dev", number);
Genode::snprintf(number, sizeof(number), "0x%x",
scope.read<Device_scope::Path::Func>(j));
xml.attribute("func", number);
});
}
});
};
for (Dmar_entry *entry = Dmar_entry::list()->first();
entry; entry = entry->next()) {
entry->apply([&] (Dmar_common const &dmar) {
if (dmar.read<Dmar_common::Type>() != Dmar_common::Type::RMRR)
return;
Dmar_rmrr rmrr(dmar.base);
xml.node("rmrr", [&] () {
char number[20];
Genode::snprintf(number, sizeof(number), "0x%llx",
rmrr.read<Dmar_rmrr::Base>());
xml.attribute("start", number);
Genode::snprintf(number, sizeof(number), "0x%llx",
rmrr.read<Dmar_rmrr::Limit>());
xml.attribute("end", number);
rmrr.apply(func_scope);
});
});
}
{
Element *e = Element::list()->first();
for (; e; e = e->next()) {
if (!e->is_device())
continue;
Pci_routing *r = e->pci_list()->first();
for (; r; r = r->next()) {
xml.node("routing", [&] () {
char number[8];
Genode::snprintf(number, sizeof(number), "0x%x", r->gsi());
xml.attribute("gsi", number);
Genode::snprintf(number, sizeof(number), "0x%x", e->bdf());
xml.attribute("bridge_bdf", number);
Genode::snprintf(number, sizeof(number), "0x%x", r->device());
xml.attribute("device", number);
Genode::snprintf(number, sizeof(number), "0x%x", r->pin());
xml.attribute("device_pin", number);
});
}
}
}
});
}