Kernel: Route PCI Interrupt Link Device interrupts
This commit is contained in:
parent
28ac6c2267
commit
27613da5ea
|
@ -48,6 +48,8 @@ namespace Kernel::ACPI
|
||||||
|
|
||||||
BAN::ErrorOr<void> load_aml_tables(BAN::StringView name, bool all);
|
BAN::ErrorOr<void> load_aml_tables(BAN::StringView name, bool all);
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> route_interrupt_link_device(const AML::Scope& device, uint64_t& routed_irq_mask);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
paddr_t m_header_table_paddr = 0;
|
paddr_t m_header_table_paddr = 0;
|
||||||
vaddr_t m_header_table_vaddr = 0;
|
vaddr_t m_header_table_vaddr = 0;
|
||||||
|
|
|
@ -626,6 +626,127 @@ acpi_release_global_lock:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic ignored "-Wstack-usage="
|
||||||
|
#endif
|
||||||
|
BAN::ErrorOr<void> ACPI::route_interrupt_link_device(const AML::Scope& device, uint64_t& routed_irq_mask)
|
||||||
|
{
|
||||||
|
ASSERT(m_namespace);
|
||||||
|
|
||||||
|
auto prs_node = TRY(AML::convert_node(TRY(m_namespace->evaluate(device, "_PRS"_sv)), AML::ConvBuffer, -1));
|
||||||
|
auto prs_span = BAN::ConstByteSpan(prs_node.as.str_buf->bytes, prs_node.as.str_buf->size);
|
||||||
|
|
||||||
|
auto [srs_path, srs_node] = TRY(m_namespace->find_named_object(device, TRY(AML::NameString::from_string("_SRS"_sv))));
|
||||||
|
if (srs_node == nullptr || srs_node->node.type != AML::Node::Type::Method)
|
||||||
|
{
|
||||||
|
dwarnln("interrupt link device does not have _SRS method");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!prs_span.empty())
|
||||||
|
{
|
||||||
|
if (!(prs_span[0] & 0x80))
|
||||||
|
{
|
||||||
|
const uint8_t name = (prs_span[0] >> 3) & 0x0F;
|
||||||
|
const uint8_t length = prs_span[0] & 0x07;
|
||||||
|
if (prs_span.size() < static_cast<size_t>(1 + length))
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
if (name == 0x04)
|
||||||
|
{
|
||||||
|
if (length < 2)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
const uint16_t irq_mask = prs_span[1] | (prs_span[2] << 8);
|
||||||
|
|
||||||
|
for (uint8_t pass = 0; pass < 2; pass++)
|
||||||
|
{
|
||||||
|
for (uint8_t irq = 0; irq < 16; irq++)
|
||||||
|
{
|
||||||
|
if (!(irq_mask & (1 << irq)))
|
||||||
|
continue;
|
||||||
|
if (pass == 0 && (routed_irq_mask & (static_cast<uint64_t>(1) << irq)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BAN::Array<uint8_t, 4> setting;
|
||||||
|
setting[0] = 0x22 | (length > 2); // small, irq, data len
|
||||||
|
setting[1] = (1 << irq) >> 0; // irq low
|
||||||
|
setting[2] = (1 << irq) >> 8; // irq high
|
||||||
|
if (length > 2)
|
||||||
|
setting[3] = prs_span[3]; // flags
|
||||||
|
|
||||||
|
auto setting_span = BAN::ConstByteSpan(setting.data(), (length > 2) ? 4 : 3);
|
||||||
|
TRY(AML::method_call(srs_path, srs_node->node, TRY(AML::Node::create_buffer(setting_span))));
|
||||||
|
|
||||||
|
dprintln("routed {} -> irq {}", device, irq);
|
||||||
|
|
||||||
|
routed_irq_mask |= static_cast<uint64_t>(1) << irq;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prs_span = prs_span.slice(1 + length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (prs_span.size() < 3)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
const uint8_t name = prs_span[0] & 0x7F;
|
||||||
|
const uint16_t length = (prs_span[2] << 8) | prs_span[1];
|
||||||
|
if (prs_span.size() < static_cast<size_t>(3 + length))
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
// Extended Interrupt Descriptor
|
||||||
|
if (name == 0x09)
|
||||||
|
{
|
||||||
|
const uint8_t irq_count = prs_span[4];
|
||||||
|
if (irq_count == 0 || length < 2 + 4*irq_count)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
for (uint8_t pass = 0; pass < 2; pass++)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < irq_count; i++)
|
||||||
|
{
|
||||||
|
// TODO: support irq over 64 irqs?
|
||||||
|
if (prs_span[6 + 4*i] || prs_span[7 + 4*i] || prs_span[8 + 4*i])
|
||||||
|
continue;
|
||||||
|
const uint8_t irq = prs_span[5 + 4*i];
|
||||||
|
if (irq >= 64)
|
||||||
|
continue;
|
||||||
|
if (pass == 0 && (routed_irq_mask & (static_cast<uint64_t>(1) << irq)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BAN::Array<uint8_t, 9> setting;
|
||||||
|
setting[0] = 0x89; // large, irq
|
||||||
|
setting[1] = 0x06; // data len
|
||||||
|
setting[2] = 0x00;
|
||||||
|
setting[3] = prs_span[3]; // flags
|
||||||
|
setting[4] = 0x01; // table size
|
||||||
|
setting[5] = irq; // irq
|
||||||
|
setting[6] = 0x00;
|
||||||
|
setting[7] = 0x00;
|
||||||
|
setting[8] = 0x00;
|
||||||
|
|
||||||
|
TRY(AML::method_call(srs_path, srs_node->node, TRY(AML::Node::create_buffer(setting.span()))));
|
||||||
|
|
||||||
|
dprintln("routed {} -> irq {}", device, irq);
|
||||||
|
|
||||||
|
routed_irq_mask |= static_cast<uint64_t>(1) << irq;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prs_span = prs_span.slice(3 + length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dwarnln("No routable interrupt found in _PRS");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
|
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
|
||||||
{
|
{
|
||||||
ASSERT(!m_namespace);
|
ASSERT(!m_namespace);
|
||||||
|
@ -769,6 +890,16 @@ acpi_release_global_lock:
|
||||||
|
|
||||||
dprintln("Initialized ACPI interrupts");
|
dprintln("Initialized ACPI interrupts");
|
||||||
|
|
||||||
|
if (auto interrupt_link_devices_or_error = m_namespace->find_device_with_eisa_id("PNP0C0F"_sv); !interrupt_link_devices_or_error.is_error())
|
||||||
|
{
|
||||||
|
uint64_t routed_irq_mask = 0;
|
||||||
|
auto interrupt_link_devices = interrupt_link_devices_or_error.release_value();
|
||||||
|
for (const auto& device : interrupt_link_devices)
|
||||||
|
if (auto ret = route_interrupt_link_device(device, routed_irq_mask); ret.is_error())
|
||||||
|
dwarnln("failed to route interrupt link device: {}", ret.error());
|
||||||
|
dprintln("Routed interrupt link devices");
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue