diff --git a/repos/pc/src/driver/platform/pc/intel/fault_handler.h b/repos/pc/src/driver/platform/pc/intel/fault_handler.h new file mode 100644 index 0000000000..da19021d49 --- /dev/null +++ b/repos/pc/src/driver/platform/pc/intel/fault_handler.h @@ -0,0 +1,30 @@ +/* + * \brief Intel IOMMU fault handler + * \author Johannes Schlatow + * \date 2025-04-11 + */ + +/* + * Copyright (C) 2025 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__INTEL__FAULT_HANDLER_H_ +#define _SRC__DRIVERS__PLATFORM__INTEL__FAULT_HANDLER_H_ + +#include + +namespace Intel { + + class Fault_handler : Interface + { + public: + + virtual bool iq_error() = 0; + virtual void handle_faults() = 0; + }; +} + +#endif /* _SRC__DRIVERS__PLATFORM__INTEL__FAULT_HANDLER_H_ */ diff --git a/repos/pc/src/driver/platform/pc/intel/invalidator.cc b/repos/pc/src/driver/platform/pc/intel/invalidator.cc index ed1b4eaf39..f0f06bfa89 100644 --- a/repos/pc/src/driver/platform/pc/intel/invalidator.cc +++ b/repos/pc/src/driver/platform/pc/intel/invalidator.cc @@ -13,7 +13,6 @@ /* local includes */ #include -#include /** * Clear IOTLB. @@ -120,6 +119,20 @@ void Intel::Register_invalidator::invalidate_all(Domain_id domain_id, Pci::rid_t } +void Intel::Queued_invalidator::_wait_for_completion() +{ + while (!_empty()) { + if (_fault_handler.iq_error()) { + /* reset tail pointer to recover from invalidation queue error */ + _queue_mmio.write(_queue_mmio.read()); + + _fault_handler.handle_faults(); + return; + } + } +} + + /* Clear interrupt entry cache */ void Intel::Queued_invalidator::invalidate_irq(unsigned idx, bool global) { @@ -130,8 +143,7 @@ void Intel::Queued_invalidator::invalidate_irq(unsigned idx, bool global) _next(); - /* wait for completion */ - while (!_empty()); + _wait_for_completion(); } @@ -159,8 +171,7 @@ void Intel::Queued_invalidator::invalidate_iotlb(Domain_id domain_id) _next(); - /* wait for completion */ - while (!_empty()); + _wait_for_completion(); /* * Note: At the moment we have no practical benefit from implementing @@ -203,8 +214,7 @@ void Intel::Queued_invalidator::invalidate_context(Domain_id domain_id, Pci::rid _next(); - /* wait for completion */ - while (!_empty()); + _wait_for_completion(); } diff --git a/repos/pc/src/driver/platform/pc/intel/invalidator.h b/repos/pc/src/driver/platform/pc/intel/invalidator.h index a21fff1fbc..c68e34a3ce 100644 --- a/repos/pc/src/driver/platform/pc/intel/invalidator.h +++ b/repos/pc/src/driver/platform/pc/intel/invalidator.h @@ -21,11 +21,11 @@ /* local includes */ #include +#include namespace Intel { using namespace Genode; - class Io_mmu; /* forward declaration */ class Invalidator; class Register_invalidator; class Queued_invalidator; @@ -213,6 +213,8 @@ class Intel::Queued_invalidator : public Invalidator bool _empty() { return _queue_mmio.read() == _queue_mmio.read(); } + void _wait_for_completion(); + Descriptor::access_t *_tail() { Descriptor::access_t *tail = @@ -234,6 +236,8 @@ class Intel::Queued_invalidator : public Invalidator _queue_mmio.write(tail_offset); } + Fault_handler & _fault_handler; + public: void invalidate_irq(unsigned, bool) override; @@ -242,9 +246,12 @@ class Intel::Queued_invalidator : public Invalidator void invalidate_all(Domain_id domain = Domain_id { Domain_id::INVALID }, Pci::rid_t = 0) override; - Queued_invalidator(Genode::Env & env, addr_t queue_reg_base) + Queued_invalidator(Genode::Env & env, + Intel::Fault_handler & fh, + addr_t queue_reg_base) : _queue_mmio({(char*)queue_reg_base, 56}), - _queue(env.ram(), env.rm(), 4096, Cache::CACHED) + _queue(env.ram(), env.rm(), 4096, Cache::CACHED), + _fault_handler(fh) { /* set tail register to zero */ _queue_mmio.write(0); diff --git a/repos/pc/src/driver/platform/pc/intel/io_mmu.cc b/repos/pc/src/driver/platform/pc/intel/io_mmu.cc index 8ac26aed55..3a6a339d32 100644 --- a/repos/pc/src/driver/platform/pc/intel/io_mmu.cc +++ b/repos/pc/src/driver/platform/pc/intel/io_mmu.cc @@ -137,25 +137,33 @@ void Intel::Io_mmu::_handle_faults() if (_fault_irq.constructed()) _fault_irq->ack_irq(); - if (read()) { - if (read()) - error("Fault recording overflow"); + handle_faults(); +} - if (read()) - error("Invalidation queue error"); - if (read()) - error("Invalidation completion error"); +void Intel::Io_mmu::handle_faults() +{ + Fault_status::access_t status = read(); - if (read()) - error("Invalidation time-out error"); + if (Fault_status::Overflow::get(status)) + error("Fault recording overflow"); - /* acknowledge all faults */ - write(0x7d); + if (Fault_status::Iqe::get(status)) + error("Invalidation queue error: ", Hex(read())); + if (Fault_status::Ice::get(status)) + error("Invalidation completion error"); + + if (Fault_status::Ite::get(status)) + error("Invalidation time-out error"); + + /* acknowledge all faults */ + write(status); + + if (Fault_status::Pending::get(status)) { error("Faults records for ", name()); unsigned num_registers = read() + 1; - for (unsigned i = read(); ; i = (i + 1) % num_registers) { + for (unsigned i = Fault_status::Fri::get(status); ; i = (i + 1) % num_registers) { Fault_record_hi::access_t hi = read_fault_record(i); if (!Fault_record_hi::Fault::get(hi)) @@ -180,6 +188,15 @@ void Intel::Io_mmu::_handle_faults() } +bool Intel::Io_mmu::iq_error() +{ + Fault_status::access_t status = read(); + return Fault_status::Iqe::get(status) || + Fault_status::Ice::get(status) || + Fault_status::Ite::get(status); +} + + void Intel::Io_mmu::generate(Xml_generator & xml) { xml.node("intel", [&] () { @@ -386,7 +403,7 @@ void Intel::Io_mmu::_init() if (read()) { /* enable queued invalidation if supported */ - _queued_invalidator.construct(_env, base() + 0x80); + _queued_invalidator.construct(_env, *this, base() + 0x80); _global_command(true); } else { /* use register-based invalidation interface as fallback */ diff --git a/repos/pc/src/driver/platform/pc/intel/io_mmu.h b/repos/pc/src/driver/platform/pc/intel/io_mmu.h index 4ebabe7d9a..42361d18ce 100644 --- a/repos/pc/src/driver/platform/pc/intel/io_mmu.h +++ b/repos/pc/src/driver/platform/pc/intel/io_mmu.h @@ -33,6 +33,7 @@ #include #include #include +#include #include namespace Intel { @@ -56,7 +57,8 @@ namespace Intel { class Intel::Io_mmu : private Attached_mmio<0x800>, public Driver::Io_mmu, - private Translation_table_registry + private Translation_table_registry, + public Fault_handler { public: @@ -353,6 +355,9 @@ class Intel::Io_mmu : private Attached_mmio<0x800>, /* not using extended interrupt mode (x2APIC) */ }; + struct Invalidation_queue_error : Register<0xb0,32> + { }; + struct Fault_status : Register<0x34, 32> { /* fault record index */ @@ -563,6 +568,13 @@ class Intel::Io_mmu : private Attached_mmio<0x800>, void flush_write_buffer(); + /** + * Fault_handler interface + */ + + bool iq_error() override; + void handle_faults() override; + /** * Io_mmu suspend/resume interface */