hw_x86_64: Support for dynamic IRQ mode setting

Add a Platform::setup_irq_mode function which enables the IRQ session to
update the trigger mode and polarity of the associated IRQ according to
the session parameters. On ARM this function is a nop.

This change enables the x86_64 platform to support devices which use
arbitrary trigger modes and polarity settings, e.g. AHCI on QEMU and
real hardware.

Fixes #1528.
This commit is contained in:
Adrian-Ken Rueegsegger 2015-05-16 02:34:45 +02:00 committed by Christian Helmuth
parent 1592e78387
commit 965d85d52d
6 changed files with 202 additions and 26 deletions

View File

@ -121,6 +121,16 @@ namespace Genode {
*/ */
static long irq(long const user_irq); static long irq(long const user_irq);
/**
* Setup mode of an IRQ to specified trigger mode and polarity
*
* \param irq_number ID of targeted interrupt
* \param trigger new interrupt trigger mode
* \param polarity new interrupt polarity setting
*/
static void setup_irq_mode(unsigned irq_number, unsigned trigger,
unsigned polarity);
/******************************** /********************************
** Platform_generic interface ** ** Platform_generic interface **
********************************/ ********************************/

View File

@ -36,6 +36,8 @@ namespace Genode
* Programmable interrupt controller for core * Programmable interrupt controller for core
*/ */
class Pic; class Pic;
enum { IRQ_COUNT = 256 };
} }
struct Genode::Irte : Register<64> struct Genode::Irte : Register<64>
@ -60,17 +62,52 @@ class Genode::Ioapic : public Mmio
/* Register selectors */ /* Register selectors */
IOAPICVER = 0x01, IOAPICVER = 0x01,
IOREDTBL = 0x10, IOREDTBL = 0x10,
/* IRQ modes */
TRIGGER_EDGE = 0,
TRIGGER_LEVEL = 1,
POLARITY_HIGH = 0,
POLARITY_LOW = 1,
}; };
/**
* IRQ mode specifies trigger mode and polarity of an IRQ
*/
struct Irq_mode
{
unsigned trigger_mode;
unsigned polarity;
};
static Irq_mode _irq_mode[IRQ_COUNT];
/** /**
* Return whether 'irq' is an edge-triggered interrupt * Return whether 'irq' is an edge-triggered interrupt
*/ */
bool _edge_triggered(unsigned const irq) bool _edge_triggered(unsigned const irq)
{ {
if ((irq >= 0 && irq <= 8) || (irq >= 12 && irq <= Board::ISA_IRQ_END)) return _irq_mode[irq].trigger_mode == TRIGGER_EDGE;
return true; }
return false; /**
* Update IRT entry of given IRQ
*
* Note: The polarity and trigger flags are located in the lower
* 32 bits so only the necessary half of the IRT entry is
* updated.
*/
void _update_irt_entry(unsigned irq)
{
Irte::access_t irte;
write<Ioregsel>(IOREDTBL + 2 * irq);
irte = read<Iowin>();
Irte::Pol::set(irte, _irq_mode[irq].polarity);
Irte::Trg::set(irte, _irq_mode[irq].trigger_mode);
write<Ioregsel>(IOREDTBL + 2 * irq);
write<Iowin>(irte);
} }
/** /**
@ -81,11 +118,9 @@ class Genode::Ioapic : public Mmio
Irte::access_t irte = REMAP_BASE + irq; Irte::access_t irte = REMAP_BASE + irq;
Irte::Mask::set(irte, 1); Irte::Mask::set(irte, 1);
/* Use level-triggered, low-active mode for non-legacy IRQs */ Irte::Pol::set(irte, _irq_mode[irq].polarity);
if (!_edge_triggered(irq)) { Irte::Trg::set(irte, _irq_mode[irq].trigger_mode);
Irte::Pol::set(irte, 1);
Irte::Trg::set(irte, 1);
}
return irte; return irte;
} }
@ -93,17 +128,34 @@ class Genode::Ioapic : public Mmio
Ioapic() : Mmio(Board::MMIO_IOAPIC_BASE) Ioapic() : Mmio(Board::MMIO_IOAPIC_BASE)
{ {
/* Remap all supported IRQs */ for (unsigned i = 0; i < IRQ_COUNT; i++)
for (unsigned i = 0; i <= IRTE_COUNT; i++) { {
Irte::access_t irte = _create_irt_entry(i); /* set legacy/ISA IRQs to edge, high */
write<Ioregsel>(IOREDTBL + 2 * i + 1); if (i <= Board::ISA_IRQ_END) {
write<Iowin>(irte >> Iowin::ACCESS_WIDTH); _irq_mode[i].trigger_mode = TRIGGER_EDGE;
write<Ioregsel>(IOREDTBL + 2 * i); _irq_mode[i].polarity = POLARITY_HIGH;
write<Iowin>(irte); } else {
_irq_mode[i].trigger_mode = TRIGGER_LEVEL;
_irq_mode[i].polarity = POLARITY_LOW;
}
/* remap all IRQs managed by I/O APIC */
if (i <= IRTE_COUNT) {
Irte::access_t irte = _create_irt_entry(i);
write<Ioregsel>(IOREDTBL + 2 * i + 1);
write<Iowin>(irte >> Iowin::ACCESS_WIDTH);
write<Ioregsel>(IOREDTBL + 2 * i);
write<Iowin>(irte);
}
} }
}; };
/* Set/unset mask bit of IRTE for given vector */ /**
* Set/unset mask bit of IRTE for given vector
*
* \param vector targeted vector
* \param set whether to set or to unset the mask bit
*/
void toggle_mask(unsigned const vector, bool const set) void toggle_mask(unsigned const vector, bool const set)
{ {
const unsigned irq = vector - REMAP_BASE; const unsigned irq = vector - REMAP_BASE;
@ -124,7 +176,20 @@ class Genode::Ioapic : public Mmio
write<Iowin>(irte); write<Iowin>(irte);
} }
/* Registers */ /**
* Setup mode of an IRQ to specified trigger mode and polarity
*
* \param irq_number ID of targeted interrupt
* \param trigger new interrupt trigger mode
* \param polarity new interrupt polarity setting
*/
void setup_irq_mode(unsigned irq_number, unsigned trigger,
unsigned polarity);
/*
* Registers
*/
struct Ioregsel : Register<0x00, 32> { }; struct Ioregsel : Register<0x00, 32> { };
struct Iowin : Register<0x10, 32> { }; struct Iowin : Register<0x10, 32> { };
}; };
@ -133,7 +198,10 @@ class Genode::Pic : public Mmio
{ {
private: private:
/* Registers */ /*
* Registers
*/
struct EOI : Register<0x0b0, 32, true> { }; struct EOI : Register<0x0b0, 32, true> { };
struct Svr : Register<0x0f0, 32> struct Svr : Register<0x0f0, 32>
{ {
@ -141,13 +209,12 @@ class Genode::Pic : public Mmio
}; };
/* /*
* ISR register, see Intel SDM Vol. 3A, section 10.8.4. Each of the 8 * ISR register, see Intel SDM Vol. 3A, section 10.8.4.
* 32-bit ISR values is followed by 12 bytes of padding. *
* Each of the 8 32-bit ISR values is followed by 12 bytes of padding.
*/ */
struct Isr : Register_array<0x100, 32, 8 * 4, 32> { }; struct Isr : Register_array<0x100, 32, 8 * 4, 32> { };
Ioapic _ioapic;
/** /**
* Determine lowest pending interrupt in ISR register * Determine lowest pending interrupt in ISR register
* *
@ -177,7 +244,7 @@ class Genode::Pic : public Mmio
* necessary * necessary
*/ */
IPI = 255, IPI = 255,
NR_OF_IRQ = 256, NR_OF_IRQ = IRQ_COUNT,
}; };
/** /**
@ -185,6 +252,8 @@ class Genode::Pic : public Mmio
*/ */
Pic(); Pic();
Ioapic ioapic;
bool take_request(unsigned &irq); bool take_request(unsigned &irq);
void finish_request(); void finish_request();

