mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 18:06:50 +00:00
parent
d998df3b7f
commit
ecc9007e84
@ -82,14 +82,19 @@ namespace Irq {
|
|||||||
if (!args.is_valid_string()) throw Root::Invalid_args();
|
if (!args.is_valid_string()) throw Root::Invalid_args();
|
||||||
|
|
||||||
long irq_number = Arg_string::find_arg(args.string(), "irq_number").long_value(-1);
|
long irq_number = Arg_string::find_arg(args.string(), "irq_number").long_value(-1);
|
||||||
|
long msi = Arg_string::find_arg(args.string(), "device_config_phys").long_value(0);
|
||||||
|
|
||||||
/* check for 'MADT' overrides */
|
/* check for 'MADT' overrides */
|
||||||
unsigned mode;
|
unsigned mode;
|
||||||
irq_number = Acpi::override(irq_number, &mode);
|
long irq_legacy = Acpi::override(irq_number, &mode);
|
||||||
|
/* rewrite IRQ solely if this is not a MSI request */
|
||||||
|
if (!msi)
|
||||||
|
irq_number = irq_legacy;
|
||||||
|
|
||||||
/* allocate IRQ at parent*/
|
/* allocate IRQ at parent*/
|
||||||
try {
|
try {
|
||||||
Irq_connection irq(irq_number, _mode2trigger(mode), _mode2polarity(mode));
|
Irq_connection irq(irq_number, _mode2trigger(mode),
|
||||||
|
_mode2polarity(mode), msi);
|
||||||
irq.on_destruction(Irq_connection::KEEP_OPEN);
|
irq.on_destruction(Irq_connection::KEEP_OPEN);
|
||||||
return irq.cap();
|
return irq.cap();
|
||||||
} catch (...) { throw Root::Unavailable(); }
|
} catch (...) { throw Root::Unavailable(); }
|
||||||
|
@ -29,37 +29,65 @@ namespace Pci {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
using namespace Genode;
|
using Genode::size_t;
|
||||||
|
using Genode::addr_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple range allocator implementation used by the Irq_proxy
|
* A simple range allocator implementation used by the Irq_proxy
|
||||||
*/
|
*/
|
||||||
class Pci::Irq_allocator : public Range_allocator, Bit_allocator<256>
|
class Pci::Irq_allocator : public Genode::Range_allocator
|
||||||
{
|
{
|
||||||
Alloc_return alloc_addr(size_t size, addr_t addr) override
|
private:
|
||||||
{
|
|
||||||
try {
|
/*
|
||||||
_array.set(addr, size);
|
* We partition the IRQ space (128 GSIs) into 40 legacy IRQs and 64 MSIs (and
|
||||||
return Alloc_return::OK;
|
* hope the partitions will never overlap on any bizarre platform.)
|
||||||
} catch (...) {
|
*/
|
||||||
return Alloc_return::RANGE_CONFLICT;
|
enum { LEGACY = 40, MSI = 64, LEGACY_ARRAY = 64 };
|
||||||
|
|
||||||
|
Genode::Bit_array<LEGACY_ARRAY> _legacy;
|
||||||
|
Genode::Bit_allocator<MSI> _msi;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Irq_allocator()
|
||||||
|
{
|
||||||
|
/* reserve the last 24 legacy IRQs - 40 IRQs remaining */
|
||||||
|
_legacy.set(LEGACY, LEGACY_ARRAY - LEGACY);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* unused methods */
|
unsigned alloc_msi()
|
||||||
int remove_range(addr_t, size_t) override { return 0; }
|
{
|
||||||
int add_range(addr_t, size_t) override { return 0; }
|
try {
|
||||||
bool valid_addr(addr_t) const override { return false; }
|
return _msi.alloc();
|
||||||
size_t avail() const override { return 0; }
|
} catch (Genode::Bit_allocator<MSI>::Out_of_indices) { return ~0U; }
|
||||||
bool alloc(size_t, void **) override { return false; }
|
}
|
||||||
void free(void *) override { }
|
|
||||||
void free(void *, size_t) override { }
|
|
||||||
size_t overhead(size_t) const override { return 0; }
|
|
||||||
bool need_size_for_free() const override { return 0; }
|
|
||||||
|
|
||||||
Alloc_return alloc_aligned(size_t, void **, int, addr_t, addr_t) override {
|
void free_msi(unsigned msi) { _msi.free(msi); }
|
||||||
return Alloc_return::RANGE_CONFLICT; }
|
|
||||||
|
Alloc_return alloc_addr(size_t size, addr_t addr) override
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_legacy.set(addr, size);
|
||||||
|
return Alloc_return::OK;
|
||||||
|
} catch (...) {
|
||||||
|
return Alloc_return::RANGE_CONFLICT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unused methods */
|
||||||
|
int remove_range(addr_t, size_t) override { return 0; }
|
||||||
|
int add_range(addr_t, size_t) override { return 0; }
|
||||||
|
bool valid_addr(addr_t) const override { return false; }
|
||||||
|
size_t avail() const override { return 0; }
|
||||||
|
bool alloc(size_t, void **) override { return false; }
|
||||||
|
void free(void *) override { }
|
||||||
|
void free(void *, size_t) override { }
|
||||||
|
size_t overhead(size_t) const override { return 0; }
|
||||||
|
bool need_size_for_free() const override { return 0; }
|
||||||
|
|
||||||
|
Alloc_return alloc_aligned(size_t, void **, int, addr_t, addr_t) override {
|
||||||
|
return Alloc_return::RANGE_CONFLICT; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -84,13 +112,13 @@ class Pci::Irq_thread : public Genode::Thread<4096>
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Signal_receiver _sig_rec;
|
Genode::Signal_receiver _sig_rec;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Irq_thread() : Thread<4096>("irq_sig_recv") { start(); }
|
Irq_thread() : Thread<4096>("irq_sig_recv") { start(); }
|
||||||
|
|
||||||
Signal_receiver & sig_rec() { return _sig_rec; }
|
Genode::Signal_receiver & sig_rec() { return _sig_rec; }
|
||||||
|
|
||||||
void entry() {
|
void entry() {
|
||||||
|
|
||||||
@ -154,7 +182,7 @@ class Pci::Irq_component : public Proxy
|
|||||||
bool _associate() { return _associated; }
|
bool _associate() { return _associated; }
|
||||||
void _wait_for_irq() { }
|
void _wait_for_irq() { }
|
||||||
|
|
||||||
virtual bool remove_sharer(Irq_sigh *s) override {
|
virtual bool remove_sharer(Genode::Irq_sigh *s) override {
|
||||||
if (!Proxy::remove_sharer(s))
|
if (!Proxy::remove_sharer(s))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -182,6 +210,13 @@ class Pci::Irq_component : public Proxy
|
|||||||
|
|
||||||
void Pci::Irq_session_component::ack_irq()
|
void Pci::Irq_session_component::ack_irq()
|
||||||
{
|
{
|
||||||
|
if (msi()) {
|
||||||
|
Genode::Irq_session_client irq_msi(_irq_cap);
|
||||||
|
irq_msi.ack_irq();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shared irq handling */
|
||||||
Irq_component *irq_obj = Proxy::get_irq_proxy<Irq_component>(_gsi);
|
Irq_component *irq_obj = Proxy::get_irq_proxy<Irq_component>(_gsi);
|
||||||
if (!irq_obj) {
|
if (!irq_obj) {
|
||||||
PERR("Expected to find IRQ proxy for IRQ %02x", _gsi);
|
PERR("Expected to find IRQ proxy for IRQ %02x", _gsi);
|
||||||
@ -193,26 +228,63 @@ void Pci::Irq_session_component::ack_irq()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pci::Irq_session_component::Irq_session_component(unsigned irq) : _gsi(irq)
|
Pci::Irq_session_component::Irq_session_component(unsigned irq,
|
||||||
|
addr_t pci_config_space)
|
||||||
|
:
|
||||||
|
_gsi(irq)
|
||||||
{
|
{
|
||||||
bool valid = false;
|
/* invalid irq number for pci_devices */
|
||||||
|
if (irq >= INVALID_IRQ)
|
||||||
/* invalid irq number for pci devices */
|
|
||||||
if (irq == 0xFF)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (pci_config_space != ~0UL) {
|
||||||
|
/* msi way */
|
||||||
|
unsigned msi = irq_alloc.alloc_msi();
|
||||||
|
if (msi != ~0U) {
|
||||||
|
try {
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
Irq_connection conn(msi, Irq_session::TRIGGER_UNCHANGED,
|
||||||
|
Irq_session::POLARITY_UNCHANGED,
|
||||||
|
pci_config_space);
|
||||||
|
|
||||||
|
_msi_info = conn.info();
|
||||||
|
if (_msi_info.type == Irq_session::Info::Type::MSI) {
|
||||||
|
_gsi = msi;
|
||||||
|
_irq_cap = conn;
|
||||||
|
|
||||||
|
conn.on_destruction(Irq_connection::KEEP_OPEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Genode::Parent::Service_denied) { }
|
||||||
|
|
||||||
|
irq_alloc.free_msi(msi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* check if IRQ object was used before */
|
/* check if shared IRQ object was used before */
|
||||||
valid = Proxy::get_irq_proxy<Irq_component>(_gsi, &irq_alloc);
|
if (Proxy::get_irq_proxy<Irq_component>(_gsi, &irq_alloc))
|
||||||
|
return;
|
||||||
} catch (Genode::Parent::Service_denied) { }
|
} catch (Genode::Parent::Service_denied) { }
|
||||||
|
|
||||||
if (!valid)
|
PERR("unavailable IRQ 0x%x requested", _gsi);
|
||||||
PERR("unavailable IRQ object 0x%x requested", _gsi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pci::Irq_session_component::~Irq_session_component()
|
Pci::Irq_session_component::~Irq_session_component()
|
||||||
{
|
{
|
||||||
|
if (msi()) {
|
||||||
|
Genode::Irq_session_client irq_msi(_irq_cap);
|
||||||
|
irq_msi.sigh(Genode::Signal_context_capability());
|
||||||
|
|
||||||
|
Genode::env()->parent()->close(_irq_cap);
|
||||||
|
|
||||||
|
irq_alloc.free_msi(_gsi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shared irq handling */
|
||||||
Irq_component *irq_obj = Proxy::get_irq_proxy<Irq_component>(_gsi);
|
Irq_component *irq_obj = Proxy::get_irq_proxy<Irq_component>(_gsi);
|
||||||
if (!irq_obj) return;
|
if (!irq_obj) return;
|
||||||
|
|
||||||
@ -223,6 +295,14 @@ Pci::Irq_session_component::~Irq_session_component()
|
|||||||
|
|
||||||
void Pci::Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
void Pci::Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
||||||
{
|
{
|
||||||
|
if (msi()) {
|
||||||
|
/* register signal handler for msi directly at parent */
|
||||||
|
Genode::Irq_session_client irq_msi(_irq_cap);
|
||||||
|
irq_msi.sigh(sigh);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shared irq handling */
|
||||||
Irq_component *irq_obj = Proxy::get_irq_proxy<Irq_component>(_gsi);
|
Irq_component *irq_obj = Proxy::get_irq_proxy<Irq_component>(_gsi);
|
||||||
if (!irq_obj) {
|
if (!irq_obj) {
|
||||||
PERR("signal handler got not registered - irq object unavailable");
|
PERR("signal handler got not registered - irq object unavailable");
|
||||||
|
@ -32,14 +32,29 @@ class Pci::Irq_session_component : public Genode::Rpc_object<Genode::Irq_session
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned _gsi;
|
unsigned _gsi;
|
||||||
Genode::Irq_sigh _irq_sigh;
|
Genode::Irq_sigh _irq_sigh;
|
||||||
|
Genode::Irq_session_capability _irq_cap;
|
||||||
|
Genode::Irq_session::Info _msi_info;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Irq_session_component(unsigned);
|
enum { INVALID_IRQ = 0xffU };
|
||||||
|
|
||||||
|
Irq_session_component(unsigned, Genode::addr_t);
|
||||||
~Irq_session_component();
|
~Irq_session_component();
|
||||||
|
|
||||||
|
bool msi()
|
||||||
|
{
|
||||||
|
return _irq_cap.valid() &&
|
||||||
|
_msi_info.type == Genode::Irq_session::Info::Type::MSI;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned gsi() { return _gsi; }
|
||||||
|
|
||||||
|
unsigned long msi_address() const { return _msi_info.address; }
|
||||||
|
unsigned long msi_data() const { return _msi_info.value; }
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
** Irq session interface **
|
** Irq session interface **
|
||||||
***************************/
|
***************************/
|
||||||
|
@ -14,15 +14,17 @@
|
|||||||
#ifndef _PCI_DEVICE_COMPONENT_H_
|
#ifndef _PCI_DEVICE_COMPONENT_H_
|
||||||
#define _PCI_DEVICE_COMPONENT_H_
|
#define _PCI_DEVICE_COMPONENT_H_
|
||||||
|
|
||||||
#include <pci_device/pci_device.h>
|
/* base */
|
||||||
#include <util/list.h>
|
|
||||||
#include <base/rpc_server.h>
|
#include <base/rpc_server.h>
|
||||||
#include <base/printf.h>
|
|
||||||
|
|
||||||
#include <io_mem_session/connection.h>
|
#include <io_mem_session/connection.h>
|
||||||
|
#include <util/list.h>
|
||||||
|
#include <util/mmio.h>
|
||||||
|
|
||||||
|
/* os */
|
||||||
|
#include <pci_device/pci_device.h>
|
||||||
|
|
||||||
|
/* local */
|
||||||
#include "pci_device_config.h"
|
#include "pci_device_config.h"
|
||||||
|
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
|
|
||||||
namespace Pci { class Device_component; class Session_component; }
|
namespace Pci { class Device_component; class Session_component; }
|
||||||
@ -32,19 +34,25 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Device_config _device_config;
|
Device_config _device_config;
|
||||||
Genode::addr_t _config_space;
|
Genode::addr_t _config_space;
|
||||||
Genode::Io_mem_connection *_io_mem;
|
Genode::Io_mem_session_capability _io_mem_config_extended;
|
||||||
Config_access _config_access;
|
Config_access _config_access;
|
||||||
Genode::Rpc_entrypoint *_ep;
|
Genode::Rpc_entrypoint *_ep;
|
||||||
Pci::Session_component *_session;
|
Pci::Session_component *_session;
|
||||||
Irq_session_component _irq_session;
|
unsigned short _irq_line;
|
||||||
bool _rewrite_irq_line;
|
Irq_session_component _irq_session;
|
||||||
|
bool _rewrite_irq_line;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
IO_BLOCK_SIZE = sizeof(Genode::Io_port_connection) *
|
IO_BLOCK_SIZE = sizeof(Genode::Io_port_connection) *
|
||||||
Device::NUM_RESOURCES + 32 + 8 * sizeof(void *)
|
Device::NUM_RESOURCES + 32 + 8 * sizeof(void *),
|
||||||
|
PCI_CMD_REG = 0x4,
|
||||||
|
PCI_CMD_DMA = 0x4,
|
||||||
|
PCI_IRQ_LINE = 0x3c,
|
||||||
|
PCI_IRQ_PIN = 0x3d
|
||||||
};
|
};
|
||||||
|
|
||||||
Genode::Tslab<Genode::Io_port_connection, IO_BLOCK_SIZE> _slab_ioport;
|
Genode::Tslab<Genode::Io_port_connection, IO_BLOCK_SIZE> _slab_ioport;
|
||||||
Genode::Slab_block _slab_ioport_block;
|
Genode::Slab_block _slab_ioport_block;
|
||||||
char _slab_ioport_block_data[IO_BLOCK_SIZE];
|
char _slab_ioport_block_data[IO_BLOCK_SIZE];
|
||||||
@ -56,7 +64,95 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
Genode::Io_port_connection *_io_port_conn [Device::NUM_RESOURCES];
|
Genode::Io_port_connection *_io_port_conn [Device::NUM_RESOURCES];
|
||||||
Genode::Io_mem_connection *_io_mem_conn [Device::NUM_RESOURCES];
|
Genode::Io_mem_connection *_io_mem_conn [Device::NUM_RESOURCES];
|
||||||
|
|
||||||
enum { PCI_IRQ_LINE = 0x3c };
|
struct Status : Genode::Register<8> {
|
||||||
|
struct Capabilities : Bitfield<4,1> { };
|
||||||
|
|
||||||
|
inline static access_t read(Genode::uint8_t t) { return t; };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read out msi capabilities of the device.
|
||||||
|
*/
|
||||||
|
Genode::uint16_t _msi_cap()
|
||||||
|
{
|
||||||
|
enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34, CAP_MSI = 0x5 };
|
||||||
|
|
||||||
|
Status::access_t status = Status::read(_device_config.read(&_config_access,
|
||||||
|
PCI_STATUS,
|
||||||
|
Pci::Device::ACCESS_16BIT));
|
||||||
|
if (!Status::Capabilities::get(status))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Genode::uint8_t cap = _device_config.read(&_config_access,
|
||||||
|
PCI_CAP_OFFSET,
|
||||||
|
Pci::Device::ACCESS_8BIT);
|
||||||
|
|
||||||
|
for (Genode::uint16_t val = 0; cap; cap = val >> 8) {
|
||||||
|
val = _device_config.read(&_config_access, cap,
|
||||||
|
Pci::Device::ACCESS_16BIT);
|
||||||
|
if ((val & 0xff) != CAP_MSI)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable MSI if already enabled.
|
||||||
|
*/
|
||||||
|
unsigned _disable_msi(unsigned irq)
|
||||||
|
{
|
||||||
|
using Genode::uint16_t;
|
||||||
|
using Genode::uint8_t;
|
||||||
|
|
||||||
|
uint8_t has_irq = _device_config.read(&_config_access, PCI_IRQ_PIN,
|
||||||
|
Pci::Device::ACCESS_8BIT);
|
||||||
|
if (!has_irq)
|
||||||
|
return Irq_session_component::INVALID_IRQ;
|
||||||
|
|
||||||
|
uint16_t cap = _msi_cap();
|
||||||
|
if (!cap)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
uint16_t msi = _device_config.read(&_config_access, cap + 2,
|
||||||
|
Pci::Device::ACCESS_16BIT);
|
||||||
|
|
||||||
|
enum { MSI_ENABLED = 0x1 };
|
||||||
|
|
||||||
|
if (msi & MSI_ENABLED)
|
||||||
|
/* disable MSI */
|
||||||
|
_device_config.write(&_config_access, cap + 2,
|
||||||
|
msi ^ MSI_ENABLED,
|
||||||
|
Pci::Device::ACCESS_8BIT);
|
||||||
|
|
||||||
|
return irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable bus master dma if already enabled.
|
||||||
|
*/
|
||||||
|
void _disable_bus_master_dma() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disabling a bridge may make the devices behind non-functional,
|
||||||
|
* as we have no driver which will switch it on again
|
||||||
|
*/
|
||||||
|
if (_device_config.is_pci_bridge())
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned cmd = _device_config.read(&_config_access, PCI_CMD_REG,
|
||||||
|
Pci::Device::ACCESS_16BIT);
|
||||||
|
if (cmd & PCI_CMD_DMA)
|
||||||
|
_device_config.write(&_config_access, PCI_CMD_REG,
|
||||||
|
cmd ^ PCI_CMD_DMA,
|
||||||
|
Pci::Device::ACCESS_16BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -69,13 +165,22 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
bool rewrite_irq_line)
|
bool rewrite_irq_line)
|
||||||
:
|
:
|
||||||
_device_config(device_config), _config_space(addr),
|
_device_config(device_config), _config_space(addr),
|
||||||
_io_mem(0), _ep(ep), _session(session),
|
_ep(ep), _session(session),
|
||||||
_irq_session(_device_config.read(&_config_access, PCI_IRQ_LINE,
|
_irq_line(_device_config.read(&_config_access, PCI_IRQ_LINE,
|
||||||
Pci::Device::ACCESS_8BIT)),
|
Pci::Device::ACCESS_8BIT)),
|
||||||
|
_irq_session(_disable_msi(_irq_line), _msi_cap() ? _config_space : ~0UL),
|
||||||
_rewrite_irq_line(rewrite_irq_line),
|
_rewrite_irq_line(rewrite_irq_line),
|
||||||
_slab_ioport(0, &_slab_ioport_block),
|
_slab_ioport(0, &_slab_ioport_block),
|
||||||
_slab_iomem(0, &_slab_iomem_block)
|
_slab_iomem(0, &_slab_iomem_block)
|
||||||
{
|
{
|
||||||
|
if (_config_space != ~0UL) {
|
||||||
|
try {
|
||||||
|
Genode::Io_mem_connection conn(_config_space, 0x1000);
|
||||||
|
conn.on_destruction(Genode::Io_mem_connection::KEEP_OPEN);
|
||||||
|
_io_mem_config_extended = conn;
|
||||||
|
} catch (Genode::Parent::Service_denied) { }
|
||||||
|
}
|
||||||
|
|
||||||
_ep->manage(&_irq_session);
|
_ep->manage(&_irq_session);
|
||||||
|
|
||||||
for (unsigned i = 0; i < Device::NUM_RESOURCES; i++) {
|
for (unsigned i = 0; i < Device::NUM_RESOURCES; i++) {
|
||||||
@ -87,6 +192,43 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
PERR("incorrect amount of space for io port resources");
|
PERR("incorrect amount of space for io port resources");
|
||||||
if (_slab_iomem.num_elem() != Device::NUM_RESOURCES)
|
if (_slab_iomem.num_elem() != Device::NUM_RESOURCES)
|
||||||
PERR("incorrect amount of space for io mem resources");
|
PERR("incorrect amount of space for io mem resources");
|
||||||
|
|
||||||
|
_disable_bus_master_dma();
|
||||||
|
|
||||||
|
if (!_irq_session.msi())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Genode::addr_t msi_address = _irq_session.msi_address();
|
||||||
|
Genode::uint32_t msi_value = _irq_session.msi_data();
|
||||||
|
Genode::uint16_t msi_cap = _msi_cap();
|
||||||
|
|
||||||
|
enum { CAP_MSI_64 = 0x80, MSI_ENABLED = 0x1 };
|
||||||
|
Genode::uint16_t msi = _device_config.read(&_config_access,
|
||||||
|
msi_cap + 2,
|
||||||
|
Pci::Device::ACCESS_16BIT);
|
||||||
|
|
||||||
|
_device_config.write(&_config_access, msi_cap + 0x4, msi_address,
|
||||||
|
Pci::Device::ACCESS_32BIT);
|
||||||
|
|
||||||
|
if (msi & CAP_MSI_64) {
|
||||||
|
Genode::uint32_t upper_address = sizeof(msi_address) > 4 ?
|
||||||
|
msi_address >> 32 : 0UL;
|
||||||
|
|
||||||
|
_device_config.write(&_config_access, msi_cap + 0x8,
|
||||||
|
upper_address,
|
||||||
|
Pci::Device::ACCESS_32BIT);
|
||||||
|
_device_config.write(&_config_access, msi_cap + 0xc,
|
||||||
|
msi_value,
|
||||||
|
Pci::Device::ACCESS_16BIT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_device_config.write(&_config_access, msi_cap + 0x8, msi_value,
|
||||||
|
Pci::Device::ACCESS_16BIT);
|
||||||
|
|
||||||
|
/* enable MSI */
|
||||||
|
_device_config.write(&_config_access, msi_cap + 2,
|
||||||
|
msi ^ MSI_ENABLED,
|
||||||
|
Pci::Device::ACCESS_8BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,8 +237,9 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
Device_component(Genode::Rpc_entrypoint * ep,
|
Device_component(Genode::Rpc_entrypoint * ep,
|
||||||
Pci::Session_component * session, unsigned irq)
|
Pci::Session_component * session, unsigned irq)
|
||||||
:
|
:
|
||||||
_config_space(~0UL), _io_mem(0), _ep(ep), _session(session),
|
_config_space(~0UL), _ep(ep), _session(session),
|
||||||
_irq_session(irq),
|
_irq_line(irq),
|
||||||
|
_irq_session(_irq_line, _config_space),
|
||||||
_slab_ioport(0, &_slab_ioport_block),
|
_slab_ioport(0, &_slab_ioport_block),
|
||||||
_slab_iomem(0, &_slab_iomem_block)
|
_slab_iomem(0, &_slab_iomem_block)
|
||||||
{
|
{
|
||||||
@ -121,6 +264,12 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
if (_io_mem_conn[i])
|
if (_io_mem_conn[i])
|
||||||
Genode::destroy(_slab_iomem, _io_mem_conn[i]);
|
Genode::destroy(_slab_iomem, _io_mem_conn[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_io_mem_config_extended.valid())
|
||||||
|
Genode::env()->parent()->close(_io_mem_config_extended);
|
||||||
|
|
||||||
|
if (_device_config.valid())
|
||||||
|
_disable_bus_master_dma();
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
@ -129,32 +278,34 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
|
|
||||||
Device_config config() { return _device_config; }
|
Device_config config() { return _device_config; }
|
||||||
|
|
||||||
Genode::addr_t config_space() { return _config_space; }
|
Genode::Io_mem_dataspace_capability get_config_space()
|
||||||
|
{
|
||||||
|
if (!_io_mem_config_extended.valid())
|
||||||
|
return Genode::Io_mem_dataspace_capability();
|
||||||
|
|
||||||
void set_config_space(Genode::Io_mem_connection * io_mem) {
|
Genode::Io_mem_session_client client(_io_mem_config_extended);
|
||||||
_io_mem = io_mem; }
|
return client.dataspace();
|
||||||
|
}
|
||||||
Genode::Io_mem_connection * get_config_space() { return _io_mem; }
|
|
||||||
|
|
||||||
/**************************
|
/**************************
|
||||||
** PCI-device interface **
|
** PCI-device interface **
|
||||||
**************************/
|
**************************/
|
||||||
|
|
||||||
void bus_address(unsigned char *bus, unsigned char *dev,
|
void bus_address(unsigned char *bus, unsigned char *dev,
|
||||||
unsigned char *fn)
|
unsigned char *fn) override
|
||||||
{
|
{
|
||||||
*bus = _device_config.bus_number();
|
*bus = _device_config.bus_number();
|
||||||
*dev = _device_config.device_number();
|
*dev = _device_config.device_number();
|
||||||
*fn = _device_config.function_number();
|
*fn = _device_config.function_number();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short vendor_id() { return _device_config.vendor_id(); }
|
unsigned short vendor_id() override { return _device_config.vendor_id(); }
|
||||||
|
|
||||||
unsigned short device_id() { return _device_config.device_id(); }
|
unsigned short device_id() override { return _device_config.device_id(); }
|
||||||
|
|
||||||
unsigned class_code() { return _device_config.class_code(); }
|
unsigned class_code() override { return _device_config.class_code(); }
|
||||||
|
|
||||||
Resource resource(int resource_id)
|
Resource resource(int resource_id) override
|
||||||
{
|
{
|
||||||
/* return invalid resource if device is invalid */
|
/* return invalid resource if device is invalid */
|
||||||
if (!_device_config.valid())
|
if (!_device_config.valid())
|
||||||
@ -163,41 +314,13 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
|||||||
return _device_config.resource(resource_id);
|
return _device_config.resource(resource_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned config_read(unsigned char address, Access_size size)
|
unsigned config_read(unsigned char address, Access_size size) override
|
||||||
{
|
{
|
||||||
return _device_config.read(&_config_access, address, size);
|
return _device_config.read(&_config_access, address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void config_write(unsigned char address, unsigned value, Access_size size)
|
void config_write(unsigned char address, unsigned value,
|
||||||
{
|
Access_size size) override;
|
||||||
/* white list of ports which we permit to write */
|
|
||||||
switch(address) {
|
|
||||||
case 0x40 ... 0xFF:
|
|
||||||
/* all device specific registers are permitted */
|
|
||||||
break;
|
|
||||||
case 0x4: /* COMMAND register - first byte */
|
|
||||||
if (size == Access_size::ACCESS_16BIT)
|
|
||||||
break;
|
|
||||||
case 0x5: /* COMMAND register - second byte */
|
|
||||||
case 0xd: /* Latency timer */
|
|
||||||
if (size == Access_size::ACCESS_8BIT)
|
|
||||||
break;
|
|
||||||
case PCI_IRQ_LINE:
|
|
||||||
/* permitted up to now solely for acpi driver */
|
|
||||||
if (address == PCI_IRQ_LINE && _rewrite_irq_line &&
|
|
||||||
size == Access_size::ACCESS_8BIT)
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
PWRN("%x:%x:%x write access to address=%x value=0x%x "
|
|
||||||
" size=0x%x got dropped", _device_config.bus_number(),
|
|
||||||
_device_config.device_number(),
|
|
||||||
_device_config.function_number(),
|
|
||||||
address, value, size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_device_config.write(&_config_access, address, value, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Irq_session_capability irq(Genode::uint8_t id) override
|
Genode::Irq_session_capability irq(Genode::uint8_t id) override
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user