diff --git a/repos/os/src/driver/platform/device.h b/repos/os/src/driver/platform/device.h index 873198e644..b76330e77f 100644 --- a/repos/os/src/driver/platform/device.h +++ b/repos/os/src/driver/platform/device.h @@ -379,6 +379,12 @@ class Driver::Device : private List_model::Element fn(idx++, ipr.range, ipr.bar); }); } + template void for_each_property(FN const & fn) const + { + _property_list.for_each([&] (Property const & p) { + fn(p.name, p.value); }); + } + template void for_pci_config(FN const & fn) const { /* diff --git a/repos/pc/src/driver/platform/pc/ioapic.cc b/repos/pc/src/driver/platform/pc/ioapic.cc new file mode 100644 index 0000000000..2fa3395ed2 --- /dev/null +++ b/repos/pc/src/driver/platform/pc/ioapic.cc @@ -0,0 +1,108 @@ +/* + * \brief IOAPIC implementation + * \author Johannes Schlatow + * \date 2024-03-20 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* local includes */ +#include + +unsigned Driver::Ioapic::_read_max_entries() +{ + write(Ioregsel::IOAPICVER); + return read() + 1; +} + + +bool Driver::Ioapic::handles_irq(unsigned irq) +{ + if (irq < _irq_start || irq >= _irq_start + _max_entries) + return false; + + return true; +} + + +/** + * Sets remapping bit and destination index in IOAPIC redirection table. + * + * Note: Expected to be called only if handles_irq() returned true. + */ +void Driver::Ioapic::remap_irq(unsigned from, unsigned to) +{ + const unsigned idx = from - _irq_start; + + /* read upper 32 bit */ + write(Ioregsel::IOREDTBL + 2 * idx + 1); + Irte::access_t irte { read() }; + irte <<= 32; + + /* read lower 32 bit */ + write(Ioregsel::IOREDTBL + 2 * idx); + irte |= read(); + + /* remap entry */ + Irte::Remap::set(irte, 1); + Irte::Index::set(irte, to & 0xFF); + + /* write upper 32 bit */ + write(Ioregsel::IOREDTBL + 2 * idx + 1); + write((Iowin::access_t)(irte >> 32)); + + /* write lower 32 bit */ + write(Ioregsel::IOREDTBL + 2 * idx); + write((Iowin::access_t)(irte & 0xFFFFFFFF)); +} + + +/** + * Reads and returns IRQ configuration from IOAPIC redirection table. + * + * Note: Expected to be called only if handles_irq() returned true. + */ +Driver::Ioapic::Irq_config Driver::Ioapic::irq_config(unsigned irq) +{ + const unsigned idx = irq - _irq_start; + + /* read upper 32 bit */ + write(Ioregsel::IOREDTBL + 2 * idx + 1); + Irte::access_t irte { read() }; + irte <<= 32; + + /* read lower 32 bit */ + write(Ioregsel::IOREDTBL + 2 * idx); + irte |= read(); + + /* extract trigger mode */ + Irq_session::Trigger trigger { Irq_session::TRIGGER_UNCHANGED }; + switch (Irte::Trigger_mode::get(irte)) { + case Irte::Trigger_mode::EDGE: + trigger = Irq_session::TRIGGER_EDGE; + break; + case Irte::Trigger_mode::LEVEL: + trigger = Irq_session::TRIGGER_LEVEL; + break; + } + + /* extract destination mode */ + Irq_config::Mode mode { Irq_config::Mode::INVALID }; + switch (Irte::Destination_mode::get(irte)) { + case Irte::Destination_mode::PHYSICAL: + mode = Irq_config::Mode::PHYSICAL; + break; + case Irte::Destination_mode::LOGICAL: + mode = Irq_config::Mode::LOGICAL; + break; + } + + return { mode, trigger, + Irte::Vector::get(irte), + Irte::Destination::get(irte) }; +} diff --git a/repos/pc/src/driver/platform/pc/ioapic.h b/repos/pc/src/driver/platform/pc/ioapic.h new file mode 100644 index 0000000000..ef3ad44381 --- /dev/null +++ b/repos/pc/src/driver/platform/pc/ioapic.h @@ -0,0 +1,152 @@ +/* + * \brief IOAPIC implementation + * \author Johannes Schlatow + * \date 2024-03-20 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__DRIVERS__PLATFORM__PC__IOAPIC_H_ +#define _SRC__DRIVERS__PLATFORM__PC__IOAPIC_H_ + +/* Genode includes */ +#include +#include +#include + +/* Platform-driver includes */ +#include +#include + +namespace Driver { + using namespace Genode; + + class Ioapic; + class Ioapic_factory; +} + + +class Driver::Ioapic : private Attached_mmio<0x1000>, + public Driver::Irq_controller +{ + private: + + Env & _env; + unsigned _irq_start; + unsigned _max_entries; + + /** + * Registers + */ + + struct Ioregsel : Register<0x00, 32> { + enum { + IOAPICVER = 0x01, + IOREDTBL = 0x10 + }; + }; + + struct Iowin : Register<0x10, 32> { + struct Maximum_entries : Bitfield<16, 8> { }; + }; + + struct Irte : Genode::Register<64> { + struct Index_15 : Bitfield<11, 1> { }; + struct Remap : Bitfield<48, 1> { }; + struct Index_0_14 : Bitfield<49, 15> { }; + struct Index : Bitset_2 { }; + + struct Vector : Bitfield<0,8> { }; + struct Trigger_mode : Bitfield<15,1> { + enum { EDGE = 0, LEVEL = 1 }; }; + + struct Destination_mode : Bitfield<11,1> { + enum { PHYSICAL = 0, LOGICAL = 1 }; }; + + struct Destination : Bitfield<56,8> { }; + }; + + unsigned _read_max_entries(); + + public: + + /** + * Irq_controller interface + */ + void remap_irq(unsigned, unsigned) override; + bool handles_irq(unsigned) override; + Irq_config irq_config(unsigned) override; + + /** + * Constructor/Destructor + */ + + Ioapic(Env & env, + Registry & irq_controller_registry, + Device::Name const & name, + Device::Name const & iommu_name, + Pci::Bdf const & bdf, + Device::Io_mem::Range range, + unsigned irq_start) + : Attached_mmio(env, {(char *)range.start, range.size}), + Driver::Irq_controller(irq_controller_registry, name, iommu_name, bdf), + _env(env), _irq_start(irq_start), _max_entries(_read_max_entries()) + { } +}; + + +class Driver::Ioapic_factory : public Driver::Irq_controller_factory +{ + private: + + Genode::Env & _env; + + public: + + Ioapic_factory(Genode::Env & env, Registry & registry) + : Driver::Irq_controller_factory(registry, Device::Type { "ioapic" }), + _env(env) + { } + + void create(Allocator & alloc, Registry & irq_controller_registry, Device const & device) override + { + using Range = Device::Io_mem::Range; + using Property = Device::Property; + + /* evaluate properties (remapping support, base IRQ, routing id) */ + bool remap { false }; + unsigned irq_start { 0 }; + Pci::rid_t rid { 0 }; + device.for_each_property([&] (Property::Name const & name, Property::Value const & value) { + if (name == "remapping") + ascii_to(value.string(), remap); + else if (name == "irq_start") + ascii_to(value.string(), irq_start); + else if (name == "routing_id") + ascii_to(value.string(), rid); + }); + + /* ignore IOAPIC devices without remapping support */ + if (!remap) + return; + + unsigned iommu_idx = 0; + device.for_each_io_mmu([&] (Device::Io_mmu const & iommu) { + if (iommu_idx++) return; + + device.for_each_io_mem([&] (unsigned idx, Range range, Device::Pci_bar, bool) + { + if (idx == 0) + new (alloc) Ioapic(_env, irq_controller_registry, device.name(), + iommu.name, Pci::Bdf::bdf(rid), range, irq_start); + }); + }, [] () { /* empty fn */ }); + } +}; + +#endif /* _SRC__DRIVERS__PLATFORM__PC__IOAPIC_H_ */ diff --git a/repos/pc/src/driver/platform/pc/spec/x86_64/main.cc b/repos/pc/src/driver/platform/pc/spec/x86_64/main.cc index 7ca7e1afe1..16d9f96ed7 100644 --- a/repos/pc/src/driver/platform/pc/spec/x86_64/main.cc +++ b/repos/pc/src/driver/platform/pc/spec/x86_64/main.cc @@ -16,6 +16,7 @@ #include #include #include +#include namespace Driver { struct Main; }; @@ -33,6 +34,7 @@ struct Driver::Main &Main::_system_update }; Intel::Io_mmu_factory _intel_iommu { _env, _common.io_mmu_factories() }; + Ioapic_factory _ioapic_factory { _env, _common.irq_controller_factories() }; void _handle_config(); void _suspend(String<8>); @@ -51,6 +53,7 @@ struct Driver::Main _system_update(); _common.acquire_io_mmu_devices(); + _common.acquire_irq_controller(); _common.announce_service(); } }; diff --git a/repos/pc/src/driver/platform/pc/spec/x86_64/target.mk b/repos/pc/src/driver/platform/pc/spec/x86_64/target.mk index b4c1927e22..8540b1e838 100644 --- a/repos/pc/src/driver/platform/pc/spec/x86_64/target.mk +++ b/repos/pc/src/driver/platform/pc/spec/x86_64/target.mk @@ -9,7 +9,9 @@ SRC_CC += intel/managed_root_table.cc SRC_CC += intel/io_mmu.cc SRC_CC += intel/page_table.cc SRC_CC += intel/default_mappings.cc +SRC_CC += ioapic.cc INC_DIR += $(PRG_DIR)/../../ vpath intel/%.cc $(PRG_DIR)/../../ +vpath ioapic.cc $(PRG_DIR)/../../