mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
parent
42a77c531a
commit
d657b61f1b
@ -174,30 +174,59 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i
|
||||
return _irq_session->cap();
|
||||
}
|
||||
|
||||
Genode::uint16_t const msi_cap = _msi_cap();
|
||||
Genode::uint16_t const msix_cap = _msix_cap();
|
||||
|
||||
_irq_session = construct_at<Irq_session_component>(_mem_irq_component,
|
||||
_configure_irq(_irq_line),
|
||||
(!_session.msi_usage() || !_msi_cap()) ? ~0UL : _config_space,
|
||||
_configure_irq(_irq_line, msi_cap, msix_cap),
|
||||
(!_session.msi_usage() || (!msi_cap && !msix_cap)) ? ~0UL : _config_space,
|
||||
_env, _global_heap);
|
||||
_env.ep().rpc_ep().manage(_irq_session);
|
||||
|
||||
Genode::uint16_t msi_cap = _msi_cap();
|
||||
bool msix_used = false;
|
||||
bool msi_used = false;
|
||||
|
||||
if (_irq_session->msi()) {
|
||||
if (msix_cap)
|
||||
msix_used = _setup_msix(msix_cap);
|
||||
if (!msix_used && msi_cap)
|
||||
msi_used = _setup_msi(msi_cap);
|
||||
}
|
||||
|
||||
Genode::addr_t msi_address = _irq_session->msi_address();
|
||||
Genode::uint32_t msi_value = _irq_session->msi_data();
|
||||
if (_irq_session->msi())
|
||||
Genode::log(_device_config, " uses ",
|
||||
msix_used ? "MSI-X " : "",
|
||||
msi_used ? "MSI ": "",
|
||||
(!msi_used && !msix_used) ? "no MSI/-X/IRQ " : "",
|
||||
"vector ", Genode::Hex(_irq_session->msi_data()), ", "
|
||||
"address ", Genode::Hex(_irq_session->msi_address()));
|
||||
else
|
||||
Genode::log(_device_config, " uses IRQ, vector ",
|
||||
Genode::Hex(_irq_line),
|
||||
", supports: ",
|
||||
msi_cap ? " MSI" : "",
|
||||
msix_cap ? " MSI-X" : "");
|
||||
|
||||
Genode::uint16_t msi = _device_config.read(_config_access,
|
||||
msi_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
return _irq_session->cap();
|
||||
}
|
||||
|
||||
bool Platform::Device_component::_setup_msi(Genode::uint16_t const msi_cap)
|
||||
{
|
||||
try {
|
||||
addr_t const msi_address = _irq_session->msi_address();
|
||||
uint32_t const msi_value = _irq_session->msi_data();
|
||||
|
||||
uint16_t msi = _device_config.read(_config_access,
|
||||
msi_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
|
||||
_device_config.write(_config_access, msi_cap + 0x4, msi_address,
|
||||
Platform::Device::ACCESS_32BIT);
|
||||
|
||||
if (msi & CAP_MSI_64) {
|
||||
Genode::uint32_t upper_address = sizeof(msi_address) > 4
|
||||
? (Genode::uint64_t)msi_address >> 32
|
||||
: 0UL;
|
||||
uint32_t upper_address = sizeof(msi_address) > 4
|
||||
? uint64_t(msi_address) >> 32
|
||||
: 0UL;
|
||||
|
||||
_device_config.write(_config_access, msi_cap + 0x8,
|
||||
upper_address,
|
||||
@ -205,8 +234,7 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i
|
||||
_device_config.write(_config_access, msi_cap + 0xc,
|
||||
msi_value,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
}
|
||||
else
|
||||
} else
|
||||
_device_config.write(_config_access, msi_cap + 0x8, msi_value,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
|
||||
@ -214,31 +242,87 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i
|
||||
_device_config.write(_config_access, msi_cap + 2,
|
||||
msi ^ MSI_ENABLED,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
}
|
||||
|
||||
bool msi_64 = false;
|
||||
bool msi_mask = false;
|
||||
if (msi_cap) {
|
||||
Genode::uint16_t msi = _device_config.read(_config_access,
|
||||
msi_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
msi_64 = msi & CAP_MSI_64;
|
||||
msi_mask = msi & CAP_MASK;
|
||||
}
|
||||
msi = _device_config.read(_config_access,
|
||||
msi_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
return msi & MSI_ENABLED;
|
||||
} catch (...) { }
|
||||
|
||||
if (_irq_session->msi())
|
||||
Genode::log(_device_config, " uses ",
|
||||
"MSI ", (msi_64 ? "64bit" : "32bit"), ", "
|
||||
"vector ", Genode::Hex(_irq_session->msi_data()), ", "
|
||||
"address ", Genode::Hex(_irq_session->msi_address()), ", ",
|
||||
(msi_mask ? "maskable" : "non-maskable"));
|
||||
else
|
||||
Genode::log(_device_config, " uses IRQ, vector ",
|
||||
Genode::Hex(_irq_line),
|
||||
(msi_cap ? (msi_64 ? ", MSI 64bit capable"
|
||||
: ", MSI 32bit capable")
|
||||
: ""),
|
||||
(msi_mask ? ", maskable" : ", non-maskable"));
|
||||
|
||||
return _irq_session->cap();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Platform::Device_component::_setup_msix(Genode::uint16_t const msix_cap)
|
||||
{
|
||||
try {
|
||||
struct Table_pba : Register<32>
|
||||
{
|
||||
struct Bir : Bitfield<0, 3> { };
|
||||
struct Offset : Bitfield<3, 29> { };
|
||||
};
|
||||
|
||||
addr_t const msi_address = _irq_session->msi_address();
|
||||
uint32_t const msi_value = _irq_session->msi_data();
|
||||
|
||||
uint16_t ctrl = _device_config.read(_config_access,
|
||||
msix_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
|
||||
uint32_t const table = _device_config.read(_config_access,
|
||||
msix_cap + 4,
|
||||
Platform::Device::ACCESS_32BIT);
|
||||
|
||||
uint32_t const slots = Msix_ctrl::Slots::get(ctrl) + 1;
|
||||
|
||||
uint8_t const table_bir = Table_pba::Bir::masked(table);
|
||||
uint32_t const table_off = Table_pba::Offset::masked(table);
|
||||
|
||||
enum { SIZEOF_MSI_TABLE_ENTRY = 16, SIZE_IOMEM = 0x1000 };
|
||||
|
||||
Pci::Resource res = _device_config.resource(table_bir);
|
||||
if (!slots || !res.valid() || res.size() < SIZE_IOMEM ||
|
||||
table_off > res.size() - SIZE_IOMEM)
|
||||
return false;
|
||||
|
||||
Genode::uint64_t const msix_base = res.base() + table_off;
|
||||
|
||||
Io_mem io_mem(_env, msix_base, SIZE_IOMEM, false);
|
||||
Attached_dataspace x(_env.rm(), io_mem.dataspace());
|
||||
|
||||
if (slots * SIZEOF_MSI_TABLE_ENTRY > SIZE_IOMEM)
|
||||
return false;
|
||||
|
||||
struct Msi_entry : public Mmio {
|
||||
Msi_entry(addr_t const base) : Mmio(base) { }
|
||||
|
||||
struct Address : Register<0x0, 64> { };
|
||||
struct Value : Register<0x8, 32> { };
|
||||
struct Vector : Register<0xc, 32> {
|
||||
struct Mask : Bitfield <0, 1> { };
|
||||
};
|
||||
} msi_entry_0 (reinterpret_cast<addr_t>(x.local_addr<void>()));
|
||||
|
||||
/* setup first msi-x table entry */
|
||||
msi_entry_0.write<Msi_entry::Address>(msi_address & ~(0x3UL));
|
||||
msi_entry_0.write<Msi_entry::Value>(msi_value);
|
||||
msi_entry_0.write<Msi_entry::Vector::Mask>(0);
|
||||
|
||||
/* disable all msi-x table entries beside the first one */
|
||||
for (unsigned i = 1; i < slots; i++) {
|
||||
struct Msi_entry unused (reinterpret_cast<addr_t>(x.local_addr<void>()) + SIZEOF_MSI_TABLE_ENTRY * i);
|
||||
unused.write<Msi_entry::Vector::Mask>(1);
|
||||
}
|
||||
|
||||
/* enable MSI-X */
|
||||
Msix_ctrl::Fmask::set(ctrl, 0);
|
||||
Msix_ctrl::Enable::set(ctrl, 1);
|
||||
_device_config.write(_config_access, msix_cap + 2, ctrl,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
|
||||
ctrl = _device_config.read(_config_access, msix_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
return Msix_ctrl::Enable::get(ctrl);
|
||||
} catch (...) { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -77,10 +77,16 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
PCI_IRQ_PIN = 0x3d,
|
||||
|
||||
CAP_MSI_64 = 0x80,
|
||||
CAP_MASK = 0x100,
|
||||
MSI_ENABLED = 0x1
|
||||
};
|
||||
|
||||
struct Msix_ctrl : Register<16>
|
||||
{
|
||||
struct Slots : Bitfield< 0, 10> { };
|
||||
struct Fmask : Bitfield<14, 1> { };
|
||||
struct Enable : Bitfield<15, 1> { };
|
||||
};
|
||||
|
||||
Genode::Tslab<Genode::Io_port_connection, IO_BLOCK_SIZE> _slab_ioport;
|
||||
char _slab_ioport_block_data[IO_BLOCK_SIZE];
|
||||
|
||||
@ -106,7 +112,19 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
*/
|
||||
Genode::uint16_t _msi_cap()
|
||||
{
|
||||
enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34, CAP_MSI = 0x5 };
|
||||
enum { CAP_MSI = 0x5 };
|
||||
return _lookup_cap(CAP_MSI);
|
||||
}
|
||||
|
||||
Genode::uint16_t _msix_cap()
|
||||
{
|
||||
enum { CAP_MSI_X = 0x11 };
|
||||
return _lookup_cap(CAP_MSI_X);
|
||||
}
|
||||
|
||||
Genode::uint16_t _lookup_cap(Genode::uint16_t const target_cap)
|
||||
{
|
||||
enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34 };
|
||||
|
||||
Status::access_t status = Status::read(_device_config.read(_config_access,
|
||||
PCI_STATUS,
|
||||
@ -121,7 +139,7 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
for (Genode::uint16_t val = 0; cap; cap = val >> 8) {
|
||||
val = _device_config.read(_config_access, cap,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
if ((val & 0xff) != CAP_MSI)
|
||||
if ((val & 0xff) != target_cap)
|
||||
continue;
|
||||
|
||||
return cap;
|
||||
@ -132,9 +150,10 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
|
||||
|
||||
/**
|
||||
* Disable MSI if already enabled.
|
||||
* Disable MSI/MSI-X if already enabled.
|
||||
*/
|
||||
unsigned _configure_irq(unsigned irq)
|
||||
unsigned _configure_irq(unsigned irq, uint16_t const msi_cap,
|
||||
uint16_t const msix_cap)
|
||||
{
|
||||
using Genode::uint16_t;
|
||||
using Genode::uint8_t;
|
||||
@ -156,18 +175,30 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
_irq_line = irq = irq_r;
|
||||
}
|
||||
|
||||
uint16_t cap = _msi_cap();
|
||||
if (!cap)
|
||||
return irq;
|
||||
if (msi_cap) {
|
||||
uint16_t msi = _device_config.read(_config_access, msi_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
|
||||
uint16_t msi = _device_config.read(_config_access, cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
if (msi & MSI_ENABLED)
|
||||
/* disable MSI */
|
||||
_device_config.write(_config_access, msi_cap + 2,
|
||||
msi ^ MSI_ENABLED,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
}
|
||||
|
||||
if (msi & MSI_ENABLED)
|
||||
/* disable MSI */
|
||||
_device_config.write(_config_access, cap + 2,
|
||||
msi ^ MSI_ENABLED,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
if (msix_cap) {
|
||||
uint16_t msix = _device_config.read(_config_access,
|
||||
msix_cap + 2,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
|
||||
if (Msix_ctrl::Enable::get(msix)) {
|
||||
Msix_ctrl::Enable::set(msix, 0);
|
||||
|
||||
_device_config.write(_config_access, msix_cap + 2,
|
||||
msix,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
}
|
||||
}
|
||||
|
||||
return irq;
|
||||
}
|
||||
@ -188,6 +219,9 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
_device_config.disable_bus_master_dma(_config_access);
|
||||
}
|
||||
|
||||
bool _setup_msi(Genode::uint16_t);
|
||||
bool _setup_msix(Genode::uint16_t);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
|
@ -1102,6 +1102,23 @@ class Platform::Root : public Genode::Root_component<Session_component>
|
||||
xml.attribute("device_id" , String<8>(Hex(config.device_id())));
|
||||
xml.attribute("class_code", String<12>(Hex(config.class_code())));
|
||||
xml.attribute("bridge" , config.pci_bridge() ? "yes" : "no");
|
||||
|
||||
enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34 };
|
||||
|
||||
try {
|
||||
config.read(config_access, PCI_STATUS, Platform::Device::ACCESS_16BIT);
|
||||
|
||||
Genode::uint8_t cap = config.read(config_access,
|
||||
PCI_CAP_OFFSET,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
|
||||
for (Genode::uint16_t val = 0; cap; cap = val >> 8) {
|
||||
val = config.read(config_access, cap, Platform::Device::ACCESS_16BIT);
|
||||
xml.attribute("cap", String<8>(Hex(val & 0xff)));
|
||||
}
|
||||
} catch (...) {
|
||||
xml.attribute("cap", "failed to read");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user