View File

@ -78,4 +78,46 @@ Irq_session_component::Irq_session_component(Range_allocator * const irq_alloc,
PERR("unavailable interrupt requested"); PERR("unavailable interrupt requested");
throw Root::Invalid_args(); throw Root::Invalid_args();
} }
long irq_trg = Arg_string::find_arg(args, "irq_trigger").long_value(-1);
long irq_pol = Arg_string::find_arg(args, "irq_polarity").long_value(-1);
Irq_session::Trigger irq_trigger;
Irq_session::Polarity irq_polarity;
switch(irq_trg) {
case -1:
case Irq_session::TRIGGER_UNCHANGED:
irq_trigger = Irq_session::TRIGGER_UNCHANGED;
break;
case Irq_session::TRIGGER_EDGE:
irq_trigger = Irq_session::TRIGGER_EDGE;
break;
case Irq_session::TRIGGER_LEVEL:
irq_trigger = Irq_session::TRIGGER_LEVEL;
break;
default:
PERR("invalid trigger mode %ld specified for IRQ %u", irq_trg,
_irq_number);
throw Root::Unavailable();
}
switch(irq_pol) {
case -1:
case POLARITY_UNCHANGED:
irq_polarity = POLARITY_UNCHANGED;
break;
case POLARITY_HIGH:
irq_polarity = POLARITY_HIGH;
break;
case POLARITY_LOW:
irq_polarity = POLARITY_LOW;
break;
default:
PERR("invalid polarity %ld specified for IRQ %u", irq_pol,
_irq_number);
throw Root::Unavailable();
}
Platform::setup_irq_mode(_irq_number, irq_trigger, irq_polarity);
} }

