From ed8eb91107f49f289576ace1efb395c3333328b9 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Thu, 15 Mar 2012 16:50:09 +0100 Subject: [PATCH] ACPI: Parse MADT Implemented IRQ service and MATD parsing. Please have a look at the 'README' file. Fixes issue #151 --- os/run/ahci.run | 17 ++++-- os/src/drivers/acpi/README | 25 +++++++-- os/src/drivers/acpi/acpi.cc | 98 +++++++++++++++++++++++++++++++++ os/src/drivers/acpi/acpi.h | 5 ++ os/src/drivers/acpi/main.cc | 105 +++++++++++++++++++++++++++++------- 5 files changed, 224 insertions(+), 26 deletions(-) diff --git a/os/run/ahci.run b/os/run/ahci.run index 63385e809..aa57e207f 100644 --- a/os/run/ahci.run +++ b/os/run/ahci.run @@ -39,20 +39,31 @@ set config { - + + + + - - + + + + + + + + + + diff --git a/os/src/drivers/acpi/README b/os/src/drivers/acpi/README index c9fc7ab91..42d149f90 100644 --- a/os/src/drivers/acpi/README +++ b/os/src/drivers/acpi/README @@ -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): ! ! ! -! +! +! +! +! ! -! -! +! +! ! ! +! +! +! +! +! +! +! +! +! Limitations and known issues ---------------------------- diff --git a/os/src/drivers/acpi/acpi.cc b/os/src/drivers/acpi/acpi.cc index e3a03d70e..7427cc7d8 100644 --- a/os/src/drivers/acpi/acpi.cc +++ b/os/src/drivers/acpi/acpi.cc @@ -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((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(this); } + + /* MADT acpi structures */ + Apic_struct *apic_struct() { return reinterpret_cast(&creator_rev + 8); } + Apic_struct *end() { return reinterpret_cast(signature + size); } +}; + + +/** + * List that holds interrupt override information + */ +class Irq_override : public List::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 *list() + { + static List _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); + + 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; +} diff --git a/os/src/drivers/acpi/acpi.h b/os/src/drivers/acpi/acpi.h index b0b6661e3..0e067b0de 100644 --- a/os/src/drivers/acpi/acpi.h +++ b/os/src/drivers/acpi/acpi.h @@ -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_ */ diff --git a/os/src/drivers/acpi/main.cc b/os/src/drivers/acpi/main.cc index 6f0dbbffd..e99a40c81 100644 --- a/os/src/drivers/acpi/main.cc +++ b/os/src/drivers/acpi/main.cc @@ -21,9 +21,73 @@ #include #include #include +#include +#include #include "acpi.h" +/** + * IRQ service + */ +namespace Irq { + + typedef Genode::Rpc_object > 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 > 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();