ACPI: Parse MADT

Implemented IRQ service and MATD parsing. Please have a look at the 'README'
file. Fixes issue #151
This commit is contained in:
Sebastian Sumpf 2012-03-15 16:50:09 +01:00 committed by Norman Feske
parent db8058c16f
commit ed8eb91107
5 changed files with 224 additions and 26 deletions

View File

@ -39,20 +39,31 @@ set config {
<start name="acpi">
<resource name="RAM" quantum="2M"/>
<binary name="acpi_drv"/>
<provides><service name="PCI"/></provides>
<provides>
<service name="PCI"/>
<service name="IRQ" />
</provides>
<route>
<service name="ROM"> <parent/> </service>
<any-service> <any-child/> <parent/> </any-service>
<service name="PCI"> <any-child /> </service>
<any-service> <parent/> <any-child /> </any-service>
</route>
</start>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
<route>
<service name="IRQ"><child name="acpi" /></service>
<any-service> <parent /> <any-child /></any-service>
</route>
</start>
<start name="ahci">
<binary name="ahci_drv" />
<resource name="RAM" quantum="10M" />
<provides><service name="Block" /></provides>
<route>
<service name="IRQ"><child name="acpi" /></service>
<any-service> <parent /> <any-child /></any-service>
</route>
</start>
<start name="test-ahci">
<resource name="RAM" quantum="2M" />

View File

@ -6,7 +6,9 @@ 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.
ACPI tables. The 'MADT' table is parsed by the server as well, enabling clients
to use the correct IRQ when 'Interrupt Override' structures are found wihtin the
table.
Usage
-----
@ -15,17 +17,30 @@ 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:
Configuration snipped (please not that IRQ service requests of the 'timer' are
routed to the ACPI-driver):
!<start name="acpi">
! <resource name="RAM" quantum="2M"/>
! <binary name="acpi_drv"/>
! <provides><service name="PCI"/></provides>
! <provides>
! <service name="PCI"/>
! <service name="IRQ" />
! </provides>
! <route>
! <service name="ROM"> <parent/> </service>
! <any-service> <any-child/> <parent/> </any-service>
! <service name="PCI"> <any-child /> </service>
! <any-service> <parent/> <any-child /> </any-service>
! </route>
!</start>
!
!<start name="timer">
! <resource name="RAM" quantum="1M"/>
! <provides><service name="Timer"/></provides>
! <route>
! <service name="IRQ"><child name="acpi" /></service>
! <any-service> <parent /> <any-child /></any-service>
! </route>
!</start>
Limitations and known issues
----------------------------

View File

@ -26,6 +26,30 @@ 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));
/* 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));
/* ACPI spec 5.2.6 */
struct Generic
{
@ -40,6 +64,37 @@ struct Generic
uint32_t creator_rev;
uint8_t const *data() { return reinterpret_cast<uint8_t *>(this); }
/* MADT acpi structures */
Apic_struct *apic_struct() { return reinterpret_cast<Apic_struct *>(&creator_rev + 8); }
Apic_struct *end() { return reinterpret_cast<Apic_struct *>(signature + size); }
};
/**
* 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;
}
bool match(uint32_t irq) const { return irq == _irq; }
uint32_t gsi() const { return _gsi; }
};
@ -119,11 +174,35 @@ class Table_wrapper
*/
bool is_facp() const { return _cmp("FACP");}
/**
* Is this a MADT table
*/
bool is_madt() { return _cmp("APIC"); }
/**
* Look for DSDT and SSDT tables
*/
bool is_searched() const { return _cmp("DSDT") || _cmp("SSDT"); }
/**
* 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);
if (verbose)
PDBG("Found IRQ %u -> GSI %u", o->irq, o->gsi);
Irq_override::list()->insert(new (env()->heap()) Irq_override(o->irq, o->gsi, o->flags));
}
}
Table_wrapper(addr_t base)
: _base(base), _io_mem(0), _table(0)
{
@ -859,6 +938,13 @@ class Acpi_table
Element::parse(table.table());
}
if (table.is_madt()) {
if (verbose)
PDBG("Found MADT");
table.parse_madt();
}
}
if (dsdt) {
@ -1067,3 +1153,15 @@ void Acpi::rewrite_irq(Pci::Session_capability &session)
}
}
/**
* Search override structures
*/
unsigned Acpi::override(unsigned irq)
{
for (Irq_override *i = Irq_override::list()->first(); i; i = i->next())
if (i->match(irq))
return i->gsi();
return irq;
}