View File

@ -18,6 +18,8 @@ using namespace Genode;
void Platform::_init_io_port_alloc() { }; void Platform::_init_io_port_alloc() { };
void Platform::setup_irq_mode(unsigned, unsigned, unsigned) { }
Native_region * mmio_regions(unsigned); Native_region * mmio_regions(unsigned);

View File

@ -12,8 +12,11 @@
* under the terms of the GNU General Public License version 2. * under the terms of the GNU General Public License version 2.
*/ */
#include <port_io.h> /* Genode includes */
#include <irq_session/irq_session.h>
/* core includes */
#include <port_io.h>
#include "pic.h" #include "pic.h"
using namespace Genode; using namespace Genode;
@ -72,10 +75,51 @@ void Pic::finish_request()
void Pic::unmask(unsigned const i, unsigned) void Pic::unmask(unsigned const i, unsigned)
{ {
_ioapic.toggle_mask(i, false); ioapic.toggle_mask(i, false);
} }
void Pic::mask(unsigned const i) void Pic::mask(unsigned const i)
{ {
_ioapic.toggle_mask(i, true); ioapic.toggle_mask(i, true);
}
Ioapic::Irq_mode Ioapic::_irq_mode[IRQ_COUNT];
void Ioapic::setup_irq_mode(unsigned irq_number, unsigned trigger,
unsigned polarity)
{
const unsigned irq_nr = irq_number - REMAP_BASE;
bool needs_sync = false;
switch (trigger) {
case Irq_session::TRIGGER_EDGE:
_irq_mode[irq_nr].trigger_mode = TRIGGER_EDGE;
needs_sync = true;
break;
case Irq_session::TRIGGER_LEVEL:
_irq_mode[irq_nr].trigger_mode = TRIGGER_LEVEL;
needs_sync = true;
break;
default:
/* Do nothing */
break;
}
switch (polarity) {
case Irq_session::POLARITY_HIGH:
_irq_mode[irq_nr].polarity = POLARITY_HIGH;
needs_sync = true;
break;
case Irq_session::POLARITY_LOW:
_irq_mode[irq_nr].polarity = POLARITY_LOW;
needs_sync = true;
break;
default:
/* Do nothing */
break;
}
/* Update IR table if IRQ mode changed */
if (needs_sync)
_update_irt_entry(irq_nr);
} }

View File

@ -18,6 +18,8 @@
#include <platform.h> #include <platform.h>
#include <board.h> #include <board.h>
#include <cpu.h> #include <cpu.h>
#include <pic.h>
#include <kernel/kernel.h>
using namespace Genode; using namespace Genode;
@ -75,3 +77,10 @@ long Platform::irq(long const user_irq)
if (user_irq) return user_irq + Board::VECTOR_REMAP_BASE; if (user_irq) return user_irq + Board::VECTOR_REMAP_BASE;
return Board::TIMER_VECTOR_USER; return Board::TIMER_VECTOR_USER;
} }
void Platform::setup_irq_mode(unsigned irq_number, unsigned trigger,
unsigned polarity)
{
Kernel::pic()->ioapic.setup_irq_mode(irq_number, trigger, polarity);
}