platform/pc: implement IOAPIC

genodelabs/genode#5066
This commit is contained in:
Johannes Schlatow 2024-03-20 18:18:09 +01:00 committed by Christian Helmuth
parent 5006b009cb
commit c767c2b129
5 changed files with 271 additions and 0 deletions

View File

@ -379,6 +379,12 @@ class Driver::Device : private List_model<Device>::Element
fn(idx++, ipr.range, ipr.bar); });
}
template <typename FN> void for_each_property(FN const & fn) const
{
_property_list.for_each([&] (Property const & p) {
fn(p.name, p.value); });
}
template <typename FN> void for_pci_config(FN const & fn) const
{
/*

View File

@ -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 <ioapic.h>
unsigned Driver::Ioapic::_read_max_entries()
{
write<Ioregsel>(Ioregsel::IOAPICVER);
return read<Iowin::Maximum_entries>() + 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>(Ioregsel::IOREDTBL + 2 * idx + 1);
Irte::access_t irte { read<Iowin>() };
irte <<= 32;
/* read lower 32 bit */
write<Ioregsel>(Ioregsel::IOREDTBL + 2 * idx);
irte |= read<Iowin>();
/* remap entry */
Irte::Remap::set(irte, 1);
Irte::Index::set(irte, to & 0xFF);
/* write upper 32 bit */
write<Ioregsel>(Ioregsel::IOREDTBL + 2 * idx + 1);
write<Iowin>((Iowin::access_t)(irte >> 32));
/* write lower 32 bit */
write<Ioregsel>(Ioregsel::IOREDTBL + 2 * idx);
write<Iowin>((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>(Ioregsel::IOREDTBL + 2 * idx + 1);
Irte::access_t irte { read<Iowin>() };
irte <<= 32;
/* read lower 32 bit */
write<Ioregsel>(Ioregsel::IOREDTBL + 2 * idx);
irte |= read<Iowin>();
/* 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) };
}

View File

@ -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 <os/attached_mmio.h>
#include <util/register_set.h>
#include <base/allocator.h>
/* Platform-driver includes */
#include <device.h>
#include <irq_controller.h>
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<Index_0_14, Index_15> { };
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> & 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<Irq_controller_factory> & registry)
: Driver::Irq_controller_factory(registry, Device::Type { "ioapic" }),
_env(env)
{ }
void create(Allocator & alloc, Registry<Irq_controller> & 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_ */

View File

@ -16,6 +16,7 @@
#include <common.h>
#include <pci.h>
#include <intel/io_mmu.h>
#include <ioapic.h>
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();
}
};

View File

@ -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)/../../