Kernel: Parse PCIConfig opregion address on read/write
I was testing on some hardware and _ADR does not have to exist in the namespace when opregion is parsed :)
This commit is contained in:
parent
3ec7aad432
commit
3960687f9d
|
@ -87,14 +87,11 @@ namespace Kernel::ACPI::AML
|
||||||
struct OpRegion
|
struct OpRegion
|
||||||
{
|
{
|
||||||
GAS::AddressSpaceID address_space;
|
GAS::AddressSpaceID address_space;
|
||||||
|
|
||||||
uint16_t seg;
|
|
||||||
uint8_t bus;
|
|
||||||
uint8_t dev;
|
|
||||||
uint8_t func;
|
|
||||||
|
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
uint64_t length;
|
uint64_t length;
|
||||||
|
alignas(Scope) uint8_t scope_storage[sizeof(Scope)];
|
||||||
|
Scope& scope() { return *reinterpret_cast<Scope*>(scope_storage); }
|
||||||
|
const Scope& scope() const { return *reinterpret_cast<const Scope*>(scope_storage); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FieldUnit
|
struct FieldUnit
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
// FIXME: Rewrite aml interpreter to not be recursive.
|
// FIXME: Rewrite aml interpreter to not be recursive.
|
||||||
// Not inlining TRYs drops our stack usage a ton...
|
// Not inlining TRYs drops our stack usage a ton...
|
||||||
#pragma GCC push_options
|
|
||||||
#pragma GCC optimize "no-inline"
|
#pragma GCC optimize "no-inline"
|
||||||
#include <BAN/Errors.h>
|
|
||||||
#pragma GCC pop_options
|
|
||||||
|
|
||||||
#include <BAN/Assert.h>
|
#include <BAN/Assert.h>
|
||||||
#include <BAN/String.h>
|
#include <BAN/String.h>
|
||||||
|
@ -3074,7 +3071,7 @@ namespace Kernel::ACPI::AML
|
||||||
return ExecutionFlowResult {
|
return ExecutionFlowResult {
|
||||||
.elem1 = ExecutionFlow::Normal,
|
.elem1 = ExecutionFlow::Normal,
|
||||||
.elem2 = BAN::Optional<Node>(),
|
.elem2 = BAN::Optional<Node>(),
|
||||||
};;
|
};
|
||||||
case AML::Byte::BreakOp:
|
case AML::Byte::BreakOp:
|
||||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_break_op");
|
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_break_op");
|
||||||
context.aml_data = context.aml_data.slice(1);
|
context.aml_data = context.aml_data.slice(1);
|
||||||
|
@ -3090,14 +3087,12 @@ namespace Kernel::ACPI::AML
|
||||||
.elem2 = BAN::Optional<Node>(),
|
.elem2 = BAN::Optional<Node>(),
|
||||||
};
|
};
|
||||||
case AML::Byte::ReturnOp:
|
case AML::Byte::ReturnOp:
|
||||||
{
|
|
||||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_return_op");
|
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_return_op");
|
||||||
context.aml_data = context.aml_data.slice(1);
|
context.aml_data = context.aml_data.slice(1);
|
||||||
return ExecutionFlowResult {
|
return ExecutionFlowResult {
|
||||||
.elem1 = ExecutionFlow::Return,
|
.elem1 = ExecutionFlow::Return,
|
||||||
.elem2 = TRY(parse_node(context)),
|
.elem2 = TRY(parse_node(context)),
|
||||||
};
|
};
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3109,13 +3104,12 @@ namespace Kernel::ACPI::AML
|
||||||
return ExecutionFlowResult {
|
return ExecutionFlowResult {
|
||||||
.elem1 = ExecutionFlow::Normal,
|
.elem1 = ExecutionFlow::Normal,
|
||||||
.elem2 = BAN::Optional<Node>(),
|
.elem2 = BAN::Optional<Node>(),
|
||||||
};;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto node = TRY(parse_node(context));
|
|
||||||
return ExecutionFlowResult {
|
return ExecutionFlowResult {
|
||||||
.elem1 = ExecutionFlow::Normal,
|
.elem1 = ExecutionFlow::Normal,
|
||||||
.elem2 = BAN::move(node)
|
.elem2 = TRY(parse_node(context)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3260,6 +3254,8 @@ namespace Kernel::ACPI::AML
|
||||||
break;
|
break;
|
||||||
case Type::OpRegion:
|
case Type::OpRegion:
|
||||||
result.as.opregion = this->as.opregion;
|
result.as.opregion = this->as.opregion;
|
||||||
|
new (&result.as.opregion.scope()) Scope();
|
||||||
|
result.as.opregion.scope() = TRY(this->as.opregion.scope().copy());
|
||||||
break;
|
break;
|
||||||
case Type::FieldUnit:
|
case Type::FieldUnit:
|
||||||
result.as.field_unit = this->as.field_unit;
|
result.as.field_unit = this->as.field_unit;
|
||||||
|
@ -3364,7 +3360,9 @@ namespace Kernel::ACPI::AML
|
||||||
break;
|
break;
|
||||||
case Type::OpRegion:
|
case Type::OpRegion:
|
||||||
this->as.opregion = other.as.opregion;
|
this->as.opregion = other.as.opregion;
|
||||||
other.as.opregion = {};
|
new (&this->as.opregion.scope()) Scope();
|
||||||
|
this->as.opregion.scope() = BAN::move(other.as.opregion.scope());
|
||||||
|
other.as.opregion.scope().~Scope();
|
||||||
break;
|
break;
|
||||||
case Type::FieldUnit:
|
case Type::FieldUnit:
|
||||||
this->as.field_unit = other.as.field_unit;
|
this->as.field_unit = other.as.field_unit;
|
||||||
|
@ -3457,6 +3455,7 @@ namespace Kernel::ACPI::AML
|
||||||
this->as.buffer_field = {};
|
this->as.buffer_field = {};
|
||||||
break;
|
break;
|
||||||
case Type::OpRegion:
|
case Type::OpRegion:
|
||||||
|
this->as.opregion.scope().~Scope();
|
||||||
this->as.opregion = {};
|
this->as.opregion = {};
|
||||||
break;
|
break;
|
||||||
case Type::FieldUnit:
|
case Type::FieldUnit:
|
||||||
|
|
|
@ -98,38 +98,8 @@ namespace Kernel::ACPI::AML
|
||||||
opregion.as.opregion.offset = region_offset.as.integer.value;
|
opregion.as.opregion.offset = region_offset.as.integer.value;
|
||||||
opregion.as.opregion.length = region_length.as.integer.value;
|
opregion.as.opregion.length = region_length.as.integer.value;
|
||||||
|
|
||||||
opregion.as.opregion.seg = 0;
|
new (&opregion.as.opregion.scope()) Scope();
|
||||||
opregion.as.opregion.bus = 0;
|
opregion.as.opregion.scope() = TRY(context.scope.copy());
|
||||||
opregion.as.opregion.dev = 0;
|
|
||||||
opregion.as.opregion.func = 0;
|
|
||||||
|
|
||||||
if (opregion.as.opregion.address_space == GAS::AddressSpaceID::PCIConfig)
|
|
||||||
{
|
|
||||||
// FIXME: Am I actually allowed to read these here or should I determine
|
|
||||||
// them on every read/write access
|
|
||||||
|
|
||||||
if (auto seg_res = TRY(Namespace::root_namespace().find_named_object(context.scope, TRY(AML::NameString::from_string("_SEG"_sv)))); seg_res.node != nullptr)
|
|
||||||
{
|
|
||||||
auto seg_node = TRY(convert_node(TRY(evaluate_node(seg_res.path, seg_res.node->node)), ConvInteger, -1));
|
|
||||||
opregion.as.opregion.seg = seg_node.as.integer.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto bbn_res = TRY(Namespace::root_namespace().find_named_object(context.scope, TRY(AML::NameString::from_string("_BBN"_sv)))); bbn_res.node != nullptr)
|
|
||||||
{
|
|
||||||
auto bbn_node = TRY(convert_node(TRY(evaluate_node(bbn_res.path, bbn_res.node->node)), ConvInteger, -1));
|
|
||||||
opregion.as.opregion.bus = bbn_node.as.integer.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto adr_res = TRY(Namespace::root_namespace().find_named_object(context.scope, TRY(AML::NameString::from_string("_ADR"_sv))));
|
|
||||||
if (adr_res.node == nullptr)
|
|
||||||
{
|
|
||||||
dwarnln("No _ADR for PCIConfig OpRegion");
|
|
||||||
return BAN::Error::from_errno(EFAULT);
|
|
||||||
}
|
|
||||||
auto adr_node = TRY(convert_node(TRY(evaluate_node(adr_res.path, adr_res.node->node)), ConvInteger, -1));
|
|
||||||
opregion.as.opregion.dev = adr_node.as.integer.value >> 16;
|
|
||||||
opregion.as.opregion.func = adr_node.as.integer.value & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRY(Namespace::root_namespace().add_named_object(context, region_name, BAN::move(opregion)));
|
TRY(Namespace::root_namespace().add_named_object(context, region_name, BAN::move(opregion)));
|
||||||
|
|
||||||
|
@ -414,6 +384,32 @@ namespace Kernel::ACPI::AML
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> get_pci_config_address(const OpRegion& opregion, uint16_t& seg, uint8_t& bus, uint8_t& dev, uint8_t& func)
|
||||||
|
{
|
||||||
|
ASSERT(opregion.address_space == GAS::AddressSpaceID::PCIConfig);
|
||||||
|
|
||||||
|
seg = 0;
|
||||||
|
if (auto seg_res = TRY(Namespace::root_namespace().find_named_object(opregion.scope(), TRY(AML::NameString::from_string("_SEG"_sv)))); seg_res.node != nullptr)
|
||||||
|
seg = TRY(convert_node(TRY(evaluate_node(seg_res.path, seg_res.node->node)), ConvInteger, -1)).as.integer.value;
|
||||||
|
|
||||||
|
bus = 0;
|
||||||
|
if (auto bbn_res = TRY(Namespace::root_namespace().find_named_object(opregion.scope(), TRY(AML::NameString::from_string("_BBN"_sv)))); bbn_res.node != nullptr)
|
||||||
|
bus = TRY(convert_node(TRY(evaluate_node(bbn_res.path, bbn_res.node->node)), ConvInteger, -1)).as.integer.value;
|
||||||
|
|
||||||
|
auto adr_res = TRY(Namespace::root_namespace().find_named_object(opregion.scope(), TRY(AML::NameString::from_string("_ADR"_sv))));
|
||||||
|
if (adr_res.node == nullptr)
|
||||||
|
{
|
||||||
|
dwarnln("No _ADR for PCIConfig OpRegion");
|
||||||
|
return BAN::Error::from_errno(EFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto adr_node = TRY(convert_node(TRY(evaluate_node(adr_res.path, adr_res.node->node)), ConvInteger, -1));
|
||||||
|
dev = adr_node.as.integer.value >> 16;
|
||||||
|
func = adr_node.as.integer.value & 0xFF;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static BAN::ErrorOr<uint64_t> perform_opregion_read(const OpRegion& opregion, uint8_t access_size, uint64_t offset)
|
static BAN::ErrorOr<uint64_t> perform_opregion_read(const OpRegion& opregion, uint8_t access_size, uint64_t offset)
|
||||||
{
|
{
|
||||||
ASSERT(offset % access_size == 0);
|
ASSERT(offset % access_size == 0);
|
||||||
|
@ -455,7 +451,11 @@ namespace Kernel::ACPI::AML
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
case GAS::AddressSpaceID::PCIConfig:
|
case GAS::AddressSpaceID::PCIConfig:
|
||||||
{
|
{
|
||||||
if (opregion.seg != 0)
|
uint16_t seg;
|
||||||
|
uint8_t bus, dev, func;
|
||||||
|
TRY(get_pci_config_address(opregion, seg, bus, dev, func));
|
||||||
|
|
||||||
|
if (seg != 0)
|
||||||
{
|
{
|
||||||
dwarnln("PCIConfig OpRegion with segment");
|
dwarnln("PCIConfig OpRegion with segment");
|
||||||
return BAN::Error::from_errno(ENOTSUP);
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
|
@ -463,11 +463,11 @@ namespace Kernel::ACPI::AML
|
||||||
|
|
||||||
switch (access_size)
|
switch (access_size)
|
||||||
{
|
{
|
||||||
case 1: return PCI::PCIManager::get().read_config_byte (opregion.bus, opregion.dev, opregion.func, byte_offset);
|
case 1: return PCI::PCIManager::get().read_config_byte (bus, dev, func, byte_offset);
|
||||||
case 2: return PCI::PCIManager::get().read_config_word (opregion.bus, opregion.dev, opregion.func, byte_offset);
|
case 2: return PCI::PCIManager::get().read_config_word (bus, dev, func, byte_offset);
|
||||||
case 4: return PCI::PCIManager::get().read_config_dword(opregion.bus, opregion.dev, opregion.func, byte_offset);
|
case 4: return PCI::PCIManager::get().read_config_dword(bus, dev, func, byte_offset);
|
||||||
default:
|
default:
|
||||||
dwarnln("{} byte read from PCI {2H}:{2H}:{2H} offset {2H}", access_size, opregion.bus, opregion.dev, opregion.func, byte_offset);
|
dwarnln("{} byte read from PCI {2H}:{2H}:{2H} offset {2H}", access_size, bus, dev, func, byte_offset);
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
|
@ -525,7 +525,11 @@ namespace Kernel::ACPI::AML
|
||||||
return {};
|
return {};
|
||||||
case GAS::AddressSpaceID::PCIConfig:
|
case GAS::AddressSpaceID::PCIConfig:
|
||||||
{
|
{
|
||||||
if (opregion.seg != 0)
|
uint16_t seg;
|
||||||
|
uint8_t bus, dev, func;
|
||||||
|
TRY(get_pci_config_address(opregion, seg, bus, dev, func));
|
||||||
|
|
||||||
|
if (seg != 0)
|
||||||
{
|
{
|
||||||
dwarnln("PCIConfig OpRegion with segment");
|
dwarnln("PCIConfig OpRegion with segment");
|
||||||
return BAN::Error::from_errno(ENOTSUP);
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
|
@ -533,11 +537,11 @@ namespace Kernel::ACPI::AML
|
||||||
|
|
||||||
switch (access_size)
|
switch (access_size)
|
||||||
{
|
{
|
||||||
case 1: PCI::PCIManager::get().write_config_byte (opregion.bus, opregion.dev, opregion.func, byte_offset, value); break;
|
case 1: PCI::PCIManager::get().write_config_byte (bus, dev, func, byte_offset, value); break;
|
||||||
case 2: PCI::PCIManager::get().write_config_word (opregion.bus, opregion.dev, opregion.func, byte_offset, value); break;
|
case 2: PCI::PCIManager::get().write_config_word (bus, dev, func, byte_offset, value); break;
|
||||||
case 4: PCI::PCIManager::get().write_config_dword(opregion.bus, opregion.dev, opregion.func, byte_offset, value); break;
|
case 4: PCI::PCIManager::get().write_config_dword(bus, dev, func, byte_offset, value); break;
|
||||||
default:
|
default:
|
||||||
dwarnln("{} byte write to PCI {2H}:{2H}:{2H} offset {2H}", access_size, opregion.bus, opregion.dev, opregion.func, byte_offset);
|
dwarnln("{} byte write to PCI {2H}:{2H}:{2H} offset {2H}", access_size, bus, dev, func, byte_offset);
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|
Loading…
Reference in New Issue