View File

@ -25,6 +25,11 @@ class Acpi
* Rewrite PCI-config space with GSIs found in ACPI tables
*/
static void rewrite_irq(Pci::Session_capability &session);
/**
* Return override GSI for IRQ
*/
static unsigned override(unsigned irq);
};
#endif /* _ACPI_H_ */

View File

@ -21,9 +21,73 @@
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <pci_session/client.h>
#include <irq_session/connection.h>
#include <root/component.h>
#include "acpi.h"
/**
* IRQ service
*/
namespace Irq {
typedef Genode::Rpc_object<Genode::Typed_root<Genode::Irq_session> > Irq_session;
/**
* Root interface of IRQ service
*/
class Root : public Irq_session
{
public:
/**
* Remap IRQ number and create IRQ session at parent
*/
Genode::Session_capability session(Root::Session_args const &args)
{
using namespace Genode;
if (!args.is_valid_string()) throw Root::Invalid_args();
long irq_number = Arg_string::find_arg(args.string(), "irq_number").long_value(-1);
/* check for 'MADT' overrides */
irq_number = Acpi::override(irq_number);
/* qouta handling */
size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").long_value(0);
size_t old_quota = 0, new_quota = 0;
Session_capability cap;
/* allocate IRQ at parent*/
try {
old_quota = env()->ram_session()->quota();
Irq_connection irq(irq_number);
new_quota = env()->ram_session()->quota();
irq.on_destruction(Irq_connection::KEEP_OPEN);
cap = irq.cap();
} catch (...) { throw Root::Unavailable(); }
/* check used quota against quota provided */
if (old_quota > new_quota && (old_quota - new_quota) > ram_quota) {
close(cap);
throw Root::Quota_exceeded();
}
return cap;
}
/**
* Close session at parent
*/
void close(Genode::Session_capability session) {
Genode::env()->parent()->close(session); }
void upgrade(Genode::Session_capability session, Upgrade_args const &args) { }
};
}
namespace Pci {
struct Provider
@ -44,6 +108,8 @@ namespace Pci {
public:
Root(Provider &pci_provider) : _pci_provider(pci_provider) { }
Genode::Session_capability session(Session_args const &args)
{
if (!args.is_valid_string()) throw Invalid_args();
@ -58,33 +124,27 @@ namespace Pci {
}
}
void close(Genode::Session_capability session) {
Genode::Root_client(_pci_provider.root()).close(session); }
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<Genode::Typed_root<Pci::Session> > Service_capability;
class Pci_policy : public Genode::Slave_policy, public Pci::Provider
{
private:
Genode::Root_capability _cap;
Genode::Rpc_entrypoint &_ep;
Genode::Rpc_entrypoint &_pci_ep;
Genode::Rpc_entrypoint &_irq_ep;
protected:
char const **_permitted_services() const
{
static char const *permitted_services[] = {
"CAP", "RM", "LOG", "IO_PORT", 0 };
{
static char const *permitted_services[] = { "CAP", "RM", "LOG", "IO_PORT", 0 };
return permitted_services;
};
@ -103,17 +163,22 @@ class Pci_policy : public Genode::Slave_policy, public Pci::Provider
Acpi::rewrite_irq(session);
/* announce service PCI to parent */
/* announce PCI/IRQ services to parent */
static Pci::Root pci_root(*this);
Genode::env()->parent()->announce(_ep.manage(&pci_root));
static Irq::Root irq_root;
Genode::env()->parent()->announce(_pci_ep.manage(&pci_root));
Genode::env()->parent()->announce(_irq_ep.manage(&irq_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)
Pci_policy(Genode::Rpc_entrypoint &slave_ep,
Genode::Rpc_entrypoint &pci_ep,
Genode::Rpc_entrypoint &irq_ep)
: Slave_policy("pci_drv", slave_ep), _pci_ep(pci_ep), _irq_ep(irq_ep)
{ }
bool announce_service(const char *service_name,
@ -144,9 +209,13 @@ int main(int argc, char **argv)
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "acpi_ep");
/* IRQ service */
static Cap_connection irq_cap;
static Rpc_entrypoint irq_ep(&irq_cap, STACK_SIZE, "acpi_irq_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 Pci_policy pci_policy(pci_ep, ep, irq_ep);
static Genode::Slave pci_slave(pci_ep, pci_policy, 512 * 1024);
Genode::sleep_forever();